Imported Upstream version 2.02.79 upstream/2.02.79
authorAnas Nashif <anas.nashif@intel.com>
Tue, 5 Mar 2013 09:43:20 +0000 (01:43 -0800)
committerAnas Nashif <anas.nashif@intel.com>
Tue, 5 Mar 2013 09:43:20 +0000 (01:43 -0800)
558 files changed:
COPYING [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
VERSION [new file with mode: 0644]
VERSION_DM [new file with mode: 0644]
WHATS_NEW [new file with mode: 0644]
WHATS_NEW_DM [new file with mode: 0644]
autoconf/config.guess [new file with mode: 0755]
autoconf/config.sub [new file with mode: 0755]
autoconf/install-sh [new file with mode: 0755]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
daemons/Makefile.in [new file with mode: 0644]
daemons/clvmd/Makefile.in [new file with mode: 0644]
daemons/clvmd/clvm.h [new file with mode: 0644]
daemons/clvmd/clvmd-cman.c [new file with mode: 0644]
daemons/clvmd/clvmd-command.c [new file with mode: 0644]
daemons/clvmd/clvmd-common.h [new file with mode: 0644]
daemons/clvmd/clvmd-comms.h [new file with mode: 0644]
daemons/clvmd/clvmd-corosync.c [new file with mode: 0644]
daemons/clvmd/clvmd-gulm.c [new file with mode: 0644]
daemons/clvmd/clvmd-gulm.h [new file with mode: 0644]
daemons/clvmd/clvmd-openais.c [new file with mode: 0644]
daemons/clvmd/clvmd-singlenode.c [new file with mode: 0644]
daemons/clvmd/clvmd.c [new file with mode: 0644]
daemons/clvmd/clvmd.h [new file with mode: 0644]
daemons/clvmd/lvm-functions.c [new file with mode: 0644]
daemons/clvmd/lvm-functions.h [new file with mode: 0644]
daemons/clvmd/refresh_clvmd.c [new file with mode: 0644]
daemons/clvmd/refresh_clvmd.h [new file with mode: 0644]
daemons/clvmd/tcp-comms.c [new file with mode: 0644]
daemons/clvmd/tcp-comms.h [new file with mode: 0644]
daemons/cmirrord/Makefile.in [new file with mode: 0644]
daemons/cmirrord/clogd.c [new file with mode: 0644]
daemons/cmirrord/cluster.c [new file with mode: 0644]
daemons/cmirrord/cluster.h [new file with mode: 0644]
daemons/cmirrord/common.h [new file with mode: 0644]
daemons/cmirrord/compat.c [new file with mode: 0644]
daemons/cmirrord/compat.h [new file with mode: 0644]
daemons/cmirrord/functions.c [new file with mode: 0644]
daemons/cmirrord/functions.h [new file with mode: 0644]
daemons/cmirrord/link_mon.c [new file with mode: 0644]
daemons/cmirrord/link_mon.h [new file with mode: 0644]
daemons/cmirrord/local.c [new file with mode: 0644]
daemons/cmirrord/local.h [new file with mode: 0644]
daemons/cmirrord/logging.c [new file with mode: 0644]
daemons/cmirrord/logging.h [new file with mode: 0644]
daemons/dmeventd/.exported_symbols [new file with mode: 0644]
daemons/dmeventd/Makefile.in [new file with mode: 0644]
daemons/dmeventd/dmeventd.c [new file with mode: 0644]
daemons/dmeventd/dmeventd.h [new file with mode: 0644]
daemons/dmeventd/libdevmapper-event.c [new file with mode: 0644]
daemons/dmeventd/libdevmapper-event.h [new file with mode: 0644]
daemons/dmeventd/libdevmapper-event.pc.in [new file with mode: 0644]
daemons/dmeventd/plugins/Makefile.in [new file with mode: 0644]
daemons/dmeventd/plugins/lvm2/.exported_symbols [new file with mode: 0644]
daemons/dmeventd/plugins/lvm2/Makefile.in [new file with mode: 0644]
daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c [new file with mode: 0644]
daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h [new file with mode: 0644]
daemons/dmeventd/plugins/mirror/.exported_symbols [new file with mode: 0644]
daemons/dmeventd/plugins/mirror/Makefile.in [new file with mode: 0644]
daemons/dmeventd/plugins/mirror/dmeventd_mirror.c [new file with mode: 0644]
daemons/dmeventd/plugins/snapshot/.exported_symbols [new file with mode: 0644]
daemons/dmeventd/plugins/snapshot/Makefile.in [new file with mode: 0644]
daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c [new file with mode: 0644]
doc/Makefile.in [new file with mode: 0644]
doc/example.conf.in [new file with mode: 0644]
doc/example_cmdlib.c [new file with mode: 0644]
doc/lvm_fault_handling.txt [new file with mode: 0644]
doc/pvmove_outline.txt [new file with mode: 0644]
doc/tagging.txt [new file with mode: 0644]
doc/testing.txt [new file with mode: 0644]
include/.symlinks.in [new file with mode: 0644]
include/Makefile.in [new file with mode: 0644]
lib/Makefile.in [new file with mode: 0644]
lib/activate/activate.c [new file with mode: 0644]
lib/activate/activate.h [new file with mode: 0644]
lib/activate/dev_manager.c [new file with mode: 0644]
lib/activate/dev_manager.h [new file with mode: 0644]
lib/activate/fs.c [new file with mode: 0644]
lib/activate/fs.h [new file with mode: 0644]
lib/activate/targets.h [new file with mode: 0644]
lib/cache/lvmcache.c [new file with mode: 0644]
lib/cache/lvmcache.h [new file with mode: 0644]
lib/commands/errors.h [new file with mode: 0644]
lib/commands/toolcontext.c [new file with mode: 0644]
lib/commands/toolcontext.h [new file with mode: 0644]
lib/config/config.c [new file with mode: 0644]
lib/config/config.h [new file with mode: 0644]
lib/config/defaults.h [new file with mode: 0644]
lib/datastruct/btree.c [new file with mode: 0644]
lib/datastruct/btree.h [new file with mode: 0644]
lib/datastruct/lvm-types.h [new file with mode: 0644]
lib/datastruct/str_list.c [new file with mode: 0644]
lib/datastruct/str_list.h [new file with mode: 0644]
lib/device/dev-cache.c [new file with mode: 0644]
lib/device/dev-cache.h [new file with mode: 0644]
lib/device/dev-io.c [new file with mode: 0644]
lib/device/dev-luks.c [new file with mode: 0644]
lib/device/dev-md.c [new file with mode: 0644]
lib/device/dev-swap.c [new file with mode: 0644]
lib/device/device.c [new file with mode: 0644]
lib/device/device.h [new file with mode: 0644]
lib/display/display.c [new file with mode: 0644]
lib/display/display.h [new file with mode: 0644]
lib/error/errseg.c [new file with mode: 0644]
lib/filters/filter-composite.c [new file with mode: 0644]
lib/filters/filter-composite.h [new file with mode: 0644]
lib/filters/filter-md.c [new file with mode: 0644]
lib/filters/filter-md.h [new file with mode: 0644]
lib/filters/filter-persistent.c [new file with mode: 0644]
lib/filters/filter-persistent.h [new file with mode: 0644]
lib/filters/filter-regex.c [new file with mode: 0644]
lib/filters/filter-regex.h [new file with mode: 0644]
lib/filters/filter-sysfs.c [new file with mode: 0644]
lib/filters/filter-sysfs.h [new file with mode: 0644]
lib/filters/filter.c [new file with mode: 0644]
lib/filters/filter.h [new file with mode: 0644]
lib/format1/.exported_symbols [new file with mode: 0644]
lib/format1/Makefile.in [new file with mode: 0644]
lib/format1/disk-rep.c [new file with mode: 0644]
lib/format1/disk-rep.h [new file with mode: 0644]
lib/format1/format1.c [new file with mode: 0644]
lib/format1/format1.h [new file with mode: 0644]
lib/format1/import-export.c [new file with mode: 0644]
lib/format1/import-extents.c [new file with mode: 0644]
lib/format1/layout.c [new file with mode: 0644]
lib/format1/lvm1-label.c [new file with mode: 0644]
lib/format1/lvm1-label.h [new file with mode: 0644]
lib/format1/vg_number.c [new file with mode: 0644]
lib/format_pool/.exported_symbols [new file with mode: 0644]
lib/format_pool/Makefile.in [new file with mode: 0644]
lib/format_pool/disk_rep.c [new file with mode: 0644]
lib/format_pool/disk_rep.h [new file with mode: 0644]
lib/format_pool/format_pool.c [new file with mode: 0644]
lib/format_pool/format_pool.h [new file with mode: 0644]
lib/format_pool/import_export.c [new file with mode: 0644]
lib/format_pool/pool_label.c [new file with mode: 0644]
lib/format_pool/pool_label.h [new file with mode: 0644]
lib/format_pool/sptype_names.h [new file with mode: 0644]
lib/format_text/archive.c [new file with mode: 0644]
lib/format_text/archiver.c [new file with mode: 0644]
lib/format_text/archiver.h [new file with mode: 0644]
lib/format_text/export.c [new file with mode: 0644]
lib/format_text/flags.c [new file with mode: 0644]
lib/format_text/format-text.c [new file with mode: 0644]
lib/format_text/format-text.h [new file with mode: 0644]
lib/format_text/import-export.h [new file with mode: 0644]
lib/format_text/import.c [new file with mode: 0644]
lib/format_text/import_vsn1.c [new file with mode: 0644]
lib/format_text/layout.h [new file with mode: 0644]
lib/format_text/tags.c [new file with mode: 0644]
lib/format_text/text_export.h [new file with mode: 0644]
lib/format_text/text_import.h [new file with mode: 0644]
lib/format_text/text_label.c [new file with mode: 0644]
lib/freeseg/freeseg.c [new file with mode: 0644]
lib/label/label.c [new file with mode: 0644]
lib/label/label.h [new file with mode: 0644]
lib/locking/.exported_symbols [new file with mode: 0644]
lib/locking/Makefile.in [new file with mode: 0644]
lib/locking/cluster_locking.c [new file with mode: 0644]
lib/locking/external_locking.c [new file with mode: 0644]
lib/locking/file_locking.c [new file with mode: 0644]
lib/locking/locking.c [new file with mode: 0644]
lib/locking/locking.h [new file with mode: 0644]
lib/locking/locking_types.h [new file with mode: 0644]
lib/locking/no_locking.c [new file with mode: 0644]
lib/log/log.c [new file with mode: 0644]
lib/log/log.h [new file with mode: 0644]
lib/log/lvm-logging.h [new file with mode: 0644]
lib/metadata/lv.c [new file with mode: 0644]
lib/metadata/lv.h [new file with mode: 0644]
lib/metadata/lv_alloc.h [new file with mode: 0644]
lib/metadata/lv_manip.c [new file with mode: 0644]
lib/metadata/merge.c [new file with mode: 0644]
lib/metadata/metadata-exported.h [new file with mode: 0644]
lib/metadata/metadata.c [new file with mode: 0644]
lib/metadata/metadata.h [new file with mode: 0644]
lib/metadata/mirror.c [new file with mode: 0644]
lib/metadata/pv.c [new file with mode: 0644]
lib/metadata/pv.h [new file with mode: 0644]
lib/metadata/pv_alloc.h [new file with mode: 0644]
lib/metadata/pv_manip.c [new file with mode: 0644]
lib/metadata/pv_map.c [new file with mode: 0644]
lib/metadata/pv_map.h [new file with mode: 0644]
lib/metadata/replicator_manip.c [new file with mode: 0644]
lib/metadata/segtype.c [new file with mode: 0644]
lib/metadata/segtype.h [new file with mode: 0644]
lib/metadata/snapshot_manip.c [new file with mode: 0644]
lib/metadata/vg.c [new file with mode: 0644]
lib/metadata/vg.h [new file with mode: 0644]
lib/mirror/.exported_symbols [new file with mode: 0644]
lib/mirror/Makefile.in [new file with mode: 0644]
lib/mirror/mirrored.c [new file with mode: 0644]
lib/misc/configure.h.in [new file with mode: 0644]
lib/misc/crc.c [new file with mode: 0644]
lib/misc/crc.h [new file with mode: 0644]
lib/misc/crc_gen.c [new file with mode: 0644]
lib/misc/intl.h [new file with mode: 0644]
lib/misc/last-path-component.h [new file with mode: 0644]
lib/misc/lib.h [new file with mode: 0644]
lib/misc/lvm-exec.c [new file with mode: 0644]
lib/misc/lvm-exec.h [new file with mode: 0644]
lib/misc/lvm-file.c [new file with mode: 0644]
lib/misc/lvm-file.h [new file with mode: 0644]
lib/misc/lvm-globals.c [new file with mode: 0644]
lib/misc/lvm-globals.h [new file with mode: 0644]
lib/misc/lvm-percent.c [new file with mode: 0644]
lib/misc/lvm-percent.h [new file with mode: 0644]
lib/misc/lvm-string.c [new file with mode: 0644]
lib/misc/lvm-string.h [new file with mode: 0644]
lib/misc/lvm-version.h.in [new file with mode: 0644]
lib/misc/lvm-wrappers.c [new file with mode: 0644]
lib/misc/lvm-wrappers.h [new file with mode: 0644]
lib/misc/sharedlib.c [new file with mode: 0644]
lib/misc/sharedlib.h [new file with mode: 0644]
lib/misc/timestamp.c [new file with mode: 0644]
lib/misc/timestamp.h [new file with mode: 0644]
lib/misc/util.c [new file with mode: 0644]
lib/misc/util.h [new file with mode: 0644]
lib/mm/memlock.c [new file with mode: 0644]
lib/mm/memlock.h [new file with mode: 0644]
lib/mm/xlate.h [new file with mode: 0644]
lib/replicator/.exported_symbols [new file with mode: 0644]
lib/replicator/Makefile.in [new file with mode: 0644]
lib/replicator/replicator.c [new file with mode: 0644]
lib/report/columns.h [new file with mode: 0644]
lib/report/properties.c [new file with mode: 0644]
lib/report/properties.h [new file with mode: 0644]
lib/report/report.c [new file with mode: 0644]
lib/report/report.h [new file with mode: 0644]
lib/snapshot/.exported_symbols [new file with mode: 0644]
lib/snapshot/Makefile.in [new file with mode: 0644]
lib/snapshot/snapshot.c [new file with mode: 0644]
lib/striped/striped.c [new file with mode: 0644]
lib/unknown/unknown.c [new file with mode: 0644]
lib/uuid/uuid.c [new file with mode: 0644]
lib/uuid/uuid.h [new file with mode: 0644]
lib/zero/zero.c [new file with mode: 0644]
libdm/.exported_symbols [new file with mode: 0644]
libdm/Makefile.in [new file with mode: 0644]
libdm/datastruct/bitset.c [new file with mode: 0644]
libdm/datastruct/hash.c [new file with mode: 0644]
libdm/datastruct/list.c [new file with mode: 0644]
libdm/ioctl/libdm-compat.h [new file with mode: 0644]
libdm/ioctl/libdm-iface.c [new file with mode: 0644]
libdm/ioctl/libdm-targets.h [new file with mode: 0644]
libdm/libdevmapper.h [new file with mode: 0644]
libdm/libdevmapper.pc.in [new file with mode: 0644]
libdm/libdm-common.c [new file with mode: 0644]
libdm/libdm-common.h [new file with mode: 0644]
libdm/libdm-deptree.c [new file with mode: 0644]
libdm/libdm-file.c [new file with mode: 0644]
libdm/libdm-report.c [new file with mode: 0644]
libdm/libdm-string.c [new file with mode: 0644]
libdm/misc/dm-ioctl.h [new file with mode: 0644]
libdm/misc/dm-log-userspace.h [new file with mode: 0644]
libdm/misc/dm-logging.h [new file with mode: 0644]
libdm/misc/dmlib.h [new file with mode: 0644]
libdm/misc/kdev_t.h [new file with mode: 0644]
libdm/mm/dbg_malloc.c [new file with mode: 0644]
libdm/mm/dbg_malloc.h [new file with mode: 0644]
libdm/mm/pool-debug.c [new file with mode: 0644]
libdm/mm/pool-fast.c [new file with mode: 0644]
libdm/mm/pool.c [new file with mode: 0644]
libdm/regex/matcher.c [new file with mode: 0644]
libdm/regex/parse_rx.c [new file with mode: 0644]
libdm/regex/parse_rx.h [new file with mode: 0644]
libdm/regex/ttree.c [new file with mode: 0644]
libdm/regex/ttree.h [new file with mode: 0644]
liblvm/.exported_symbols [new file with mode: 0644]
liblvm/Doxyfile [new file with mode: 0644]
liblvm/Makefile.in [new file with mode: 0644]
liblvm/liblvm2app.pc.in [new file with mode: 0644]
liblvm/lvm2app.h [new file with mode: 0644]
liblvm/lvm_base.c [new file with mode: 0644]
liblvm/lvm_lv.c [new file with mode: 0644]
liblvm/lvm_misc.c [new file with mode: 0644]
liblvm/lvm_misc.h [new file with mode: 0644]
liblvm/lvm_pv.c [new file with mode: 0644]
liblvm/lvm_vg.c [new file with mode: 0644]
make.tmpl.in [new file with mode: 0644]
man/Makefile.in [new file with mode: 0644]
man/clvmd.8.in [new file with mode: 0644]
man/cmirrord.8.in [new file with mode: 0644]
man/dmeventd.8.in [new file with mode: 0644]
man/dmsetup.8.in [new file with mode: 0644]
man/fsadm.8.in [new file with mode: 0644]
man/lvchange.8.in [new file with mode: 0644]
man/lvconvert.8.in [new file with mode: 0644]
man/lvcreate.8.in [new file with mode: 0644]
man/lvdisplay.8.in [new file with mode: 0644]
man/lvextend.8.in [new file with mode: 0644]
man/lvm.8.in [new file with mode: 0644]
man/lvm.conf.5.in [new file with mode: 0644]
man/lvmchange.8.in [new file with mode: 0644]
man/lvmconf.8.in [new file with mode: 0644]
man/lvmdiskscan.8.in [new file with mode: 0644]
man/lvmdump.8.in [new file with mode: 0644]
man/lvmsadc.8.in [new file with mode: 0644]
man/lvmsar.8.in [new file with mode: 0644]
man/lvreduce.8.in [new file with mode: 0644]
man/lvremove.8.in [new file with mode: 0644]
man/lvrename.8.in [new file with mode: 0644]
man/lvresize.8.in [new file with mode: 0644]
man/lvs.8.in [new file with mode: 0644]
man/lvscan.8.in [new file with mode: 0644]
man/pvchange.8.in [new file with mode: 0644]
man/pvck.8.in [new file with mode: 0644]
man/pvcreate.8.in [new file with mode: 0644]
man/pvdisplay.8.in [new file with mode: 0644]
man/pvmove.8.in [new file with mode: 0644]
man/pvremove.8.in [new file with mode: 0644]
man/pvresize.8.in [new file with mode: 0644]
man/pvs.8.in [new file with mode: 0644]
man/pvscan.8.in [new file with mode: 0644]
man/vgcfgbackup.8.in [new file with mode: 0644]
man/vgcfgrestore.8.in [new file with mode: 0644]
man/vgchange.8.in [new file with mode: 0644]
man/vgck.8.in [new file with mode: 0644]
man/vgconvert.8.in [new file with mode: 0644]
man/vgcreate.8.in [new file with mode: 0644]
man/vgdisplay.8.in [new file with mode: 0644]
man/vgexport.8.in [new file with mode: 0644]
man/vgextend.8.in [new file with mode: 0644]
man/vgimport.8.in [new file with mode: 0644]
man/vgimportclone.8.in [new file with mode: 0644]
man/vgmerge.8.in [new file with mode: 0644]
man/vgmknodes.8.in [new file with mode: 0644]
man/vgreduce.8.in [new file with mode: 0644]
man/vgremove.8.in [new file with mode: 0644]
man/vgrename.8.in [new file with mode: 0644]
man/vgs.8.in [new file with mode: 0644]
man/vgscan.8.in [new file with mode: 0644]
man/vgsplit.8.in [new file with mode: 0644]
packaging/device-mapper.changes [new file with mode: 0644]
packaging/device-mapper.spec [new file with mode: 0644]
po/Makefile.in [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/lvm2.po [new file with mode: 0644]
po/pogen.h [new file with mode: 0644]
report-generators/lib/log.rb [new file with mode: 0644]
report-generators/lib/report_templates.rb [new file with mode: 0644]
report-generators/lib/reports.rb [new file with mode: 0644]
report-generators/lib/schedule_file.rb [new file with mode: 0644]
report-generators/lib/string-store.rb [new file with mode: 0644]
report-generators/memcheck.rb [new file with mode: 0644]
report-generators/templates/boiler_plate.rhtml [new file with mode: 0644]
report-generators/templates/index.rhtml [new file with mode: 0644]
report-generators/templates/memcheck.rhtml [new file with mode: 0644]
report-generators/templates/unit_detail.rhtml [new file with mode: 0644]
report-generators/templates/unit_test.rhtml [new file with mode: 0644]
report-generators/test/example.schedule [new file with mode: 0644]
report-generators/test/strings/more_strings/test3.txt [new file with mode: 0644]
report-generators/test/strings/test1.txt [new file with mode: 0644]
report-generators/test/strings/test2 [new file with mode: 0644]
report-generators/test/tc_log.rb [new file with mode: 0644]
report-generators/test/tc_schedule_file.rb [new file with mode: 0644]
report-generators/test/tc_string_store.rb [new file with mode: 0644]
report-generators/test/ts.rb [new file with mode: 0644]
report-generators/title_page.rb [new file with mode: 0644]
report-generators/unit_test.rb [new file with mode: 0644]
reports/stylesheet.css [new file with mode: 0644]
scripts/Makefile.in [new file with mode: 0644]
scripts/VolumeGroup.ocf [new file with mode: 0644]
scripts/clvmd_fix_conf.sh [new file with mode: 0644]
scripts/clvmd_init_red_hat.in [new file with mode: 0644]
scripts/cmirrord_init_red_hat.in [new file with mode: 0755]
scripts/fsadm.sh [new file with mode: 0644]
scripts/last_cvs_update.sh [new file with mode: 0755]
scripts/lvm2_monitoring_init_red_hat.in [new file with mode: 0644]
scripts/lvm2_monitoring_init_rhel4 [new file with mode: 0644]
scripts/lvm2create_initrd/Makefile [new file with mode: 0644]
scripts/lvm2create_initrd/README [new file with mode: 0644]
scripts/lvm2create_initrd/lvm2create_initrd [new file with mode: 0644]
scripts/lvm2create_initrd/lvm2create_initrd.8 [new file with mode: 0644]
scripts/lvm2create_initrd/lvm2create_initrd.pod [new file with mode: 0644]
scripts/lvm2create_initrd/lvm2udev [new file with mode: 0644]
scripts/lvmconf.sh [new file with mode: 0644]
scripts/lvmconf_lockingtype2.sh [new file with mode: 0644]
scripts/lvmdump.sh [new file with mode: 0755]
scripts/relpath.awk [new file with mode: 0755]
scripts/vg_convert [new file with mode: 0755]
scripts/vgimportclone.sh [new file with mode: 0755]
test/.gitignore [new file with mode: 0644]
test/Makefile.in [new file with mode: 0644]
test/api/Makefile.in [new file with mode: 0644]
test/api/percent.c [new file with mode: 0644]
test/api/percent.sh [new file with mode: 0644]
test/api/test.c [new file with mode: 0644]
test/api/vgtest.c [new file with mode: 0644]
test/api/vgtest.sh [new file with mode: 0644]
test/check.sh [new file with mode: 0644]
test/harness.c [new file with mode: 0644]
test/harness.sh [new file with mode: 0644]
test/lvm-utils.sh [new file with mode: 0644]
test/mkdtemp [new file with mode: 0755]
test/not.c [new file with mode: 0644]
test/t-000-basic.sh [new file with mode: 0755]
test/t-activate-missing.sh [new file with mode: 0644]
test/t-activate-partial.sh [new file with mode: 0644]
test/t-covercmd.sh [new file with mode: 0755]
test/t-dmeventd-restart.sh [new file with mode: 0644]
test/t-fsadm.sh [new file with mode: 0644]
test/t-inconsistent-metadata.sh [new file with mode: 0644]
test/t-listings.sh [new file with mode: 0644]
test/t-lock-blocking.sh [new file with mode: 0644]
test/t-lvchange-mirror.sh [new file with mode: 0644]
test/t-lvconvert-mirror-basic-0.sh [new file with mode: 0644]
test/t-lvconvert-mirror-basic-1.sh [new file with mode: 0644]
test/t-lvconvert-mirror-basic-2.sh [new file with mode: 0644]
test/t-lvconvert-mirror-basic-3.sh [new file with mode: 0644]
test/t-lvconvert-mirror-basic.sh [new file with mode: 0644]
test/t-lvconvert-mirror.sh [new file with mode: 0644]
test/t-lvconvert-repair-dmeventd.sh [new file with mode: 0644]
test/t-lvconvert-repair-policy.sh [new file with mode: 0644]
test/t-lvconvert-repair-replace.sh [new file with mode: 0644]
test/t-lvconvert-repair-transient.sh [new file with mode: 0644]
test/t-lvconvert-repair.sh [new file with mode: 0644]
test/t-lvconvert-twostep.sh [new file with mode: 0644]
test/t-lvcreate-mirror.sh [new file with mode: 0644]
test/t-lvcreate-operation.sh [new file with mode: 0644]
test/t-lvcreate-pvtags.sh [new file with mode: 0755]
test/t-lvcreate-small-snap.sh [new file with mode: 0644]
test/t-lvcreate-usage.sh [new file with mode: 0755]
test/t-lvextend-percent-extents.sh [new file with mode: 0755]
test/t-lvextend-snapshot-dmeventd.sh [new file with mode: 0644]
test/t-lvextend-snapshot-policy.sh [new file with mode: 0644]
test/t-lvm-init.sh [new file with mode: 0644]
test/t-lvmcache-exercise.sh [new file with mode: 0755]
test/t-lvresize-mirror.sh [new file with mode: 0644]
test/t-lvresize-usage.sh [new file with mode: 0755]
test/t-mdata-strings.sh [new file with mode: 0755]
test/t-metadata-balance.sh [new file with mode: 0755]
test/t-metadata.sh [new file with mode: 0755]
test/t-mirror-names.sh [new file with mode: 0644]
test/t-mirror-vgreduce-removemissing.sh [new file with mode: 0755]
test/t-nomda-missing.sh [new file with mode: 0644]
test/t-pool-labels.sh [new file with mode: 0755]
test/t-pv-range-overflow.sh [new file with mode: 0755]
test/t-pvchange-usage.sh [new file with mode: 0755]
test/t-pvcreate-metadata0.sh [new file with mode: 0755]
test/t-pvcreate-operation-md.sh [new file with mode: 0644]
test/t-pvcreate-operation.sh [new file with mode: 0755]
test/t-pvcreate-usage.sh [new file with mode: 0755]
test/t-pvmove-basic.sh [new file with mode: 0755]
test/t-pvremove-usage.sh [new file with mode: 0755]
test/t-read-ahead.sh [new file with mode: 0755]
test/t-snapshot-autoumount-dmeventd.sh [new file with mode: 0644]
test/t-snapshot-merge.sh [new file with mode: 0755]
test/t-snapshots-of-mirrors.sh [new file with mode: 0644]
test/t-tags.sh [new file with mode: 0755]
test/t-test-partition.sh [new file with mode: 0644]
test/t-topology-support.sh [new file with mode: 0644]
test/t-unknown-segment.sh [new file with mode: 0644]
test/t-unlost-pv.sh [new file with mode: 0644]
test/t-vgcfgbackup-usage.sh [new file with mode: 0644]
test/t-vgchange-maxlv.sh [new file with mode: 0644]
test/t-vgchange-usage.sh [new file with mode: 0644]
test/t-vgcreate-usage.sh [new file with mode: 0755]
test/t-vgextend-restoremissing.sh [new file with mode: 0644]
test/t-vgextend-usage.sh [new file with mode: 0644]
test/t-vgmerge-operation.sh [new file with mode: 0755]
test/t-vgmerge-usage.sh [new file with mode: 0755]
test/t-vgreduce-usage.sh [new file with mode: 0755]
test/t-vgrename-usage.sh [new file with mode: 0755]
test/t-vgsplit-operation.sh [new file with mode: 0755]
test/t-vgsplit-stacked.sh [new file with mode: 0644]
test/t-vgsplit-usage.sh [new file with mode: 0755]
test/test-utils.sh [new file with mode: 0644]
tools/.exported_symbols [new file with mode: 0644]
tools/Makefile.in [new file with mode: 0644]
tools/args.h [new file with mode: 0644]
tools/cmdnames.h [new file with mode: 0644]
tools/commands.h [new file with mode: 0644]
tools/dmsetup.c [new file with mode: 0644]
tools/dumpconfig.c [new file with mode: 0644]
tools/formats.c [new file with mode: 0644]
tools/lvchange.c [new file with mode: 0644]
tools/lvconvert.c [new file with mode: 0644]
tools/lvcreate.c [new file with mode: 0644]
tools/lvdisplay.c [new file with mode: 0644]
tools/lvextend.c [new file with mode: 0644]
tools/lvm-static.c [new file with mode: 0644]
tools/lvm.c [new file with mode: 0644]
tools/lvm2cmd-static.c [new file with mode: 0644]
tools/lvm2cmd.c [new file with mode: 0644]
tools/lvm2cmd.h [new file with mode: 0644]
tools/lvm2cmdline.h [new file with mode: 0644]
tools/lvmchange.c [new file with mode: 0644]
tools/lvmcmdlib.c [new file with mode: 0644]
tools/lvmcmdline.c [new file with mode: 0644]
tools/lvmdiskscan.c [new file with mode: 0644]
tools/lvreduce.c [new file with mode: 0644]
tools/lvremove.c [new file with mode: 0644]
tools/lvrename.c [new file with mode: 0644]
tools/lvresize.c [new file with mode: 0644]
tools/lvscan.c [new file with mode: 0644]
tools/polldaemon.c [new file with mode: 0644]
tools/polldaemon.h [new file with mode: 0644]
tools/pvchange.c [new file with mode: 0644]
tools/pvck.c [new file with mode: 0644]
tools/pvcreate.c [new file with mode: 0644]
tools/pvdisplay.c [new file with mode: 0644]
tools/pvmove.c [new file with mode: 0644]
tools/pvremove.c [new file with mode: 0644]
tools/pvresize.c [new file with mode: 0644]
tools/pvscan.c [new file with mode: 0644]
tools/reporter.c [new file with mode: 0644]
tools/segtypes.c [new file with mode: 0644]
tools/stub.h [new file with mode: 0644]
tools/toollib.c [new file with mode: 0644]
tools/toollib.h [new file with mode: 0644]
tools/tools.h [new file with mode: 0644]
tools/vgcfgbackup.c [new file with mode: 0644]
tools/vgcfgrestore.c [new file with mode: 0644]
tools/vgchange.c [new file with mode: 0644]
tools/vgck.c [new file with mode: 0644]
tools/vgconvert.c [new file with mode: 0644]
tools/vgcreate.c [new file with mode: 0644]
tools/vgdisplay.c [new file with mode: 0644]
tools/vgexport.c [new file with mode: 0644]
tools/vgextend.c [new file with mode: 0644]
tools/vgimport.c [new file with mode: 0644]
tools/vgmerge.c [new file with mode: 0644]
tools/vgmknodes.c [new file with mode: 0644]
tools/vgreduce.c [new file with mode: 0644]
tools/vgremove.c [new file with mode: 0644]
tools/vgrename.c [new file with mode: 0644]
tools/vgscan.c [new file with mode: 0644]
tools/vgsplit.c [new file with mode: 0644]
udev/10-dm.rules.in [new file with mode: 0644]
udev/11-dm-lvm.rules [new file with mode: 0644]
udev/12-dm-permissions.rules [new file with mode: 0644]
udev/13-dm-disk.rules [new file with mode: 0644]
udev/95-dm-notify.rules [new file with mode: 0644]
udev/Makefile.in [new file with mode: 0644]
unit-tests/datastruct/Makefile.in [new file with mode: 0644]
unit-tests/datastruct/TESTS [new file with mode: 0644]
unit-tests/datastruct/bitset_t.c [new file with mode: 0644]
unit-tests/mm/Makefile.in [new file with mode: 0644]
unit-tests/mm/TESTS [new file with mode: 0644]
unit-tests/mm/check_results [new file with mode: 0755]
unit-tests/mm/pool_valgrind_t.c [new file with mode: 0644]
unit-tests/regex/Makefile.in [new file with mode: 0644]
unit-tests/regex/TESTS [new file with mode: 0644]
unit-tests/regex/dev_patterns [new file with mode: 0644]
unit-tests/regex/devices.list [new file with mode: 0644]
unit-tests/regex/matcher_t.c [new file with mode: 0644]
unit-tests/regex/matcher_t.expected [new file with mode: 0644]
unit-tests/regex/matcher_t.expected2 [new file with mode: 0644]
unit-tests/regex/matcher_t.expected3 [new file with mode: 0644]
unit-tests/regex/nonprint_input [new file with mode: 0644]
unit-tests/regex/nonprint_regexes [new file with mode: 0644]
unit-tests/regex/parse_t.c [new file with mode: 0644]
unit-tests/regex/random_regexes [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..5ab7695
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..8d0d54d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,31 @@
+Installation
+============
+
+1) Generate custom makefiles.
+
+   Run the 'configure' script from the top directory.
+
+   If you don't want to include the LVM1 backwards-compatibility code use:
+     ./configure --with-lvm1=none 
+
+   To separate the LVM1 support into a shared library loaded by lvm.conf use:
+     ./configure --with-lvm1=shared
+
+   Use ./configure --help to see other options.
+
+2) Build and install.
+
+   Run 'make' from the top directory to build everything you configured.
+   Run 'make install' to build and install everything you configured.
+
+   If you only want the device-mapper libraries and tools use
+   'make device-mapper' or 'make install_device-mapper'.
+
+3) If using LVM2, create a configuration file.
+
+   The tools will work fine without a configuration file being
+   present, but you ought to review the example file in doc/example.conf.
+
+Please also refer to the WHATS_NEW file and the manual pages for the 
+individual commands.
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..f7e34f4
--- /dev/null
@@ -0,0 +1,156 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SUBDIRS = doc include man scripts
+
+ifeq ("@UDEV_RULES@", "yes")
+  SUBDIRS += udev
+endif
+
+ifeq ("@INTL@", "yes")
+  SUBDIRS += po
+endif
+
+SUBDIRS += lib tools daemons libdm
+
+ifeq ("@APPLIB@", "yes")
+  SUBDIRS += liblvm
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+  SUBDIRS = doc include man scripts \
+    lib tools daemons libdm \
+    udev po liblvm test/api test
+endif
+DISTCLEAN_DIRS += lcov_reports*
+DISTCLEAN_TARGETS += config.cache config.log config.status make.tmpl
+
+include make.tmpl
+
+libdm: include
+lib: libdm
+liblvm: lib
+daemons: lib tools
+tools: lib device-mapper
+po: tools daemons
+
+lib.device-mapper: include.device-mapper
+libdm.device-mapper: include.device-mapper
+liblvm.device-mapper: include.device-mapper
+daemons.device-mapper: libdm.device-mapper
+tools.device-mapper: libdm.device-mapper
+device-mapper: tools.device-mapper daemons.device-mapper man.device-mapper
+
+ifeq ("@INTL@", "yes")
+lib.pofile: include.pofile
+tools.pofile: lib.pofile
+daemons.pofile: lib.pofile
+po.pofile: tools.pofile daemons.pofile
+pofile: po.pofile
+endif
+
+ifneq ("$(CFLOW_CMD)", "")
+tools.cflow: libdm.cflow lib.cflow
+daemons.cflow: tools.cflow
+cflow: include.cflow
+endif
+
+ifneq ("@CSCOPE_CMD@", "")
+cscope.out:
+       @CSCOPE_CMD@ -b -R -s$(top_srcdir)
+all: cscope.out
+endif
+DISTCLEAN_TARGETS += cscope.out
+
+check check_cluster check_local: all
+       $(MAKE) -C test $(@)
+
+install_system_dirs:
+       $(INSTALL_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR)
+       $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_ARCHIVE_DIR)
+       $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_BACKUP_DIR)
+       $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_CACHE_DIR)
+       $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_LOCK_DIR)
+       $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
+       $(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
+
+install_initscripts: 
+       $(MAKE) -C scripts install_initscripts
+
+LCOV_TRACES = libdm.info lib.info tools.info \
+       daemons/dmeventd.info daemons/clvmd.info
+CLEAN_TARGETS += $(LCOV_TRACES)
+
+ifneq ("$(LCOV)", "")
+.PHONY: lcov-reset lcov lcov-dated $(LCOV_TRACES)
+
+ifeq ($(MAKECMDGOALS),lcov-dated)
+LCOV_REPORTS_DIR := lcov_reports-$(shell date +%Y%m%d%k%M%S)
+lcov-dated: lcov
+else
+LCOV_REPORTS_DIR := lcov_reports
+endif
+
+lcov-reset:
+       $(LCOV) --zerocounters $(addprefix -d , $(basename $(LCOV_TRACES)))
+
+# maybe use subdirs processing to create tracefiles...
+$(LCOV_TRACES):
+       $(LCOV) -b $(top_srcdir)/$(basename $@) \
+               -d $(basename $@) -c -o - | $(SED) \
+               -e "s/\(dmeventd_lvm.[ch]\)/plugins\/lvm2\/\1/" \
+               -e "s/\(dmeventd_mirror.c\)/plugins\/mirror\/\1/" \
+               -e "s/\(dmeventd_snapshot.c\)/plugins\/snapshot\/\1/" \
+               >$@
+
+ifneq ("$(GENHTML)", "")
+lcov: $(LCOV_TRACES)
+       $(RM) -r $(LCOV_REPORTS_DIR)
+       $(MKDIR_P) $(LCOV_REPORTS_DIR)
+       for i in $(LCOV_TRACES); do \
+               test -s $$i && lc="$$lc $$i"; \
+       done; \
+       test -z "$$lc" || $(GENHTML) -p @abs_top_builddir@ \
+               -o $(LCOV_REPORTS_DIR) $$lc
+endif
+
+endif
+
+ifeq ("$(TESTING)", "yes")
+# testing and report generation
+RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test
+
+.PHONEY: unit-test ruby-test test-programs
+
+# FIXME: put dependencies on libdm and liblvm
+test-programs:
+       cd unit-tests/regex && $(MAKE)
+       cd unit-tests/datastruct && $(MAKE)
+       cd unit-tests/mm && $(MAKE)
+
+unit-test: test-programs
+       $(RUBY) report-generators/unit_test.rb $(shell find . -name TESTS)
+       $(RUBY) report-generators/title_page.rb
+
+memcheck: test-programs
+       $(RUBY) report-generators/memcheck.rb $(shell find . -name TESTS)
+       $(RUBY) report-generators/title_page.rb
+
+ruby-test:
+       $(RUBY) report-generators/test/ts.rb
+endif
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..bd6214c
--- /dev/null
+++ b/README
@@ -0,0 +1,27 @@
+This tree contains the LVM2 and device-mapper tools and libraries.
+
+For more information about LVM2 read the changelog in the WHATS_NEW file.
+Installation instructions are in INSTALL.
+
+There is no warranty - see COPYING and COPYING.LIB.
+
+Tarballs are available from:
+  ftp://sources.redhat.com/pub/lvm2/
+
+To access the CVS tree use:
+  cvs -d :pserver:cvs@sources.redhat.com:/cvs/lvm2 login
+  CVS password: cvs
+  cvs -d :pserver:cvs@sources.redhat.com:/cvs/lvm2 co LVM2
+
+Mailing list for general discussion related to LVM2:
+  linux-lvm@redhat.com
+  Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm
+
+Mailing list for LVM2 development, patches and commits:
+  lvm-devel@redhat.com
+  Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm
+
+Mailing list for device-mapper development, including kernel patches
+and multipath-tools:
+  dm-devel@redhat.com
+  Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..f8dbace
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+2.02.79(2) (2010-12-20)
diff --git a/VERSION_DM b/VERSION_DM
new file mode 100644 (file)
index 0000000..8593207
--- /dev/null
@@ -0,0 +1 @@
+1.02.60 (2010-12-20)
diff --git a/WHATS_NEW b/WHATS_NEW
new file mode 100644 (file)
index 0000000..08cc26c
--- /dev/null
+++ b/WHATS_NEW
@@ -0,0 +1,2418 @@
+Version 2.02.79 - 20th December 2010
+====================================
+  Remove some unused variables.
+  Add missing test for reallocation error in _find_parallel_space().
+  Add checks for allocation errors in config node clonning.
+  Fix error path if regex engine cannot be created in _build_matcher().
+  Use char* arithmetic in target_version(), _process_all() & _targets().
+  Fixing const cast gcc warnings in the code.
+  Check read() and close() results in _get_cmdline().
+  Add const for struct config_node usage.
+  Fix NULL pointer check in error path in clvmd do_command(). (2.02.78)
+  Fix device.c #include to ensure 64-bit fopen64 use. (2.02.51)
+  Add copy_percent and snap_percent to liblvm.
+  Enhance vg_validate to ensure integrity of LV and PV structs referenced.
+  Enhance vg_validate to check composition of pvmove LVs.
+  Create /var/run/lvm directory during clvmd initialisation if missing.
+  Use new dm_prepare_selinux_context instead of dm_set_selinux_context.
+  Avoid revalidating the label cache immediately after scanning.
+  Support scanning for a single VG in independent mdas.
+  Don't skip full scan when independent mdas are present even if memlock is set.
+  Set cmd->independent_metadata_areas if metadata/dirs or disk_areas in use.
+  Cope better with an undefined target_percent operation in _percent_run.
+  Avoid writing to freed memory in vg_release and rename to free_vg.  (2.02.78)
+
+Version 2.02.78 - 6th December 2010
+===================================
+  Abort if segment tag allocation fails in pool format _add_stripe_seg.
+  Abort in _mirrored_transient_status if referenced log/image LV is not active.
+  Add backtraces for dev_set() and dev_close_immediate() errors in set_lv().
+  Log any unlink() error in clvmd remove_lockfile().
+  Log any pipe write() or close() errors in clvmd child_init_signal().
+  Detect if orphan vginfo was lost from cache before _lvmcache_update_vgname().
+  Do a full rescan if some device is missing in lvm1 format read_pvs_in_vg.
+  Add missing check that dm_pool_create succeeded in write_config_node().
+  Use dm_snprintf in clvmd-command.c to ensure an overlong buffer is truncated.
+  Don't write to buffer if its reallocation failed in clvmd do_command().
+  Switch from float to fixed point percentage handling.
+  Avoid misleading missing PV warnings in vgextend --restoremissing.
+  Fix memory leak when VG allocation policy in metadata is invalid.
+  Ignore unrecognised allocation policy found in metadata instead of aborting.
+  Factor out tag printing into _out_tags and avoid leaking string buffer.
+  Remove some unused variables & assignments.
+  Add missing vg_release calls in _vg_read_by_vgid.
+  Fix debug logging of derived flag LCK_CACHE in clvmd.
+  Fix test for no system_dir in _init_backup().
+  Disallow lvconvert ops that both allocate & free supplied PEs in a single cmd.
+  Fix liblvm seg_size to give bytes not sectors.
+  Add functions to look up LV/PV by name/uuid to liblvm.
+  Free cmd_context if fallback to LVM1 fails in lvm2_main().
+  Free device name buffer in dmsetup parse_loop_device_name() error paths.
+  Close format lib if init_format_fn fails in _init_formats().
+  Don't leave /proc/mounts open after dmeventd snapshot event processing.
+  Fix out-of-scope arg_vgnames use in process_each_lv().
+  Remove incorrect dm_task_destroy(NULL) from _node_clear_table() error path.
+  Add missing closedir in _rm_blks after removing stray LVM1 VG files.
+  Suppress 'No PV label' message when removing several PVs without mdas.
+  Fix default /etc/lvm permissions to be 0755. (2.02.66)
+
+Version 2.02.77 - 22nd November 2010
+====================================
+  Allocate a pool for dummy VG in _pvsegs_sub_single.
+  Add PV and LV segment types and functions to liblvm.
+  Add set_property functions to liblvm.
+  Remove tag length restriction and allow / = ! : # & characters.
+  Support repetition of --addtag and --deltag arguments.
+  Add infrastructure for specific cmdline arguments to be repeated in groups.
+  Split the_args cmdline arguments and values into arg_props and arg_values.
+  Fix fsadm no longer to require '-f' to resize an unmounted filesystem.
+  Fix fsadm to detect mounted filesystems on older systems. (2.0.75)
+  Extend cling allocation policy to recognise PV tags (cling_by_tags).
+  Add allocation/cling_tag_list to lvm.conf.
+  Regenerate configure with 'autoreconf' for --enable-ocf. (2.02.76)
+
+Version 2.02.76 - 8th November 2010
+===================================
+  Clarify error messages when activation fails due to activation filter use.
+  Add pacemaker script VolumeGroup.ocf with configure --enable-ocf.
+  Import make.tmpl into include/ Makefile.
+  Fix handling of online filesystem resize (using new fsadm return code).
+  Add DIAGNOSTICS section to fsadm man page.
+  Modify fsadm to return different status code for check of mounted filesystem.
+  Update VG metadata only once in vgchange when making multiple changes.
+  Allow independent vgchange arguments to be used together.
+  Automatically unmount invalidated snapshots in dmeventd.
+  Suppress some superfluous messages from clang static analysis.
+  Fix a deadlock caused by double close in clvmd.
+  Fix NULL pointer dereference on too-large MDA error path in _vg_read_raw_area.
+  Use static for internal _align_chunk() and _new_chunk() from pool-fast.c.
+  Fix vgchange to process -a, --refresh, --monitor and --poll like lvchange.
+  Add lvm2app functions to query any pv, vg, or lv property / report field.
+
+Version 2.02.75 - 25th October 2010
+===================================
+  Annotate more variables and parameters as const.
+  Fix missing variable initialization in cluster_send() function from cmirrord.
+  Fix pointer for VG name in _pv_resize_single error code path.
+  Fix warning for changed alignment requirements for dmeventd read/write func.
+  Add global/metadata_read_only to use unrepaired metadata in read-only cmds.
+  Don't take write lock in vgchange --refresh, --poll or --monitor.
+  Skip dm devices in scan if they contain only error targets or are empty.
+  Fix strict-aliasing compile warning in partition table scanning.
+  Fix pthread mutex usage deadlock in clvmd.
+  Automatically extend snapshots with dmeventd according to policy in lvm.conf.
+  Add activation/snapshot_autoextend_threshold/percent to lvm.conf.
+  Fix liblvm2cmd link order to support --as-needed.
+  Remove dependency on libm by replacing floor() by an integer-based algorithm.
+  Fix hang when repairing a mirrored-log that had both devs fail.
+  Convey need for snapshot-merge target in lvconvert error message and man page.
+  Add devices/disable_after_error_count config to limit access to failing devs.
+  Give correct error message when creating a too-small snapshot.
+  Implement vgextend --restoremissing to reinstate missing devs that return.
+  Make lvconvert respect --yes and --force when converting an inactive log.
+  Refactor and add 'get' functions for lv properties/fields.
+  Update script for fsadm testing.
+  Better support of noninteractive shell execution of fsadm.
+  Fix usage of --yes flag for ReiserFS resize in fsadm.
+  Fix detection of mounted filesystems for fsadm when udev is used.
+  Fix assignment of default value to LVM variable in fsadm.
+  Fix support for --yes flag for fsadm.
+  Do not execute lvresize from fsadm --dry-run.
+  Fix fsadm return error code from user's break action.
+  Allow CC to be overridden at build time (for 'scan-build make').
+  Rename 'flags' to 'status' in struct metadata_area.
+  Avoid segfault by limiting partial mode for lvm1 metadata. (2.02.74)
+  Use dm_zalloc and dm_pool_zalloc throughout.
+  Add pv_get_property and create generic internal _get_property function.
+  Add 'get' functions for pv and vg properties/fields.
+  Make generic GET_*_PROPERTY_FN macros with secondary macro for vg, pv & lv.
+  Add tags_format_and_copy() common function and call from _tags_disp.
+  Add id_format_and_copy() common function and call from _uuid_disp.
+  Refactor report.c '*_disp' functions to call supporting functions.
+  Move parts of metadata*.[ch] into new {pv|vg|lv}.[ch] files.
+  Fix vg_read memory leak with directory-based metadata.
+  Fix memory leak of config_tree in reinitialization code path.
+  Fix pool destruction order in dmeventd_lvm2_exit() to avoid leak debug mesg.
+  Read whole /proc/self/maps file before working with maps entries.
+  Speed up unquoting of quoted double quotes and backslashes.
+  Speed up CRC32 calculations by using a larger lookup table.
+
+Version 2.02.74 - 24th September 2010
+=====================================
+  Allow : and @ to be escaped with \ in device names of PVs.
+  Replace alloca with dm_malloc in _aligned_io to avoid stack corruption.
+  Fix partial mode operations for lvm1 metadata format.
+  Track recursive filter iteration to avoid refreshing while in use. (2.02.56)
+  Revert to old glibc vsnprintf behaviour in emit_to_buffer() to catch overflow.
+  Allocate buffer for metadata tags dynamically to remove 4k limit.
+  Add random suffix to archive file names to prevent races when being created.
+  Reinitialize archive and backup handling on toolcontext refresh.
+  Make poll_mirror_progress report PROGRESS_CHECK_FAILED if LV is not a mirror.
+  Like mirrors, don't scan origins if ignore_suspended_devices() is set.
+  Fix return type qualifier to avoid compiler warning. (2.02.69)
+  Automatically generate LSB Requires-Start for clvmd init script.
+  Fix return code of pvmove --abort PV.
+  Fix pvmove --abort to remove even for empty pvmove LV.
+  Add configure --with-default-data-alignment.
+  Update heuristic used for default and detected data alignment.
+  Add "devices/default_data_alignment" to lvm.conf.
+  Add implementation for simple numeric 'get' property functions.
+  Define GET_NUM_PROPERTY_FN macro to simplify numeric property 'get' function
+  Add properties.[ch] to lib/report using columns.h.
+  Add macro definitions to report infrastructure for character array length.
+  Remove explicit double quotes from columns.h 'id' entries.
+  Add 'flags' field to columns.h and define FIELD_MODIFIABLE.
+  Add vg_mda_size and vg_mda_free functions.
+  Simplify MD/swap signature detection in pvcreate and allow aborting.
+  Allow --yes to be used without --force mode.
+  Fix file descriptor leak in swap signature detection error path.
+  Detect and allow abort in pvcreate if LUKS signature is detected.
+  Always mask lock flags correctly when checking for LCK_WRITE.
+
+Version 2.02.73 - 18th August 2010
+==================================
+  Fix potential for corruption during cluster mirror device failure.
+  Use 'SINGLENODE' instead of 'dead' in clvmd singlenode messages.
+  Ignore snapshots when performing mirror recovery beneath an origin.
+  Pass LCK_ORIGIN_ONLY flag around cluster.
+  Add suspend_lv_origin and resume_lv_origin using LCK_ORIGIN_ONLY.
+  Allow internal suspend and resume of origin without its snapshots.
+  Fix dev_manager_transient to access -real device not snapshot-origin.
+  Monitor origin -real device below snapshot instead of overlay device.
+  Don't really change monitoring status when in test mode.
+  Fix some exit statuses when starting/stopping monitoring fails.
+  Enable snapshot monitoring by default when dmeventd is enabled.
+  Move cloned libdevmapper-event client code from segments into lib/activate.
+  Fix 'lvconvert --splitmirrors' in cluster operation.
+  Fix clvmd init script exit code to return 4 when executed as non-root user.
+  Change default alignment of pe_start to 1MB.
+  Add --norestorefile option to pvcreate.
+  Require --restorefile when using pvcreate --uuid.
+  Recognise and give preference to md device partitions (blkext major).
+  Never scan internal LVM devices.
+  Don't ignore user-specified PVs in split-mirror operations. (2.02.71)
+  Fix data corruption bug in cluster mirrors.
+  Require logical volume(s) to be explicitly named for lvconvert --merge.
+  Avoid changing aligned pe_start as a side-effect of very verbose logging.
+  Use built-in rule for device aliases: block/ < dm- < disk/ < mapper/ < other.
+  Fix const warning in dev_manager_info() and _dev_manager_lv_rmnodes().
+  Fix const warning in archive_file structure from archive.c.
+  Clean generated files .exported_symbols_generated, example.conf for distclean.
+  Handle failure of all mirrored log devices and all but one mirror leg. 
+  Disallow 'mirrored' log type for cluster mirrors.
+  Do not use VPATH in include/Makefile.
+  Fix exported_symbols generation to use standard compiler arguments.
+  Use #include <> not "" in lvm2app.h which gets installed on the system.
+  Make lib and liblvm.device-mapper wait for include file generation.
+  Fix configure to supply DEFAULT_RUN_DIR to Makefiles.
+  Fix allocation of wrong number of mirror logs with 'remove' fault policy.
+
+Version 2.02.72 - 28th July 2010  [CVE-2010-2526]
+=================================================
+  Change clvmd to communicate with lvm2 via a socket in /var/run/lvm.
+  Return controlled error if clvmd is run by non-root user.
+  Add configure --default-run-dir for /var/run/lvm.
+  Never use clvmd singlenode unless explicitly requested with -Isinglenode.
+
+Version 2.02.71 - 28th July 2010
+================================
+  Document LVM fault handling in doc/lvm_fault_handling.txt.
+  Make vgck warn about missing PVs.
+  Clarify help text for vg_mda_count.
+  Check if cluster log daemon is running before allowing cmirror create.
+  Add unit-tests dir.
+  Add configure --enable-testing and reports and report-generators dirs.
+  Correct LV list order used by lvconvert when splitting a mirror.
+  Check if LV with specified name already exists when splitting a mirror.
+  Fix suspend/resume logic for LVs resulting from splitting a mirror.
+  Update pvcreate, {pv|vg}change, and lvm.conf man pages about metadataignore.
+  Switch cmirrord and clvmd to use dm_create_lockfile.
+  Allow clvmd pidfile to be configurable.
+  Update comments about memory handling in lvm2app.h.
+  Add more verbose messages while checking volume_list and hosttags settings.
+  Add log_error when strdup fails in {vg|lv}_change_tag().
+  Remove unnecessary includes in liblvm files.
+  Use __attribute__ consistently throughout.
+  Fix redundant declarations and always compile with -Wredundant-decls.
+  Fix possible hang when all mirror images of a mirrored log fail.
+  Pass metadataignore to pv_create, pv_setup, _mda_setup, and add_mda.
+  Init mda->list in mda_copy.
+  Do not log backtrace in valid _lv_resume() code path.
+  Cleanup help strings in configure.in.
+  Prompt if metadataignore with vgextend or pvchange would adjust vg_mda_copies.
+  Adjust vg_mda_copies if metadataignore given with vgextend or pvchange.
+  Adjust auto-metadata repair and caching logic to try to cope with empty mdas.
+
+Version 2.02.70 - 6th July 2010
+===============================
+  Remove log directly if all mirror images of a mirrored log fail.
+  Randomly select which mdas to use or ignore.
+  Add some missing standard configure.in checks.
+  Add printf format attributes to yes_no_prompt and fix a caller.
+  Always pass unsuspended dm devices through persistent filter to other filters.
+  Move test for suspended dm devices ahead of other filters.
+  Fix another segfault in clvmd -R if no response from daemon. (2.02.68)
+  Remove superfluous suspended device counter from clvmd.
+  Fix lvm shell crash when input is entirely whitespace.
+  Update partial mode warning message.
+  Preserve memlock balance in clvmd when activation triggers a resume.
+  Restore the removemissing behaviour of lvconvert --repair --use-policies.
+
+Version 2.02.69 - 30th June 2010
+================================
+  Fix vgremove to allow removal of VG with missing PVs. (2.02.52)
+  Add metadata/vgmetadatacopies to lvm.conf.
+  Add --metadataignore to pvcreate and vgextend.
+  Add vg_mda_copies, pv_mda_used_count and vg_mda_used_count to reports.
+  Describe --vgmetadatacopies in lvm.conf and other man pages.
+  Add --[vg]metadatacopies to select number of mdas to use in a VG.
+  Make the metadata ignore bit control read/write metadata areas in a PV.
+  Add pvchange --metadataignore to set or clear a metadata ignore bit.
+  Refactor metadata code to prepare for --metadataignore / --vgmetadatacopies.
+  Ensure region_size of mirrored log does not exceed its full size.
+  Generate liblvm2app exported symbols from header file.
+  Preload libc locale messages to prevent reading it in memory locked state.
+  Fix handling of simultaneous mirror image and mirrored log image failure.
+
+Version 2.02.68 - 23rd June 2010
+================================
+  Fix clvmd initscript status to print only active clustered LVs.
+  Add lv_path to reports to offer full /dev pathname.
+  Fix typo in warning message about missing device with allocated data areas.
+  Add device name and offset to raw_read_mda_header error messages.
+  Honour log argument when down-converting stacked mirror.
+  Sleep to workaround clvmd -S race: socket closed early and server drops cmd.
+  Use early udev synchronisation and update of dev nodes for clustered mirrors.
+  Remove incorrect inclusion of kdev_t.h from cmirrord/functions.h.
+  Add man pages for lvmconf and non-existent lvmsadc and lvmsar tools.
+  Exit successfully when using -o help (but not -o +help) with LVM reports.
+  Do not use internal DLM lock definitions in generic LVM2 clvmd code.
+  Add --force, --nofsck and --resizefs to lvresize/extend/reduce man pages.
+  Fix lvm2cmd example in documentation.
+  Allow use of lvm2app and lvm2cmd headers in C++ code.
+  Remove unused #includes from clvmd files and introduce clvmd-common.h.
+  Move common inclusions to clvmd-common.h.
+  Use #include "" for libdevmapper.h and configure.h throughout tree.
+  Fix LVM_PATH expansion when exec_prefix=NONE. (2.02.67)
+  Fix segfault in clvmd -R if no response from daemon received.
+
+Version 2.02.67 - 4th June 2010
+===============================
+  Handle failed restart of clvmd using -S switch properly.
+  Fix clvmd initscript restart command to start clvmd if not yet running.
+  Use built-in absolute paths in clvmd (clvmd restart and PV and LV queries).
+  Require partial option in lvchange --refresh for partial LVs.
+  Do not fail lvm_init() if init_logging() or _init_rand() generates an errno.
+  Don't merge unchanged persistent cache file before dumping if tool scanned.
+  Fix incorrect memory pool deallocation while using vg_read for files.
+  Add --type parameter description to the lvcreate man page.
+  Replace strncmp kernel version number checks with proper ones.
+  Avoid selecting names under /dev/block if there is an alternative.
+  Update clustered log kernel module name to log-userspace for 2.6.31 onwards.
+  Add replicators' LVs to dtree for activation.
+  Supress activation message if there is a missing replicator VG.
+  Fix scripts/relpath.awk to work in mawk
+  Extend lock_vol to check for missing replicator VGs first.
+  Update _process_one_vg and process_each_lv_in_vg to populate cmd_vg.
+  Add cmd_vg structure and associated functions for replicator.
+  Extend _lv_each_dependency() to handle replicator dependencies.
+  Add check_replicator_segment() to catch internal replicator errors.
+  Initial support for replicator metadata.
+  Extend process_each_lv_in_vg() to provide list of failed lvnames.
+  Consistently return ECMD_FAILED if process_each_*lv() is interrupted.
+
+Version 2.02.66 - 20th May 2010
+===============================
+  If unable to obtain snapshot percentage leave value blank on reports.
+  Add install_system_dirs and install_initscripts makefile targets.
+  Add configure options for system and locking directories.
+  Generate example.conf so default lvm.conf contents can be configured.
+  Install lvmconf script by default.
+  Remove unnecessary versioned dmeventd plugin symlinks.
+  Add tests for lvm_vgname_from_{pvid|device}.
+  Add lvm2app interfaces to lookup a vgname from a pvid and pvname.
+  Update pvchange to always obtain a vg handle for each pv to process.
+  Add find_vgname_from_{pvname|pvid} functions.
+  Add pvid_from_devname and lvmcache_vgname_from_pvid lvmcache functions.
+  Validate orphan and VG_GLOBAL lock order too.
+  Accept orphan VG names as parameters to lock_vol() and related functions.
+  Use is_orphan_vg in place of hard-coded prefix tests and add is_global_vg.
+
+Version 2.02.65 - 17th May 2010
+===============================
+  Fix clvmd init script never to deactivate non-clustered volume groups.
+  Disallow vgchange --clustered if there are active mirrors or snapshots.
+  Introduce lv_is_mirrored.
+  Use /bin/bash for scripts with bashisms.
+  Skip internal lvm devices in scan if ignore_suspended_devices is set.
+  Do not merge old device cache after we run full scan. (2.02.56)
+  Add pkgconfigdir Makefile variable for make install override.
+  Configure pkgconfig udev and selinux dependencies.
+  Switch Libs.private to Requires.private in devmapper.pc and lvm2app.pc.
+  Use pkgconfig Requires.private for devmapper-event.pc.
+  Add libdevmapper to linked libdevmapper-event.so.
+  Link liblvm2cmd.so with libdevmapper-event and libdevmapper.
+  Fix truncated total size displayed by pvscan.
+  Add new --sysinit compound option to vgchange and lvchange.
+  Drop duplicate errors for read failures and missing devices to verbose level.
+  Use $(libdir)/lvm2 with make install_lvm2_plugin.
+  Use $(libdir)/device-mapper with make install_dm_plugin.
+  Add dm_list_splice() function to join two lists together.
+
+Version 2.02.64 - 30th April 2010
+=================================
+  Avoid pointless initialisation when the 'version' command is run directly.
+  Fix memory leak for invalid regex pattern input.
+  Display invalid regex pattern for filter configuration in case of error.
+  Remove no-longer-used arg_ptr_value.
+  Fix -M and --type to use strings, not pointers that change on config refresh.
+  Fix lvconvert error message when existing mirrored LV is not found.
+  Set appropriate udev flags for reserved LVs.
+  Disallow the direct removal of a merging snapshot.
+  Don't preload the origin when removing a snapshot whose merge is pending.
+  Disallow the addition of mirror images while a conversion is happening.
+  Disallow primary mirror image removal when mirror is not in-sync.
+  Remove obsolete --name parameter from vgcfgrestore.
+  Add -S command to clvmd to restart the daemon preserving exclusive locks.
+  Increment lvm2app version from 1 to 2 (memory allocation changes).
+  Change lvm2app memory alloc/free for pv/vg/lv properties.
+  Change daemon lock filename from lvm2_monitor to lvm2-monitor for consistency.
+  Install symbolic .so links with relative paths between usrlibdir and libdir.
+  Add awk script relpath.awk to calculate paths for relative symlinks.
+  Use @AWK@ in makefiles.
+  Fix double DESTDIR usage for infodir and mandir.
+
+Version 2.02.63 - 14th April 2010
+=================================
+  Rename lvm_dump.sh to lvmdump.sh.
+  Allow incomplete mirror restore in lvconvert --repair upon insufficient space.
+  Do not reset position in metadata ring buffer on vgrename and vgcfgrestore.
+  Allow VGs with active LVs to be renamed.
+  Use UUIDs instead of names while processing event handlers.
+  Only pass visible LVs to tools in cmdline VG name/tag expansions without -a.
+  Use typedefs for toollib process_each functions.
+  Use C locales and use_mlockall for clvmd.
+  Refactor code related to vg->pvs list and add pv->vg link.
+  Mask LCK_HOLD flag in cluster VG locks for backwards compatibility.
+  Add activation/polling_interval to lvm.conf as --interval default.
+  Don't ignore error if resuming any LV fails in resume_lvs.
+  Skip closing persistent filter cache file if open failed.
+  Install .a and .so links into $(usrlibdir).
+  Add --enable-write_install options to install user-writable files.
+  Use INSTALL_PROGRAM/DATA/WDATA target.
+  Switch from using VPATH  to vpath in Makefiles.
+  Permit mimage LVs to be striped in lvcreate, lvresize and lvconvert.
+  Fix pvmove allocation to take existing parallel stripes into account.
+  Add pvmove_source_seg to struct lv_segment.
+  Fix incorrect removal of symlinks after LV deactivation fails.
+  Fix is_partitioned_dev not to attempt to reopen device.
+  Fix another thread race in clvmd.
+  Refactor management of vg->pvs list.
+  Fix lcov rules and generate better coverage report.
+  Improve vg_validate to detect some loops in lists.
+  Change most remaining log_error WARNING messages to log_warn.
+  Always use blocking lock for VGs and orphan locks.
+  Allocate all memory for segments from private VG mempool.
+  Return newly allocated PV segment after segment split.
+  Optimise searching PV segments for seeking the most recently-added.
+  Remove vg_validate call when parsing cached metadata.
+  Use hash table of LVs to speed up parsing of text metadata with many LVs.
+  Fix two vg_validate messages, adding whitespace and parentheses.
+  When dmeventd is not forking because of -d flag, don't kill parent process.
+  Fix 'make install' when $(builddir) is different from $(srcdir).
+  Fix dso resource leak in error path of dmeventd.
+  Use C locales and use_mlockall for dmeventd.
+  Fix --alloc contiguous policy only to allocate one set of parallel areas.
+  Do not allow {vg|lv}change --ignoremonitoring if on clustered VG.
+  Improved dependency tracking for dmeventd and liblvm2cmd sources.
+  Improved Makefile rules for distclean and cflow targets.
+  Add ability to create mirrored logs for mirror LVs.
+  Fix clvmd cluster propagation of dmeventd monitoring mode.
+  Allow ALLOC_ANYWHERE to split contiguous areas.
+  Use INTERNAL_ERROR for internal errors throughout tree.
+  Add some assertions to allocation code.
+  Introduce pv_area_used into allocation algorithm and add debug messages.
+  Add activation/monitoring to lvm.conf.
+  Add --monitor and --ignoremonitoring to lvcreate.
+  Allow dynamic extension of array of areas selected as allocation candidates.
+  Export and use only valid cookie value in test suite.
+  Remove const modifier for struct volume_group* from process_each_lv_in_vg().
+  Don't allow resizing of internal logical volumes.
+  Fix libdevmapper-event pkgconfig version string to match libdevmapper.
+  Avoid scanning all pvs in the system if operating on a device with mdas.
+  Add configure --with-clvmd=singlenode to use clvmd w/o cluster infrastructure.
+  Get stacktrace if testsuite test drops core and lvm was built with debugging.
+  Disable long living process flag in lvm2app.
+  Fix pvcreate device md filter check.
+  Suppress repeated errors about the same missing PV uuids.
+  Bypass full device scans when using internally-cached VG metadata.
+  Only do one full device scan during each read of text format metadata.
+  Remove unnecessary full_scan parameter from get_vgids and get_vgnames calls.
+  Look up missing PVs by uuid not dev_name in _pvs_single to avoid invalid stat.
+  Make find_pv_in_vg_by_uuid() return same type as related functions.
+  Introduce is_missing_pv().
+  Fix clvmd Makefile to not overwrite LIBS from template definition.
+
+Version 2.02.62 - 9th March 2010
+================================
+  Add use_mlockall and mlock_filter to activation section of lvm.conf.
+  Add default alternative to mlockall using mlock to reduce pinned memory size.
+  Remove -rdynamic from static builds.
+  Update checks for pthread, readline & selinux libs and link only when needed.
+  Introduce makefile vars UDEV_LIBS, DL_LIBS, SELINUX_LIBS, STATIC_LIBS.
+  Introduce makefile vars LVMINTERNAL_LIBS, READLINE_LIBS, PTHREAD_LIBS.
+  Toggle configure help to print --disable-fsadm.
+  Use $() instead of ${} consistently for all Makefile variables.
+  Replace CFLOW_CMD only in make.tmpl and use it as variable elsewhere.
+  Use $(top_builddir) for inclusion of make.tmpl in Makefiles.
+  Fix autoconf warning about ignored datarootdir.
+  Increase AC_PREREQ version to 2.61 (for AC_PROC_SED, AC_PROG_MKDIR_P).
+  Handle misaligned devices that report alignment_offset of -1.
+  Extend core allocation code in preparation for mirrored log areas.
+  Rewrite clvmd init script.
+  Remove lvs_in_vg_activated_by_uuid_only call.
+  No longer fall back to looking up active devices by name if uuid not found.
+  Don't touch /dev in vgmknodes if activation is disabled.
+  Update lvm2app.h Doxygen comments and add lvm2app Doxygen config file.
+  Update nightly tests and lvm2app unit tests to cover tags.
+  Add lvm2app functions lvm_{vg|lv}_{get|add|remove}_tag() functions.
+  Add dm_pool_strdup to allocate and copy memory in tag library function.
+  Refactor vgcreate, vgchange, and lvchange for tag library function.
+  Refactor snapshot-merge deptree and device removal to support info-by-uuid.
+
+Version 2.02.61 - 15th February 2010
+====================================
+  Fix some consts and floating point gcc warnings.
+  Fix dm_report_field_uint64 function to accept 64-bit ints.
+  Change readhead display to use 32-bit -1 const instead of 64-bit.
+  Add LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES environment variable.
+  Remove hard-coding that skipped _mimage devices from 11-dm-lvm.rules.
+  Use udev transactions in test suite.
+  Set udev state automatically instead of using LVM_UDEV_DISABLE_CHECKING.
+  Add lvm_pv_get_size, lvm_pv_get_free and lvm_pv_get_dev_size to lvm2app.
+  Change lvm2app to return all sizes in bytes as documented (not sectors).
+  Add 'fail_if_percent_unsupported' arg to _percent and _percent_run.
+  Remove false "failed to find tree node" error when activating merging origin.
+  Exit with success when lvconvert --repair --use-policies performs no action.
+  Accept a list of LVs with 'lvconvert --merge @tag' using process_each_lv.
+  Avoid unnecessary second resync when adding mimage to core-logged mirror.
+  Exclude internal VG names and uuids from lists returned through lvm2app.
+  Add %ORIGIN support to lv{create,extend,reduce,resize} --extents.
+  Add _mda_copy to clone a struct metadata_area.
+  Remove pointless versioned symlinks to dmeventd plugin libraries.
+  Fix dmeventd snapshot plugin build dependency.
+  Make clvmd -V return status zero.
+  Remove unnecessary 'dmsetup resume' following 'dmsetup create' in tests.
+  Fix cmirrord segfault in clog_cpg list processing when converting mirror log.
+  Deactivate temporary pvmove mirror cluster-wide when activating it fails.
+  Always query device by uuid and not name in clvmd.
+  Add missing metadata vg_reverts in pvmove error paths.
+  Unlock shared lock in clvmd if activation calls fail.
+  Return success from dev_manager_info with non-existent uuid if ioctl succeeds.
+
+Version 2.02.60 - 23rd January 2010
+===================================
+  Extend cmirrord man page.
+  Sleep before first progress check if pvmove/lvconvert interval has prefix '+'.
+  Default to checking progress before waiting in _wait_for_single_lv.
+  Fix cmirror initscript (including syntax error).
+  Eliminate avoidable ioctls for checking open_count in _add_new_lv_to_dtree.
+  Disable memory debugging if dmeventd is configured. (Not thread-safe.)
+  Fix first log message prefix in syslog for dmeventd plugins.
+  Fix exported symbols names for dmeventd lvm2 wrapper plugin.
+  Make failed locking initialisation messages more descriptive.
+
+Version 2.02.59 - 21st January 2010
+===================================
+  Add libdevmapper-event-lvm2.so to serialise dmeventd plugin liblvm2cmd use.
+  Cleanup memory initialization and freeing in pv_read() and pv_create().
+  Clear pointer and counters after their release in _fin_commands().
+  Stop dmeventd trying to access already-removed snapshots.
+  Remove (fallback) /dev mknod from cmirrord.
+  Add t-topology-support.sh and t-snapshot-merge.sh tests.
+  Fix clvmd to never scan suspended devices.
+  Fix dmeventd build outside source tree.
+  Assorted cmirror code changes to remove various compiler warnings.
+  Fix detection of completed snapshot merge.
+  Add Red Hat cmirror initscript (unfinished).
+  Add cmirrord man page (incomplete).
+  Make cluster log communication structures architecture independant.
+  Fix cluster log in-memory bitmap handling.
+  Improve snapshot merge metadata import validation.
+  Improve target type compatibility checking in _percent_run().
+  Add 'target_status_compatible' method to 'struct segtype_handler'.
+  Change underscore to hyphen in table line for clustered log type.
+
+Version 2.02.58 - 14th January 2010
+===================================
+  Cleanup some minor gcc warnings.
+  Add --merge to lvconvert to merge a snapshot into its origin.
+  Fix clvmd automatic target module loading crash (no reset_locking fn).
+  Fix allocation code not to stop at the first area of a PV that fits.
+
+Version 2.02.57 - 12th January 2010
+===================================
+  Ensure exactly one process returns from poll_daemon(), never two.
+  Reset _vgs_locked in lvmcache_init() in child after forking.
+  Define {DM, LVM}_UDEV_DISABLE_CHECKING=1 environment variables during tests.
+  Enable udev_sync and udev_rules in lvm.conf by default while running tests.
+  If LVM_UDEV_DISABLE_CHECKING in set in environment, disable udev warnings.
+  Add --splitmirrors to lvconvert to split off part of a mirror.
+  Change background polldaemon's process name to "(lvm2)".
+  Allow vgremove to remove a VG with PVs missing after a prompt.
+  Return success in lvconvert --repair --use-policies on failed allocation.
+  Keep log type consistent when changing mirror image count.
+  Always set environment variables for an LVM2 device in 11-dm-lvm.rules.
+  Add activation/udev_rules config option in lvm.conf.
+  Add consts to text metadata flag structs.
+  Add macros outfc, outsize, outhint and function out_text_with_comment.
+  Reimplement report FIELD macro using offsetof instead of static structs.
+  Fix fsadm man page typo (fsdam).
+  Rename mirror_device_fault_policy to mirror_image_fault policy.
+  Remove empty PV devices if lvconvert --repair is using defined policies.
+  Use fixed buffer to prevent stack overflow in persistent filter dump.
+  Use extended status of new kernel snapshot target 1.8.0 to detect when empty.
+  Insert stack macros in suspend_lv, resume_lv & (de)activate_lv callers.
+  Add --poll flag to vgchange and lvchange to control background daemon launch.
+  Propagate metadata commit and revert notifications to other cluster nodes.
+  Use proper mask for VG lock mode in clvmd.
+  Allow precommitted metadata to be dropped from lvmcache.
+  Move processing of VG locks to separate function in clvmd.
+  Properly decode all flags in clvmd messages including VG locks.
+  Properly handle precommitted cache flag when only committed metadata present.
+  Resume renamed volumes in reverse order to preserve memlock pairing.
+  Drop cached metadata after device was auto-repaired and removed from VG.
+  Clear MISSING_PV flag if PV reappeared and is empty.
+  Fix removal of multiple devices from a mirror.
+  Also clean up PVs flagged as missing in vgreduce --removemissing --force.
+  Introduce INTERNAL_ERROR macro for error messages and use throughout.
+  Remove superfluous returns from void functions.
+  Destroy allocated mempool in _vg_read_orphans() error path.
+  Fix some pvresize and toollib error paths with missing VG releases/unlocks.
+  Explicitly call suspend for temporary mirror layer.
+  Allow use of precommitted metadata when a PV is missing.
+  Add memlock information to do_lock_lv debug output.
+  Always bypass calls to remote cluster nodes for non-clustered VGs.
+  Permit implicit cluster lock conversion in pre/post callbacks on local node.
+  Permit implicit cluster lock conversion to the lock mode already held.
+  Fix lock flag masking in clvmd so intended code paths get invoked.
+  Replace magic masks in cluster locking code by defined masks.
+  Remove newly-created mirror log from metadata if initial deactivation fails.
+  Correct activated or deactivated text in vgchange summary message.
+  Improve pvmove error message when all source LVs are skipped.
+  Fix memlock imbalance in lv_suspend if already suspended.
+  Fix pvmove test mode not to poll (and fail).
+  Fix vgcreate error message if VG already exists.
+  Fix tools to use log_error when aborted due to user response to prompt.
+  Fix ignored readahead setting in lvcreate --readahead.
+  Fix clvmd memory leak in lv_info_by_lvid by calling release_vg.
+  If aborting due to internal error, always send that message to stderr.
+  Add global/abort_on_internal_errors to lvm.conf to assist testing.
+  Fix test Makefiles when builddir and srcdir differ.
+  Impose limit of 8 mirror images to match the in-kernel kcopyd restriction.
+  Use locking_type 3 (compiled in) for lvmconf --enable-cluster.
+  Remove list.c and list.h with no-longer-used dm_list macros and functions.
+  Log failure type and recognise type 'F' (flush) in dmeventd mirror plugin.
+  Extend internal PV/VG/LV/segment status variables from 32-bit to 64-bit.
+
+Version 2.02.56 - 24th November 2009
+====================================
+  Add missing vg_release to pvs and pvdisplay to fix memory leak.
+  Do not try to unlock VG which is not locked in _process_one_vg.
+  Move is_long_lived persistent_filter_dump to happen after every full scan.
+  Refresh device filters before full device rescan in lvmcache.
+  Return error status if vgchange fails to activate some volume.
+  Fix suspend/resume lock type test causing unbalanced memory locking.
+  Revert vg_read_internal change as clvmd was not ready for vg_read. (2.02.55)
+
+Version 2.02.55 - 19th November 2009
+====================================
+  Fix deadlock when changing mirrors due to unpaired memlock refcount changes.
+  Use separate memlock counter for dmeventd handlers to permit device scanning.
+  Directly restrict vgchange to activating visible LVs.
+  Fix pvmove region_size overflow for very large PVs.
+  Fix lvcreate and lvresize %PVS argument always to use sensible total size.
+  Tidy some uses of arg_count and introduce arg_is_set.
+  Export outnl and indent functions for modules.
+  Flush stdout after yes/no prompt.
+  Update vgsplit and vgcreate to use vg_set_clustered.
+  Add vg_mda_count and vg_set_clustered library functions.
+  Add more vgcreate and vgsplit nightly tests.
+  Insert some missing stack macros into activation code.
+  Recognise DRBD devices and handle them like md devices.
+
+Version 2.02.54 - 26th October 2009
+===================================
+  Update lvcreate/lvconvert man pages to explain PhysicalVolume parameter.
+  Document --all option in man pages, cleanup {pv|vg|lv}{s|display} man pages.
+  Permit snapshots of mirrors.
+  Cleanup mimagetmp LV if allocation fails for new lvconvert mimage.
+  Fix clvmd segfault when refresh_toolcontext fails.
+  Remember to clear 'global lock held during cache refresh' state after use.
+  Use udev flags support in LVM and apply various fixes to udev rules.
+  Delay announcing mirror monitoring to syslog until initialisation succeeded.
+  Handle metadata with unknown segment types more gracefully.
+  Set default owner and group to null.
+  Add dmeventd.static to the build.
+  Disable realtime support code by default.
+  Make clvmd return 0 on success rather than 1.
+  Add --pvmetadatacopies for pvcreate, vgcreate, vgextend, vgconvert.
+  Add implict pvcreate support to vgcreate and vgextend.
+  Correct example.conf to indicate that lvm2 not lvm1 is the default format.
+  Remove an unused stray LVM1_SUPPORT ifdef.
+  Only include selinux libs in libdevmapper.pc when selinux build enabled.
+  Allow for a build directory separate from the source.
+  Update distclean target for rename clogd to cmirrord. (2.02.52)
+  Only do lock conversions in clvmd if we are explicitly asked for one.
+  Introduce percent_range_t and centralise snapshot full/mirror in-sync checks.
+  Factor out poll_mirror_progress and introduce progress_t.
+  Distinguish between powers of 1000 and powers of 1024 in unit suffixes.
+  Restart lvconverts in vgchange by sharing lv_spawn_background_polling.
+  Generalise polldaemon code by changing mirror-specific variable names.
+  Don't attempt to deactivate an LV if any of its snapshots are in use.
+  Return error if lv_deactivate fails to remove device from kernel.
+  Provide alternative implementation of obsolete siginterrupt().
+  Consolidate LV allocation into alloc_lv().
+  Treat input units of both 's' and 'S' as 512-byte sectors.  (2.02.49)
+  Use standard output units for 'PE Size' and 'Stripe size' in pv/lvdisplay.
+  Add configure --enable-units-compat to set si_unit_consistency off by default.
+  Add global/si_unit_consistency to enable cleaned-up use of units in output.
+
+Version 2.02.53 - 25th September 2009
+=====================================
+  Create any directories in /dev with DM_DEV_DIR_UMASK (022).
+  Enable dmeventd monitoring section of config file by default.
+  Update lvm2 monitoring script to lvm2_monitoring_init_red_hat.in.
+  Fix lvm2app test to run under test/api subdirectory only when configured.
+  Add vg_is_resizeable() and cleanup reference to VG_RESIZEABLE.
+
+Version 2.02.52 - 15th September 2009
+=====================================
+  Update _process_one_vg to cleanup properly after vg_read_error.
+  Add lots of missing stack debug messages to tools.
+  Make readonly locking available as locking type 4.
+  Fix readonly locking to permit writeable global locks (for vgscan). (2.02.49)
+  Add DM_UDEV_RULES_VSN environment variable to udev rules.
+  Update vgsplit, vgmerge, and vgrename to obey new vgname ordering rules.
+  Make lvm2app pv_t, lv_t, vg_t handle definitions consistent with lvm_t.
+  Enforce an alphabetical lock ordering on vgname locking.
+  Prioritise write locks over read locks by default for file locking.
+  Add local lock files with suffix ':aux' to serialise locking requests.
+  Fix global locking in PV reporting commands (2.02.49).
+  Fix pvcreate string termination in duplicate uuid warning message.
+  Don't loop reading sysfs with pvcreate on a non-blkext partition (2.02.51).
+  Fix vgcfgrestore error paths when locking fails (2.02.49).
+  Update Makefile distclean target.
+  Add libudev configuration check.
+  Make clvmd check corosync to see what cluster interface it should use.
+  Add clvmd autodetection check and cleanup related configure messages.
+  Rewrite clvmd configuration code to cope with all combinations of libs.
+  Added configure --enable-cmirrord to build the cluster mirror log daemon.
+  Rename clogd to cmirrord.
+  Make lvchange --refresh only take a read lock on volume group.
+  Fix race where non-blocking file locks could be granted in error.
+  Fix vgextend error path - if ORPHAN lock fails, unlock / release vg (2.02.49).
+  Fix compile warning in clvmd.
+  Clarify use of PE ranges in lv{convert|create|extend|resize} man pages.
+  Remove useless _pv_write wrapper.
+  Add lvm2app.sh to tests conditional upon configure --enable-applib.
+  Add lvm_vg_is_clustered, lvm_vg_is_exported, and lvm_vg_is_partial.
+  Update lvm_vg_remove to require lvm_vg_write to commit remove to disk.
+  Update test/api/test.c to call lvm_vg_create and lvm_vg_remove.
+
+Version 2.02.51 - 6th August 2009
+=================================
+  Fix locking in clvmd (2.02.50).
+  Add --noudevsync option for relevant LVM tools.
+  Add activation/udev_sync to lvm.conf.
+  Only change LV symlinks on ACTIVATE not PRELOAD.
+  Make lvconvert honour log mirror options combined with downconversion.
+  Allow LV suspend while --ignorelockingfailure is in force.
+  Update synopsis in lvconvert manpage to mention --repair.
+  Set cookies in activation code and wait for udev to complete processing.
+  Added configure --enable-udev_rules --enable-udev_sync.
+  Added configure --with-udev-prefix --with-udevdir.
+  Added udev dir to hold udev rules.
+  Add devices/data_alignment_detection to lvm.conf.
+  Add devices/data_alignment_offset_detection to lvm.conf.
+  Add --dataalignmentoffset to pvcreate to shift start of aligned data area.
+  Fix _mda_setup() to not check first mda's size before pe_align rounding.
+  Document -I option of clvmd in the man page.
+  Fix configure script to handle multiple clvmd selections.
+  Fix lvm2app.pc installation filename.
+  Remove pv_t, vg_t & lv_t handles from lib.  Only liblvm uses them.
+  Rename lvm.h to lvm2app.h for now.
+
+Version 2.02.50 - 28th July 2009
+================================
+  Change test/api/test.c prompt so it's not confused with the main lvm prompt.
+  Update liblvm unit tests in test/api to cover latest liblvm changes.
+  Add unimplemented lvm_lv_resize and lvm_pv_resize skeletons to liblvm.
+  Add lvm_library_get_version to liblvm.
+  Add lvm_config_override to liblvm to allow caller to override LVM config.
+  Add lvm_lv_is_active and lvm_lv_is_suspended to liblvm.
+  Add lvm_lv_activate and lvm_lv_deactivate to liblvm.
+  Add lvm_scan, lvm_vg_reduce and lvm_vg_remove_lv to liblvm.
+  Add functions to get numeric properties to liblvm.
+  Add lvm_{pv|vg|lv}_get_{name|uuid} to liblvm.
+  Add lvm_vg_list_pvs and lvm_vg_list_lvs to liblvm.
+  Add lvm_vg_open and lvm_vg_create_lv_linear to liblvm.
+  Add lvm_list_vg_names/uuids to liblvm.
+  Add lvm_errno and lvm_errmsg to liblvm to obtain failure information.
+  Rename lvm_create/destroy to lvm_init/quit.
+  Rename lvm_reload_config to lvm_config_reload.
+  Refactor _override_settings to use new override_config_tree_from_string.
+  Add vg_reduce to metadata.c and metadata-exported.h.
+  Update lvm.h to clarify API behavior and return codes.
+  Update lvm_vg_extend to do an implicit pvcreate on the device.
+  Update display.c to use vg_free(vg) instead of duplicating the calculation.
+  Refactor vg_size, vg_free, and pv_mda_count field calculations for liblvm.
+  Refactor pvcreate and lvcreate for liblvm.
+  Add global/wait_for_locks to lvm.conf so blocking for locks can be disabled.
+  All LV locks are non-blocking so remove LCK_NONBLOCK from separate macros.
+  Fix race condition with vgcreate and vgextend on same device (2.02.49).
+  Remove redundant validate_name call from vgreduce.
+  Remove unused handles lvseg, pvseg inside liblvm/lvm.h.
+  Add liblvm2app Makefile installation targets.
+  Add liblvm pkgconfig file.
+  Use newly-independent LVM_LIBAPI in liblvm soname.  E.g. liblvm2app.so.2.1.
+  Add an API version number, LVM_LIBAPI, to the VERSION string for liblvm.
+  Pass a pointer to struct cmd_context to init_multiple_segtypes
+  Return EINVALID_CMD_LINE not success when invalid VG name format is used.
+  Remove unnecessary messages after vgcreate/vgsplit refactor (2.02.49).
+  Add log_errno to set a specific errno and replace log_error in due course.
+  Change create_toolcontext to still return an object if it fails part-way.
+  Add EUNCLASSIFIED (-1) as the default LVM errno code.
+  Store any errno and error messages issued while processing each command.
+  Use log_error macro consistently throughout in place of log_err.
+
+Version 2.02.49 - 15th July 2009
+================================
+  Add readonly locking type to replace implementation of --ignorelockingfailure.
+  Exclude VG_GLOBAL from vg_write_lock_held so scans open devs read-only again.
+  Add unit test case for liblvm VG create/delete APIs.
+  Add liblvm APIs to implement creation and deletion of VGs.
+  Initialize cmd->cmd_line to "liblvm" in new liblvm library.
+  Place handles to liblvm objects for pv, vg, lv, lvseg, pvseg inside lvm.h.
+  Refactor vgsplit and vgextend to remove READ_REQUIRE_RESIZEABLE flag.
+  Use _exit() not exit() after forking to avoid flushing libc buffers twice.
+  Add cast to log_info arg in _find_labeller to avoid Sparc64 warning.
+  Make cmd->cmd_line const.
+  Fix dev name mismatch in vgcreate man page example.
+  Refactor vg_remove_single for use in liblvm.
+  Make all tools use consistent lock ordering obtaining VG_ORPHAN lock second.
+  Check md devices for a partition table during device scan.
+  Add extended device (blkext) and md partition (mdp) types to filters.
+  Make text metadata read errors for segment areas more precise.
+  Fix text segment metadata read errors to mention correct segment name.
+  Include segment and LV names in text segment import error messages.
+  Add parent node to config_node structure.
+  Update vgsplit and vgcreate to call new vg_create and 'set' functions.
+  Change vg_create to take minimal parameters, obtain a lock, and return vg_t.
+  Refactor vgchange extent_size, max_lv, max_pv, and alloc_policy for liblvm.
+  Update t-vgcreate-usage.sh to test for default vg properties.
+  Fix memory leak in vgsplit when re-reading the vg.
+  Make various exit/cleanup paths more robust after lvm init failures.
+  Use LCK_NONBLOCK implicitly instead of explicit vg_read() flag.
+  Remove unnecessary locking and existence tests from new vg_read() interface.
+  Permit several segment types to be registered by a single shared object.
+  Update the man pages to document size units uniformly.
+  Allow commandline sizes to be specified in terms of bytes and sectors.
+  Update 'md_chunk_alignment' to use stripe-width to align PV data area.
+  Update test/t-inconsistent-metadata.sh to match new vg_read interface.
+  Add lvmcache_init() to polldaemon initialization.
+  Convert tools to use new vg_read / vg_read_for_update.
+  Fix segfault in vg_release when vg->cmd is NULL.
+
+Version 2.02.48 - 30th June 2009
+================================
+  Abort if automatic metadata correction fails when reading VG to update it.
+  Explicitly request fallback to default major number in device mapper.
+  Ignore suspended devices during repair.
+  Call vgreduce --removemissing automatically to fix missing PVs in dmeventd.
+  Suggest using lvchange --resync when adding leg to not-yet-synced mirror.
+  Destroy toolcontext on clvmd exit to avoid memory pool leaks.
+  Fix lvconvert not to poll mirror if no conversion in progress.
+  Fix memory leaks in toolcontext error path.
+  Reinstate partial activation support in clustered mode. (2.02.40)
+  Allow metadata correction even when PVs are missing.
+  Use 'lvm lvresize' instead of 'lvresize' in fsadm.
+  Do not use '-n' realine option in fsadm for busybox compatiblity.
+  Add vg_lock_newname() library function for vgrename, vgsplit and vgcreate.
+  Round up requested readahead to at least one page and print warning.
+  Try to repair vg before actual vgremove when force flag provided.
+  Fix possible double release of VG after recovery.
+  Add parameter to process_each_vg specifying what to do with inconsistent VG.
+  Unify error messages when processing inconsistent volume group.
+  Use lvconvert --repair instead of vgreduce in mirror dmeventd DSO.
+  Introduce lvconvert --use_policies (repair policy according to lvm.conf).
+  Update clvmd-corosync to match new corosync API.
+  Fix lib Makefile to include any shared libraries in default target.
+  Fix rename of active snapshot with virtual origin.
+  Fix convert polling to ignore LV with different UUID.
+  Cache underlying device readahead only before activation calls.
+  Fix segfault when calculating readahead on missing device in vgreduce.
+  Remove verbose 'visited' messages.
+  Handle multi-extent mirror log allocation when smallest PV has only 1 extent.
+  Add LSB standard headers and functions (incl. reload) to clvmd initscript.
+  When creating new LV, double-check that name is not already in use.
+  Remove /dev/vgname/lvname symlink automatically if LV is no longer visible.
+  Rename internal vorigin LV to match visible LV.
+  Suppress 'removed' messages displayed when internal LVs are removed.
+  Fix lvchange -a and -p for sparse LVs.
+  Fix lvcreate --virtualsize to activate the new device immediately.
+  Make --snapshot optional with lvcreate --virtualsize.
+  Generalise --virtualoriginsize to --virtualsize.
+  Skip virtual origins in process_each_lv_in_vg() without --all.
+  Fix counting of virtual origin LVs in vg_validate.
+  Attempt to load dm-zero module if zero target needed but not present.
+
+Version 2.02.47 - 22nd May 2009
+===============================
+  Rename liblvm.so to liblvm2app.so and use configure --enable-applib.
+  Reinstate version in liblvm2cmd.so soname. (2.02.44)
+
+Version 2.02.46 - 21st May 2009
+===============================
+  Inherit readahead setting from underlying devices during activation.
+  Detect LVs active on remote nodes by querying locks if supported.
+  Enable online resizing of mirrors.
+  Use suspend with flush when device size was changed during table preload.
+  Implement query_resource_fn for cluster_locking.
+  Support query_resource_fn in locking modules.
+  Introduce CLVMD_CMD_LOCK_QUERY command for clvmd.
+  Fix pvmove to revert operation if temporary mirror creation fails.
+  Fix metadata export for VG with missing PVs.
+  Add vgimportclone and install it and the man page by default.
+  Force max_lv restriction only for newly created LV.
+  Remove unneeded import parameter from lv_create_empty.
+  Merge lv_is_displayable and lv_is_visible functions.
+  Introduce lv_set_visible & lv_set_hidden functions.
+  Fix lv_is_visible to handle virtual origin.
+  Introduce link_lv_to_vg and unlink_lv_from_vg functions.
+  Remove lv_count from VG and use counter function instead.
+  Fix snapshot segment import to not use duplicate segments & replace.
+  Do not query nonexistent devices for readahead.
+  Remove NON_BLOCKING lock flag from tools and set a policy to auto-set.
+  Remove snapshot_count from VG and use function instead.
+  Fix first_seg() call for empty segment list.
+  Add install_lvm2 makefile target to install only the LVM2 components.
+  Reject missing PVs from allocation in toollib.
+  Fix PV datalignment for values starting prior to MDA area. (2.02.45)
+  Add sparse devices: lvcreate -s --virtualoriginsize (hidden zero origin).
+  Fix minimum width of devices column in reports.
+  Add lvs origin_size field.
+  Fix linux configure --enable-debug to exclude -O2.
+  Implement lvconvert --repair for repairing partially-failed mirrors.
+  Fix vgreduce --removemissing failure exit code.
+  Fix remote metadata backup for clvmd.
+  Introduce unlock_and_release_vg macro.
+  Introduce vg_release() to be called to free every struct volume_group.
+  Alloc PV internal structure from VG mempool if possible.
+  Fix metadata backup to run after vg_commit always.
+  Tidy clvmd volume lock cache functions.
+  Fix pvs report for orphan PVs when segment attributes are requested.
+  Fix pvs -a output to not read volume groups from non-PV devices.
+  Add MMC (mmcblk) device type to filters.
+  Introduce memory pools per volume group (to reduce memory for large VGs).
+  Use copy of PV structure when manipulating global PV lists.
+  Always return exit error status when locking of volume group fails.
+  Fix mirror log convert validation question.
+  Avoid referencing files from DESTDIR during build process.
+  Avoid creating some static libraries unless configured --enable-static_link.
+  Enable use of cached metadata for pvs and pvdisplay commands.
+  Add missing 'device-mapper' internal subdir build dependency.
+  Fix memory leak in mirror allocation code.
+  Save and restore the previous logging level when log level is changed.
+  Fix error message when archive initialization fails.
+  Make sure clvmd-corosync releases the lockspace when it exits.
+  Fix segfault for vgcfgrestore on VG with missing PVs.
+  Block SIGTERM & SIGINT in clvmd subthreads.
+  Detect and conditionally wipe swapspace signatures in pvcreate.
+  Fix maximal volume count check for snapshots if max_lv set for volume group.
+  Fix lvcreate to remove unused cow volume if the snapshot creation fails.
+  Fix error messages when PV uuid or pe_start reading fails.
+  Build new liblvm application-level library.
+  Rename liblvm.a to liblvm-internal.a.
+  Flush memory pool and fix locking in clvmd refresh and backup command.
+  Fix unlocks in clvmd-corosync. (2.02.45)
+  Fix error message when adding metadata directory to internal list fails.
+  Fix size and error message of memory allocation at backup initialization.
+  Remove old metadata backup file after renaming VG.
+  Restore log_suppress state when metadata backup file is up-to-date.
+
+Version 2.02.45 - 3rd March 2009
+================================
+  Avoid scanning empty metadata areas for VG names.
+  Attempt proper clean up in child before executing new binary in exec_cmd().
+  Do not scan devices if reporting only attributes from PV label.
+  Use pkgconfig to obtain corosync library details during configuration.
+  Fix error returns in clvmd-corosync interface to DLM.
+  Add --refresh to vgchange and vgmknodes man pages.
+  Pass --test from lvresize to fsadm as --dry-run.
+  Supply argv[] list to exec_cmd() to allow for variable number of parameters.
+  Prevent fsadm from checking mounted filesystems.
+  No longer treats any other key as 'no' when prompting in fsadm.
+  Tidy fsadm command line processing.
+  Add lib/lvm.h and lib/lvm_base.c for the new library interface.
+  Move tools/version.h to lib/misc/lvm-version.h.
+  Split LVM_VERSION into MAJOR, MINOR, PATCHLEVEL, RELEASE and RELEASE_DATE.
+  Add system_dir parameter to create_toolcontext().
+  Add --dataalignment to pvcreate to specify alignment of data area.
+  Exclude LCK_CACHE locks from _vg_lock_count, fixing interrupt unblocking.
+  Provide da and mda locations in debug message when writing text format label.
+  Mention the restriction on file descriptors at invocation on the lvm man page.
+  Index cached vgmetadata by vgid not vgname to cope with duplicate vgnames.
+  No longer require kernel and metadata major numbers to match.
+  Add a fully-functional get_cluster_name() to clvmd corosync interface.
+  Remove duplicate cpg_initialize from clvmd startup.
+  Add option to /etc/sysconfig/cluster to select cluster type for clvmd.
+  Allow clvmd to start up if its lockspace already exists.
+  Separate PV label attributes which do not need parse metadata when reporting.
+  Remove external dependency on the 'cut' command from fsadm.
+  Fix pvs segfault when pv mda attributes requested for not available PV.
+  Add fsadm support for reszing ext4 filesysystems.
+  Move locking_type reading inside init_locking().
+  Rename get_vgs() to get_vgnames() and clarify related error messages.
+  Allow clvmd to be built with all cluster managers & select one on cmdline.
+  Mention --with-clvmd=corosync in ./configure.
+  Replace internal vg_check_status() implementation.
+  Rename vg_read() to vg_read_internal().
+
+Version 2.02.44 - 26th January 2009
+===================================
+  Fix --enable-static_link after the recent repository changes.
+  Add corosync/DLM cluster interface to clvmd.
+  Add --nameprefixes, --unquoted, --rows to pvs, vgs, lvs man pages.
+  Fix lvresize size conversion for fsadm when block size is not 1K.
+  Fix pvs segfault when run with orphan PV and some VG fields.
+  Display a 'dev_size' of zero for missing devices in reports.
+  Add pv_mda_size to pvs and vg_mda_size to vgs.
+  Fix lvmdump /sys listing to include virtual devices directory.
+  Add "--refresh" functionality to vgchange and vgmknodes.
+  Avoid exceeding LV size when wiping device.
+  Calculate mirror log size instead of using 1 extent.
+  Ensure requested device number is available before activating with it.
+  Fix incorrect exit status from 'help <command>'.
+  Fix vgrename using UUID if there are VGs with identical names.
+  Fix segfault when invalid field given in reporting commands.
+  Move is_static from cmd to global is_static().
+  Refactor init_lvm() for lvmcmdline and clvmd.
+  Add liblvm interactive test infrastructure to build.
+  Add skeleton lvm2.h file in preparation for a shared library interface.
+  Use better random seed value in temp file creation.
+  Add read_urandom to read /dev/urandom. Use in uuid calculation.
+  Use displayable_lvs_in_vg and lv_is_displayable for consistency throughout.
+  Fix race in vgcreate that would result in second caller overwriting first.
+  Fix uninitialised lv_count in vgdisplay -c.
+  Don't skip updating pvid hash when lvmcache_info struct got swapped.
+  Add tinfo to termcap search path for pld-linux.
+  Fix startup race in clvmd.
+  Generate Red Hat clvmd startup script at config time with correct paths.
+  Fix clvmd & dmeventd builds after tree restructuring.
+  Cope with snapshot dependencies when removing a whole VG with lvremove.
+  Make man pages and tool help text consistent using | for alternative options.
+
+Version 2.02.43 - 10th November 2008
+====================================
+  Merge device-mapper into the lvm2 tree.
+  Correct prototype for --permission on lvchange and lvcreate man pages.
+  Exit with non-zero status from vgdisplay if couldn't show any requested VG.
+  Move list.c into libdevmapper and rename functions.
+  Rename a couple of variables that matched function names.
+  Use simplified x.y.z version number in libdevmapper.pc.
+  Remove ancient debian directory.
+  Split out lvm-logging.h from log.h and lvm-globals.[ch] from log.[ch].
+
+Version 2.02.42 - 26th October 2008
+===================================
+  Accept locking fallback_to_* options in the global section as documented.
+  Fix temp table activation in mirror conversions not to happen in other cmds.
+  Fix temp table in mirror conversions to use always-present error not zero.
+
+Version 2.02.41 - 17th October 2008
+===================================
+  Use temp table to set device size when converting mirrors.
+  In resume_mirror_images replace activate_lv with resume_lv as workaround.
+  Avoid overwriting in-use on-disk text metadata by forgetting MDA_HEADER_SIZE.
+  Fix snapshot monitoring library to not cancel monitoring invalid snapshot.
+  Generate man pages from templates and include version.
+  Add usrlibdir and usrsbindir to configure.
+  Fix conversion of md chunk size into sectors.
+  Free text metadata buffer after a failure writing it.
+  Fix misleading error message when there are no allocatable extents in VG.
+  Fix handling of PVs which reappeared with old metadata version.
+  Fix mirror DSO to call vgreduce with proper parameters.
+  Fix validation of --minor and --major in lvcreate to require -My always.
+  Fix release: clvmd build, vgreduce consolidate & tests, /dev/ioerror warning.
+
+Version 2.02.40 - 19th September 2008
+=====================================
+  Allow lvremove to remove LVs from VGs with missing PVs.
+  In VG with PVs missing, by default allow activation of LVs that are complete.
+  Track PARTIAL_LV and MISSING_PV flags internally.
+  Require --force with --removemissing in vgreduce to remove partial LVs.
+  No longer write out PARTIAL flag into metadata backups.
+  Treat new default activation/missing_stripe_filler "error" as an error target.
+  Remove internal partial_mode.
+  Add devices/md_chunk_alignment to lvm.conf.
+  Pass struct physical_volume to pe_align and adjust for md chunk size.
+  Store sysfs location in struct cmd_context.
+  Avoid shuffling remaining mirror images when removing one, retaining primary.
+  Add missing LV error target activation in _remove_mirror_images.
+  Prevent resizing an LV while lvconvert is using it.
+  Avoid repeatedly wiping cache while VG_GLOBAL is held in vgscan & pvscan.
+  Fix pvresize to not allow resize if PV has two metadata areas.
+  Fix setting of volume limit count if converting to lvm1 format.
+  Fix vgconvert logical volume id metadata validation.
+  Fix lvmdump metadata gather option (-m) to work correctly.
+  Fix allocation bug in text metadata format write error path.
+  Fix vgcfgbackup to properly check filename if template is used.
+  configure aborts if lcov or genhtml are missing with --enable-profiling
+  vgremove tries to remove lv snapshot first.
+  Added function lv_remove_with_dependencies().
+  Improve file descriptor leak detection to display likely culprit and filename.
+  Change clustered mirror kernel module name from cmirror to dm-log-clustered.
+  Avoid looping forever in _pv_analyze_mda_raw used by pvck.
+  Change lvchange exit status to indicate if any part of the operation failed.
+  Fix pvchange and pvremove to handle PVs without mdas.
+  Refactor _text_pv_read and always return mda list if requested.
+  Fix configure to work w/o readline unless --enable-readline used. (2.02.39)
+  Remove is_lvm_partition template which has not yet been coded.
+  Refactor pvcreate to separate parameter parsing from validation logic.
+  Check for label_write() failure in _text_pv_write().
+  Add pvcreate tests and update vgsplit tests to handle lvm1 and lvm2 metadata.
+  Fix pvchange -M1 -u to preserve existing extent locations when there's a VG.
+  Cease recognising snapshot-in-use percentages returned by early devt kernels.
+  Add backward-compatible flags field to on-disk format_text metadata.
+  Fix dmeventd monitoring libraries to link against liblvm2cmd again. (2.02.39)
+
+Version 2.02.39 - 27th June 2008
+================================
+  Enable readline by default if available.
+  Update autoconf to 2008-01-16.
+  Add $DISTCLEAN_DIRS to make.tmpl.in.
+  Create coverage reports with --enable-profiling and make lcov or lcov-dated.
+  Fix up cache for PVs without mdas after consistent VG metadata is processed.
+  Update validation of safe mirror log type conversions in lvconvert.
+  Fix lvconvert to disallow snapshot and mirror combinations.
+  Fix reporting of LV fields alongside unallocated PV segments.
+  Add --unquoted and --rows to reporting tools.
+  Add and use uninitialized_var() macro to suppress invalid compiler warnings.
+  Introduce enum for md minor sb version to suppress compiler warning.
+  Avoid undefined return value after _memlock manipulation in lvm2_run.
+  Avoid link failure if configured without --enable-cmdlib or --enable-readline.
+  Make clvmd return at once if other nodes down in a gulm or openais cluster.
+  Fix and improve readahead 'auto' calculation for stripe_size.
+  Fix lvchange output for -r auto setting if auto is already set.
+  Add test case for readahead.
+  Avoid ambiguous use of identifier error_message_produced.
+  Begin syncing configure.in for merge/unification with device-mapper.
+  Fix add_mirror_images not to dereference uninitialized log_lv upon failure.
+  Don't call openlog for every debug line output by clvmd.
+  Add --force to lvextend and lvresize.
+  Fix vgchange not to activate component mirror volumes directly.
+  Fix test directory clean up in make distclean.
+
+Version 2.02.38 - 11th June 2008
+================================
+  Fix tracking of validity of PVs with no mdas in lvmcache.
+  Fix return values for reporting commands when run with no PVs, LVs, or VGs.
+  Add omitted unlock_vg() call when sigint_caught() during vg processing.
+  Fix free_count when reading pool metadata.
+  Fix segfault when using pvcreate on a device containing pool metadata.
+  Fix segfault after _free_vginfo by remembering to remove vginfo from list.
+  Tweak detection of invalid fid after changes to PVs in VG in _vg_read.
+  Revert assuming precommitted metadata is live when activating (unnecessary).
+  Drop cached metadata for disappearing VG in vgmerge.
+  In script-processing mode, stop if any command fails.
+  Warn if command exits with non-zero status code without a prior log_error.
+  Check lv_count in vg_validate.
+  Add --nameprefixes to reporting tools for field name prefix output format.
+
+Version 2.02.37 - 6th June 2008
+===============================
+  Make clvmd-cman use a hash rather than an array for node updown info.
+  Correct config file line numbers in messages when parsing comments.
+  Drop cached metadata when renaming a VG.
+  Allow for vginfo changing during _vg_read.
+  Decode numbers in clvmd debugging output.
+  Add missing deactivation after activation failure in lvcreate -Zy.
+  When activating, if precommitted metadata is still cached, assume it's live.
+  When removing LV symlinks, skip any where the VG name is not determined.
+  Drop metadata cache if update fails in vg_revert or vg_commit.
+  Avoid spurious duplicate VG messages referring to VGs that are gone.
+  Drop dev_name_confirmed error message to debug level.
+  Fix setpriority error message to signed int.
+  Temporarily disable dmeventd mirror monitoring during lvchange --resync.
+  Refactor some vginfo manipulation code.
+  Add assertions to trap deprecated P_ and V_ lock usage.
+  Add missing mutex around clvmd lvmcache_drop_metadata library call.
+  Fix uninitialised mutex in clvmd if all daemons are not running at startup.
+  Avoid using DLM locks with LCK_CACHE type P_ lock requests.
+  When asked to drop cached committed VG metadata, invalidate cached PV labels.
+  Drop metadata cache before writing precommitted metadata instead of after.
+  Don't touch /dev in vgrename if activation is disabled.
+
+Version 2.02.36 - 29th April 2008
+=================================
+  Fix fsadm.sh to work with older blockdev, blkid & readlink binaries.
+  Fix lvresize to pass new size to fsadm when extending device.
+  Remove unused struct in clvmd-openais, and use correct node count.
+  Fix nodes list in clvmd-openais, and allow for broadcast messages.
+  Exclude VG_GLOBAL from internal concurrent VG lock counter.
+  Fix vgsplit internal counting of snapshot LVs.
+  Fix vgmerge snapshot_count when source VG contains snapshots.
+  Simplify clvmd-openais by using non-async saLckResourceLock.
+  Fix internal LV counter when a snapshot is removed.
+  Fix metadata corruption writing lvm1-formatted metadata with snapshots.
+  Fix lvconvert -m0 allocatable space check.
+
+Version 2.02.35 - 15th April 2008
+=================================
+  Drop cached VG metadata before and after committing changes to it.
+  Rename P_global to P_#global.
+  Don't attempt remote metadata backups of non-clustered VGs. (2.02.29)
+  Don't store fid in VG metadata cache to avoid clvmd segfault. (2.02.34)
+  Update vgsplit tests to verify loosening of active LV restriction.
+  Update vgsplit to only restrict split with active LVs involved in split.
+  Add lv_is_active() to determine whether an lv is active.
+
+Version 2.02.34 - 10th April 2008
+=================================
+  Improve preferred_names lvm.conf example.
+  Fix vgdisplay 'Cur LV' field to match lvdisplay output.
+  Fix lv_count report field to exclude hidden LVs.
+  Add vg_is_clustered() helper function.
+  Fix vgsplit to only move hidden 'snapshotN' LVs when necessary.
+  Update vgsplit tests for lvnames on the cmdline.
+  Update vgsplit man page to reflect lvnames on the cmdline.
+  Update vgsplit to take "-n LogicalVolumeName" on the cmdline.
+  Use clustered mirror log with pvmove in clustered VGs, if available.
+  Fix some pvmove error status codes.
+  Fix vgsplit error paths to release vg_to lock.
+  Indicate whether or not VG is clustered in vgcreate log message.
+  Mention default --clustered setting in vgcreate man page.
+  Add config file overrides to clvmd when it reads the active LVs list.
+  Fix vgreduce to use vg_split_mdas to check sufficient mdas remain.
+  Add (empty) orphan VGs to lvmcache during initialisation.
+  Fix orphan VG name used for format_pool.
+  Create a fid for internal orphan VGs.
+  Update lvmcache VG lock state for all locking types now.
+  Fix output if overriding command_names on cmdline.
+  Add detection of clustered mirror log capability.
+  Add check to vg_commit() ensuring VG lock held before writing new VG metadata.
+  Add validation of LV name to pvmove -n.
+  Make clvmd refresh the context correctly when lvm.conf is updated.
+  Add some basic internal VG lock validation.
+  Add per-command flags to control which commands use the VG metadata cache.
+  Fix vgsplit locking of new VG (2.02.30).
+  Avoid erroneous vgsplit error message for new VG. (2.02.29)
+  Suppress duplicate message when lvresize fails because of invalid vgname.
+  Cache VG metadata internally while VG lock is held.
+  Fix redundant lvresize message if vg doesn't exist.
+  Fix another allocation bug with clvmd and large node IDs.
+  Add find_lv_in_lv_list() and find_pv_in_pv_list().
+  Fix uninitialised variable in clvmd that could cause odd hangs.
+  Add vgmerge tests.
+  Add pvseg_is_allocated() for identifying a PV segment allocated to a LV.
+  Add list_move() for moving elements from one list to another.
+  Add 'is_reserved_lvname()' for identifying hidden LVs.
+  Correct command name in lvmdiskscan man page.
+  clvmd no longer crashes if it sees nodeids over 50.
+  Fix potential deadlock in clvmd thread handling.
+  Refactor text format initialisation into _init_text_import.
+  Escape double quotes and backslashes in external metadata and config data.
+  Add functions for escaping double quotes in strings.
+  Rename count_chars_len to count_chars.
+  Use return_0 in a couple more places.
+  Correct a function name typo in _line_append error message.
+  Include limits.h in clvmd so it compiles with newer headers.
+  Add VirtIO disks (virtblk) to filters.
+  Fix resetting of MIRROR_IMAGE and VISIBLE_LV after removal of LV. (2.02.30)
+  Fix remove_layer_from_lv to empty the LV before removing it. (2.02.30)
+  Add missing no-longer-used segs_using_this_lv test to check_lv_segments.
+  Remove redundant non-NULL tests before calling free in clvmd.c.
+  Avoid a compiler warning: make is_orphan's parameter const.
+  Fix lvconvert detection of mirror conversion in progress. (2.02.30)
+  Avoid automatic lvconvert polldaemon invocation when -R specified. (2.02.30)
+  Fix 'pvs -a' to detect VGs of PVs without metadata areas.
+  Divide up internal orphan volume group by format type.
+  Update usage message for clvmd.
+  Fix clvmd man page not to print <br> and clarified debug options.
+  Fix lvresize to support /dev/mapper prefix in the LV name.
+  Fix unfilled parameter passed to fsadm from lvresize.
+  Update fsadm to call lvresize if the partition size differs (with option -l).
+  Fix fsadm to support VG/LV names.
+
+Version 2.02.33 - 31st January 2008
+===================================
+  Fix mirror log name construction during lvconvert. (2.02.30)
+  Make monitor_dev_for_events recurse through the stack of LVs.
+  Clean up some more compiler warnings.
+  Some whitespace tidy-ups.
+  Use stack return macros throughout.
+  Rely upon internally-cached PV labels while corresponding VG lock is held.
+
+Version 2.02.32 - 29th January 2008
+===================================
+  Fix two check_lv_segments error messages to show whole segment.
+  Refactor mirror log attachment code.
+  Fix internal metadata corruption in lvchange --resync. (2.02.30)
+  Fix new parameter validation in vgsplit and test mode. (2.02.30)
+  Remove redundant cnxman-socket.h file from clvmd directory.
+  Fix pvs, vgs, lvs error exit status on some error paths.
+
+Version 2.02.31 - 19th January 2008
+===================================
+  Fix lvcreate --nosync not to wait for non-happening sync. (2.02.30)
+  Add very_verbose lvconvert messages.
+  Avoid readahead error message with default setting of lvcreate -M1. (2.02.29)
+
+Version 2.02.30 - 17th January 2008
+===================================
+  Set default readahead to twice maximium stripe size.
+  Reinstate VG extent size and stripe size defaults (halved). (2.02.29)
+  Add lists of stacked LV segments using each LV to the internal metadata.
+  Change vgsplit -l (for unimplemented --list) into --maxlogicalvolumes.
+  Fix process_all_pvs to detect non-orphans with no MDAs correctly.
+  Don't use block_on_error with mirror targets version 1.12 and above.
+  Update vgsplit to accept vgcreate options when new VG is destination.
+  Update vgsplit to accept existing VG as destination.
+  lvconvert waits for completion of initial sync by default.
+  Refactor vgcreate for parameter validation and add tests.
+  Add new convert_lv field to lvs output.
+  Print warning when lvm tools are running as non-root.
+  Add snapshot dmeventd library (enables dmeventd snapshot monitoring).
+  Prevent pvcreate from overwriting MDA-less PVs belonging to active VGs.
+  Fix a segfault if using pvs with --all argument. (2.02.29)
+  Update --uuid argument description in man pages.
+  Fix vgreduce PV list processing not to process every PV in the VG. (2.02.29)
+  Extend lvconvert to use polldaemon.
+  Add support for stacked mirrors.
+  Major restructuring of pvmove and lvconvert layer manipulation code.
+  Replace tools/fsadm with scripts/fsadm.sh.
+  Append fields to report/pvsegs_cols_verbose.
+  Permit LV segment fields with PV segment reports.
+  Add seg_start_pe and seg_pe_ranges to reports.
+
+Version 2.02.29 - 5th December 2007
+===================================
+  Make clvmd backup vg metadata on remote nodes.
+  Refactor pvmove allocation code.
+  Decode cluster locking state in log message.
+  Change file locking state messages from debug to very verbose.
+  Fix --addtag to drop @ prefix from name.
+  Stop clvmd going haywire if a pre_function fails.
+  Convert some vg_reads into vg_lock_and_reads.
+  Avoid nested vg_reads when processing PVs in VGs and fix associated locking.
+  Accept sizes with --readahead argument.
+  Store size arguments as sectors internally.
+  Attempt to remove incomplete LVs with lvcreate zeroing/activation problems.
+  Add read_ahead activation code.
+  Add activation/readahead configuration option and FMT_RESTRICTED_READAHEAD.
+  Extend readahead arg to accept "auto" and "none".
+  Add lv_read_ahead and lv_kernel_read_ahead fields to reports and lvdisplay.
+  Prevent lvconvert -s from using same LV as origin and snapshot.
+  Fix human-readable output of odd numbers of sectors.
+  Add pv_mda_free and vg_mda_free fields to reports for raw text format.
+  Add LVM2 version to 'Generated by' comment in metadata.
+  Show 'not usable' space when PV is too large for device in pvdisplay.
+  Ignore and fix up any excessive device size found in metadata.
+  Fix error message when fixing up PV size in lvm2 metadata (2.02.11).
+  Fix orphan-related locking in pvdisplay and pvs.
+  Fix missing VG unlocks in some pvchange error paths.
+  Add some missing validation of VG names.
+  Rename validate_vg_name() to validate_new_vg_name().
+  Change orphan lock to VG_ORPHANS.
+  Change format1 to use ORPHAN as orphan VG name.
+  Convert pvchange, pvdisplay, pvscan to use is_orphan()
+  Add is_orphan_vg() and change all hard-coded checks to use it.
+  Detect md superblocks version 1.0, 1.1 and 1.2.
+  Add _alloc_pv() and _free_pv() from _pv_create() code and fix error paths.
+  Add pv_dev_name() to access PV device name.
+  Add const attributes to pv accessor functions.
+  Refactor vg_add_snapshot() and lv_create_empty().
+  Handle new sysfs subsystem/block/devices directory structure.
+  Run test with LVM_SYSTEM_DIR pointing to private root and /dev dirs.
+  Fix a bug in lvm_dump.sh checks for lvm/dmsetup binaries.
+  Fix underquotations in lvm_dump.sh.
+  Refactor lvcreate stripe and mirror parameter validation.
+  Print --help output to stdout, not stderr.
+  After a cmdline processing error, don't print help text but suggest --help.
+  Add %PVS extents option to lvresize, lvextend, and lvcreate.
+  Add 'make check' to run tests in new subdirectory 'test'.
+  Moved the obsolete test subdirectory to old-tests.
+  Cope with relative paths in configure --with-dmdir.
+  Remove no-longer-correct restrictions on PV arg count with stripes/mirrors.
+  Fix strdup memory leak in str_list_dup().
+  Link with -lpthread when static SELinux libraries require that.
+  Detect command line PE values that exceed their 32-bit range.
+  Include strerror string in dev_open_flags' stat failure message.
+  Move guts of pvresize into library.
+  Avoid error when --corelog is provided without --mirrorlog. (2.02.28)
+  Correct --mirrorlog argument name in man pages (not --log).
+  Clear MIRROR_NOTSYNCED LV flag when converting from mirror to linear.
+  Modify lvremove to prompt for removal if LV active on other cluster nodes.
+  Add '-f' to vgremove to force removal of VG even if LVs exist.
+
+Version 2.02.28 - 24th August 2007
+==================================
+  Fix clvmd logging so you can get lvm-level debugging out of it.
+  Introduce VG_GLOBAL lock type for vgscan/pvscan to trigger clvmd -R.
+  Change locking_flags from int to uint32_t.
+  Fix clvmd -R, so it fully refreshes the caches.
+  Change lvconvert_mirrors to use mirror segtype not striped.
+  Fix lvconvert_mirrors detection of number of existing mirrors.
+  Clean up numerous compiler warnings that appeared in recent releases.
+  Remove several unused parameters from _allocate().
+  Only permit --force, --verbose and --debug arguments to be repeated.
+  Fix inconsistent licence notices: executables are GPLv2; libraries LGPLv2.1.
+  Move guts of vgremove and lvremove into library, including yes_no_prompt.
+  Allow clvmd debug to be turned on in a running daemon using clvmd -d [-C].
+  Update to use autoconf 2.61, while still supporting 2.57.
+  Add more cluster info to lvmdump.
+  Add further const attributes throughout.
+  Add support for renaming mirrored LVs.
+  Factor out core of lvrename() to library function.
+  Add --mirrorlog argument to specify log type for mirrors.
+  Don't attempt to monitor devices if their creation failed in _lv_activate.
+  Don't leak a file descriptor in fcntl_lock_file() when fcntl fails.
+  Replace create_dir with dm_create_dir.
+  Detect stream write failure reliably with lvm_fclose using dm_fclose.
+  Fix clvmd if compiled with gulm support. (2.02.26)
+  Fix lvdisplay man page to say LV size is reported in sectors, not KB.
+  Add vg_lock_and_read() external library function.
+  Fix loading of persistent cache if cache_dir is used. (2.02.23)
+  Reduce _compare_paths lstat error message from log_error to log_very_verbose.
+  Create util.h with last_path_component replacing strdup + basename.
+  Use gcc's printf attribute wherever possible.
+  In _line_append, use "sizeof buf - 1" rather than equivalent "4095".
+  Introduce is_same_inode macro, now including a comparison of st_dev.
+  Don't leak a file descriptor in _lock_file() when flock fails.
+  Add SUN's LDOM virtual block device (vdisk) and ps3disk to filters.
+  Split metadata-external.h out from metadata.h for the tools to use.
+
+Version 2.02.27 - 17th July 2007
+================================
+  Fix snapshot cow area deactivation if origin is not active. (2.02.13)
+  Fix configure libdevmapper.h check when --with-dmdir is used.
+  Turn _add_pv_to_vg() into external library function add_pv_to_vg().
+  Add pv_by_path() external library function.
+  Tidy clvmd-openais of redundant bits, and improve an error report.
+  Cope with find_seg_by_le() failure in check_lv_segments().
+  Call dev_iter_destroy() if _process_all_devs() is interrupted by sigint.
+  Add vg_mda_count and pv_mda_count columns to reports.
+  Fix dumpconfig to use log_print instead of stdout directly.
+  Remove unused parameter 'fid' from _add_pv_to_vg.
+  Add kernel and device-mapper targets versions to lvmdump.
+  Replace BSD (r)index with C89 str(r)chr.
+  Handle vgsplit of an entire VG as a vgrename.
+  Reinitialise internal lvmdiskscan variables when called repeatedly.
+  Fix missing lvm_shell symbol in lvm2cmd library. (2.02.23)
+  Add vg_status function and clean up vg->status in tools directory.
+  Add --ignoremonitoring to disable all dmeventd interaction.
+  Remove get_ prefix from get_pv_* functions.
+  clvmd-openais now uses cpg_local_get() to get nodeid, rather than Clm.
+  Print warnings to stderr instead of stdout.
+
+Version 2.02.26 - 15th June 2007
+================================
+  Update vgcfgrestore man page.
+  Allow keyboard interrupt during user prompts when appropriate.
+  Remove unused clvmd system-lv code.
+  Replace many physical_volume struct dereferences with new get_pv_* functions.
+  Suppress a benign compile-time warning.
+  Convert find_pv_in_vg_by_uuid and pv_create to use PV handles.
+  Add wrappers to some functions in preparation for external LVM library.
+  Add -f to vgcfgrestore to list metadata backup files.
+  Add vg_check_status to consolidate vg status checks and error messages.
+  Add pvdisplay --maps implementation.
+  Remove unsupported LVM1 options from vgcfgrestore man page.
+  Update vgcfgrestore man page to show mandatory VG name.
+  Update vgrename man page to include UUID and be consistent with lvrename.
+  Add (experimental) OpenAIS support to clvmd.
+  Fix deactivation code to follow dependencies and remove symlinks.
+  Fix and clarify vgsplit error messages.
+  Fix a segfault in device_is_usable() if a device has no table.
+  Add some more debug messages to clvmd startup.
+  Misc clvmd cleanups.
+
+Version 2.02.25 - 27th April 2007
+=================================
+  Fix get_config_uint64() to read a 64-bit value not a 32-bit one.
+  Add -Wformat-security and change one fprintf() to fputs().
+  Move regex functions into libdevmapper.
+  Change some #include lines to search only standard system directories.
+  Add devices/preferred_names config regex list for displayed device names.
+  Free a temporary dir string in fcntl_lock_file() after use.
+  Fix a dm_pool_destroy() in matcher_create().
+  Introduce goto_bad macro.
+  Fix warnings on x86_64 involving ptrdiff_t in log_error messages.
+  Update pvck to include text metadata area and record detection.
+  Add support functions for token counting in config file extracts.
+  Update pvck to read labels on disk, with --labelsector parameter.
+  Add count_chars and count_chars_len functions.
+  Add /sys/block listings to lvm_dump.sh.
+  Make lvm_dump.sh list /dev recursively.
+  Fix thread race in clvmd.
+  Add scan_sector param to label_read and _find_labeller.
+  Make clvmd cope with quorum devices.
+  Add extra internal error checking to clvmd.
+  Add dev_read_circular.
+  Add pvck command stub.
+  Update lists of attribute characters in man pages.
+  Change cling alloc policy attribute character from 'C' to l'.
+  Fix creation and conversion of mirrors with tags.
+  Fix vgsplit for lvm1 format (set and validate VG name in PVs metadata).
+  Split metadata areas in vgsplit properly.
+
+Version 2.02.24 - 19th March 2007
+=================================
+  Fix processing of exit status in init scripts
+  Fix vgremove to require at least one vg argument.
+  Fix reading of striped LVs in LVM1 format.
+  Flag nolocking as clustered so clvmd startup sees clustered LVs. (2.02.10)
+  Add a few missing pieces of vgname command line validation.
+  Support the /dev/mapper prefix on most command lines.
+
+Version 2.02.23 - 8th March 2007
+================================
+  Fix vgrename active LV check to ignore differing vgids.
+  Remove no-longer-used uuid_out parameter from activation info functions.
+  Fix two more segfaults if an empty config file section encountered.
+  Move .cache file into a new /etc/lvm/cache directory by default.
+  Add devices/cache_dir & devices/cache_file_prefix, deprecating devices/cache.
+  Create directory in fcntl_lock_file() if required.
+  Exclude readline support from lvm.static.
+  Fix a leak in a reporting error path (2.02.19).
+
+Version 2.02.22 - 13th February 2007
+====================================
+  Correct -b and -P on a couple of man pages.
+  Add global/units to example.conf.
+  Fix loading of segment_libraries.
+  If a PV reappears after it was removed from its VG, make it an orphan.
+  Don't update metadata automatically if VGIDs don't match.
+  Fix some vgreduce --removemissing command line validation.
+
+Version 2.02.21 - 30th January 2007
+===================================
+  Add warning to lvm2_monitoring_init_rhel4 if attempting to stop monitoring.
+  Fix vgsplit to handle mirrors.
+  Reorder fields in reporting field definitions.
+  Fix vgs to treat args as VGs even when PV fields are displayed.
+  Fix md signature check to handle both endiannesses.
+
+Version 2.02.20 - 25th January 2007
+===================================
+  dmeventd mirror sets ignore_suspended_devices and avoids scanning mirrors.
+  Add devices/ignore_suspended_devices to ignore suspended dm devices.
+  Add some missing close() and fclose() return code checks.
+  Fix exit statuses of reporting tools (2.02.19).
+  Add init script for dmeventd monitoring.
+  lvm.static no longer interacts with dmeventd unless explicitly asked to.
+  Add field definitions to report help text.
+  Remove unnecessary cmd arg from target_*monitor_events().
+  Add private variable to dmeventd shared library interface.
+  Long-lived processes write out persistent dev cache in refresh_toolcontext().
+  Fix refresh_toolcontext() always to wipe persistent device filter cache.
+  Add is_long_lived to toolcontext.
+  Add --clustered to man pages.
+  Streamline dm_report_field_* interface.
+  Change remaining dmeventd terminology 'register' to 'monitor'.
+  Update reporting man pages.
+  No longer necessary to specify alignment type for report fields.
+
+Version 2.02.19 - 17th January 2007
+===================================
+  Fix a segfault if an empty config file section encountered.
+  Move basic reporting functions into libdevmapper.
+  Fix partition table processing after sparc changes (2.02.16).
+  Fix cmdline PE range processing segfault (2.02.13).
+  Some libdevmapper-event interface changes.
+  Report dmeventd mirror monitoring status.
+  Fix dmeventd mirror status line processing.
+
+Version 2.02.18 - 11th January 2007
+===================================
+  Revised libdevmapper-event interface for dmeventd.
+  Remove dmeventd mirror status line word limit.
+  Use CFLAGS when linking so mixed sparc builds can supply -m64.
+  Prevent permission changes on active mirrors.
+  Print warning instead of error message if lvconvert cannot zero volume.
+  Add snapshot options to lvconvert man page.
+  dumpconfig accepts a list of configuration variables to display.
+  Change dumpconfig to use --file to redirect output to a file.
+  Avoid vgreduce error when mirror code removes the log LV.
+  Remove 3 redundant AC_MSG_RESULTs from configure.in.
+  Free memory in _raw_read_mda_header() error paths.
+  Fix ambiguous vgsplit error message for split LV.
+  Fix lvextend man page typo.
+  Add configure --with-dmdir to compile against a device-mapper source tree.
+  Use no flush suspending for mirrors.
+  Add dmeventd_mirror register_mutex, tidy initialisation & add memlock.
+  Fix create mirror with name longer than 22 chars.
+  Fix some activate.c prototypes when compiled without devmapper.
+  Fix dmeventd mirror to cope if monitored device disappears.
+
+Version 2.02.17 - 14th December 2006
+====================================
+  Add missing pvremove error message when device doesn't exist.
+  When lvconvert allocates a mirror log, respect parallel area constraints.
+  Use loop to iterate through the now-ordered policy list in _allocate().
+  Check for failure to allocate just the mirror log.
+  Introduce calc_area_multiple().
+  Support mirror log allocation when there is only one PV: area_count now 0.
+  Fix detection of smallest area in _alloc_parallel_area() for cling policy.
+  Add manpage entry for clvmd -T
+  Fix gulm operation of clvmd, including a hang when doing lvchange -aey
+  Fix hang in clvmd if a pre-command failed.
+
+Version 2.02.16 - 1st December 2006
+===================================
+  Fix VG clustered read locks to use PR not CR.
+  Adjust some alignments for ia64/sparc.
+  Fix mirror segment removal to use temporary error segment.
+  Always compile debug logging into clvmd.
+  Add startup timeout to RHEL4 clvmd startup script.
+  Add -T (startup timeout) switch to clvmd.
+  Improve lvm_dump.sh robustness.
+  Update lvm2create_initrd to support gentoo.
+
+Version 2.02.15 - 21st November 2006
+====================================
+  Fix clvmd_init_rhel4 line truncation (2.02.14).
+  Install lvmdump by default.
+  Fix check for snapshot module when activating snapshot.
+  Fix pvremove error path for case when PV is in use.
+  Warn if certain duplicate config file entries are seen.
+  Enhance lvm_dump.sh for sysreport integration and add man page.
+  Fix --autobackup argument which could never disable backups.
+  Fix a label_verify error path.
+
+Version 2.02.14 - 10th November 2006
+====================================
+  Fix adjusted_mirror_region_size() to handle 64-bit size.
+  Add some missing bounds checks on 32-bit extent counters.
+  Add Petabyte and Exabyte support.
+  Fix lvcreate error message when 0 extents requested.
+  lvremove man page: volumes must be cluster inactive before being removed.
+  Protect .cache manipulations with fcntl locking.
+  Change .cache timestamp comparisons to use ctime.
+  Fix mirror log LV writing to set all bits in whole LV.
+  Fix clustered VG detection and default runlevels in clvmd_init_rhel4.
+  Fix high-level free space check for partial allocations.
+
+Version 2.02.13 - 27th October 2006
+===================================
+  Add couple of missing files to tools/Makefile CLEAN_TARGETS.
+  When adding snapshot leave cow LV mapped device active after zeroing.
+  Fix a clvmd debug message.
+  Add dev_flush() to set_lv().
+  Add lvchange --resync.
+  Perform high-level free space check before each allocation attempt.
+  Don't allow a node to remove an LV that's exclusively active on anther node.
+  Cope if same PV is included more than once in cmdline PE range list.
+  Set PV size to current device size if it is found to be zero.
+  Add segment parameter to target_present functions.
+
+Version 2.02.12 - 16th October 2006
+===================================
+  Fix pvdisplay to use vg_read() for non-orphans.
+  Fall back to internal locking if external locking lib is missing or fails.
+  Retain activation state after changing LV minor number with --force.
+  Propagate clustered flag in vgsplit and require resizeable flag.
+
+Version 2.02.11 - 12th October 2006
+===================================
+  Add clvmd function to return the cluster name. not used by LVM yet.
+  Add cling allocation policy.
+  Change _check_contiguous() to use _for_each_pv().
+  Extend _for_each_pv() to allow termination without error.
+  Abstract _is_contiguous().
+  Remove duplicated pv arg from _check_contiguous().
+  Accept regionsize with lvconvert.
+  Add report columns with underscore before field names ending 'size'.
+  Correct regionsize default on lvcreate man page (MB).
+  Fix clvmd bug that could cause it to die when a node with a long name crashed.
+  Add device size to text metadata.
+  Fix format_text mda_setup pv->size and pv_setup pe_count calculations.
+  Fix _for_each_pv() for mirror with core log.
+  Add lvm_dump.sh script to create a tarball of debugging info from a system.
+  Capture error messages in clvmd and pass them back to the user.
+  Remove unused #defines from filter-md.c.
+  Make clvmd restart init script wait until clvmd has died before starting it.
+  Add -R to clvmd which tells running clvmds to reload their device cache.
+  Add LV column to reports listing kernel modules needed for activation.
+  Show available fields if report given invalid field. (e.g. lvs -o list)
+  Add timestamp functions with --disable-realtime configure option.
+  Add %VG, %LV and %FREE suffices to lvcreate/lvresize --extents arg.
+  Fix two potential NULL pointer derefs in error cases in vg_read().
+  Separate --enable-cluster from locking lib options in lvmconf.sh.
+  Add a missing comma in lvcreate man page.
+
+Version 2.02.10 - 19th September 2006
+=====================================
+  Fix lvconvert mirror change case detection logic.
+  Fix mirror log detachment so it correctly becomes a standalone LV.
+  Extend _check_contiguous() to detect single-area LVs.
+  Include mirror log (untested) in _for_each_pv() processing.
+  Use MIRROR_LOG_SIZE constant.
+  Remove struct seg_pvs from _for_each_pv() to generalise.
+  Avoid adding duplicates to list of parallel PVs to avoid.
+  Fix several incorrect comparisons in parallel area avoidance code.
+  Fix segment lengths when flattening existing parallel areas.
+  Log existing parallel areas prior to allocation.
+  Fix mirror log creation when activation disabled.
+  Don't attempt automatic recovery without proper locking.
+  When using local file locking, skip clustered VGs.
+  Add fallback_to_clustered_locking and fallback_to_local_locking parameters.
+  lvm.static uses built-in cluster locking instead of external locking.
+  Don't attempt to load shared libraries if built statically.
+  Change default locking_lib to liblvm2clusterlock.so.
+  Add skip_dev_dir() to process command line VGs.
+  Stop clvmd complaining about nodes that have left the cluster.
+  Move lvm_snprintf(), split_words() and split_dm_name() into libdevmapper.
+  Add lvconvert man page.
+  Add mirror options to man pages.
+  Prevent mirror renames.
+  Move CMDLIB code into separate file and record whether static build.
+
+Version 2.02.09 - 17th August 2006
+==================================
+  Fix PE_ALIGN for pagesize over 32KB.
+  Separate out LVM1_PE_ALIGN and pe_align().
+  Add lvm_getpagesize wrapper.
+  Add --maxphysicalvolumes to vgchange.
+
+Version 2.02.08 - 15th August 2006
+==================================
+  Add checks for duplicate LV name, lvid and PV id before writing metadata.
+  Report all sanity check failures, not just the first.
+  Fix missing lockfs on first snapshot creation.
+  Add unreliable --trustcache option to reporting commands.
+  Fix locking for mimage removal.
+  Fix clvmd_init_rhel4 'status' exit code.
+
+Version 2.02.07 - 17th July 2006
+================================
+  Fix activation logic in lvchange --persistent.
+  Don't ignore persistent minor numbers when activating.
+  Use RTLD_GLOBAL when loading shared libraries.
+  Add some forgotten memlock checks to _vg_read to protect against full scans.
+  Add mutex to dmeventd_mirror to avoid concurrent execution.
+  Fix vgreduce --removemissing to return success if VG is already consistent.
+  Fix return code if VG specified on command line is not found.
+  Fix PV tools to include orphaned PVs in default output again.
+  Fixed unaligned access when using clvm.
+  Fix an extra dev_close in a label_read error path.
+  Append patches to commit emails.
+  Fix target_register_events args.
+  Prevent snapshots of mirrors.
+  Add DISTCLEAN_TARGETS to make template for configure.h.
+  More fixes to error paths.
+  Fix lvcreate corelog validation.
+  Add --config for overriding most config file settings from cmdline.
+  Quote arguments when printing command line.
+  Remove linefeed from 'initialising logging' message.
+  Add 'Completed' debug message.
+  Don't attempt library exit after reloading config files.
+  Always compile with libdevmapper, even if device-mapper is disabled.
+
+Version 2.02.06 - 12th May 2006
+===============================
+  Propagate --monitor around cluster.
+  Add --monitor to vgcreate and lvcreate to control dmeventd registration.
+  Filter LCK_NONBLOCK in clvmd lock_vg.
+  Add --nosync to lvcreate with LV flag NOTSYNCED.
+  Use mirror's uuid for a core log.
+  Add mirror log fault-handling policy.
+  Improve mirror warning messages and tidy dmeventd syslog output.
+  Propagate nosync flag around cluster.
+  Allow vgreduce to handle mirror log failures.
+  Add --corelog to lvcreate and lvconvert.
+  Create a log header for replacement in-sync mirror log.
+  Use set_lv() and dev_set() to wipe sections of devices.
+  Add mirror_in_sync() flag to avoid unnecessary resync on activation.
+  Add mirror_library description to example.conf.
+  Fix uuid_from_num() buffer overrun.
+  Make SIZE_SHORT the default for display_size().
+  Fix some memory leaks in error paths found by coverity.
+  Use C99 struct initialisers.
+  Move DEFS into configure.h.
+  Clean-ups to remove miscellaneous compiler warnings.
+  Improve stripe size validation.
+  Increase maximum stripe size limit to physical extent size for lvm2 metadata.
+  Fix activation code to check for pre-existing mirror logs.
+  Tighten region size validation.
+  Ignore empty strings in config files.
+  Require non-zero regionsize and document parameter on lvcreate man page.
+  Invalidate cache if composition of VG changed externally.
+
+Version 2.02.05 - 21st April 2006
+=================================
+  Fix vgid string termination in recent cache code.
+
+Version 2.02.04 - 19th April 2006
+=================================
+  Check for libsepol.
+  Add some cflow & scope support.
+  Separate out DEFS from CFLAGS.
+  Remove inlines and use unique function names.
+
+Version 2.02.03 - 14th April 2006
+=================================
+  vgrename accepts vgid and exported VG.
+  Add --partial to pvs.
+  When choosing between identically-named VGs, also consider creation_host.
+  Provide total log suppression with 2.
+  Fix vgexport/vgimport to set/reset PV exported flag so pv_attr is correct.
+  Add vgid to struct physical_volume and pass with vg_name to some functions.
+  If two or more VGs are found with the same name, use one that is not exported.
+  Whenever vgname is captured, also capture vgid and whether exported.
+  Remove an incorrect unlock_vg() from process_each_lv().
+  Update extent size information in vgchange and vgcreate man pages.
+  Introduce origin_from_cow() and lv_is_visible().
+  pvremove without -f now fails if there's no PV label.
+  Support lvconvert -s.
+  Suppress locking library load failure message if --ignorelockingfailure.
+  Propagate partial mode around cluster.
+  Fix archive file expiration.
+  Fix dmeventd build.
+  clvmd now uses libcman rather than cman ioctls.
+  clvmd will allow new cman to shutdown on request.
+
+Version 2.02.02 - 7th February 2006
+===================================
+  Add %.so: %.a make template rule.
+  Switchover library building to use LIB_SUFFIX.
+  Only do lockfs filesystem sync when suspending snapshots.
+  Always print warning if activation is disabled.
+  vgreduce removes mirror images.
+  Add --mirrorsonly to vgreduce.
+  vgreduce replaces active LVs with error segment before removing them.
+  Set block_on_error parameter if available.
+  Add target_version.
+  Add details to format1 'Invalid LV in extent map' error message.
+  Fix lvscan snapshot full display.
+  Bring lvdisplay man page example into line.
+  Add mirror dmeventd library.
+  Add some activation logic to remove_mirror_images().
+  lvconvert can remove specified PVs from a mirror.
+  lvconvert turns an existing LV into a mirror.
+  Allow signed mirrors arguments.
+  Move create_mirror_log() into toollib.
+  Determine parallel PVs to avoid with ALLOC_NORMAL allocation.
+  Fix lv_empty.
+
+Version 2.02.01 - 23rd November 2005
+====================================
+  Fix lvdisplay cmdline to accept snapshots.
+  Fix open RO->RW promotion.
+  Fix missing vg_revert in lvcreate error path.
+
+Version 2.02.00 - 10th November 2005
+====================================
+  Extend allocation areas to avoid overflow with contiguous with other PVs.
+  Stop lvcreate attempting to wipe zero or error segments.
+  Added new lvs table attributes.
+  Separated out activation preload.
+  Moved activation functions into libdevmapper.
+  Fixed build_dm_name.
+  Add return macros.
+  Added xen xvd devices.
+  Clear up precommitted metadata better.
+  A pvresize implementation.
+  Fix contiguous allocation when there are no preceding segments.
+  Add mirror_seg pointer to lv_segment struct.
+  Only keep a device open if it's known to belong to a locked VG.
+  Fix lvdisplay to show all mirror destinations.
+  Replacement suspend code using libdevmapper dependency tree.
+  Add DEFS to make.tmpl.
+  Use dm_is_dm_major instead of local copy.
+  Allow mapped devices to be used as PVs.
+  Move set_selinux_context into libdevmapper.
+  Fix automatic text metadata buffer expansion (using macro).
+  Cache formatted text metadata buffer between metadata area writes.
+  Add pe_start field to pvs.
+  Add 'LVM-' prefix to uuids.
+  Split lv_segment_area from lv_segment to permit extension.
+  Replacement deactivation code using libdevmapper dependency tree.
+  Simplify dev_manager_info().
+  Attempt to load missing targets using modprobe.
+  Add -a to lvscan.
+  Move mknodes into libdevmapper.
+  Move bitset, hash, pool and dbg_malloc into libdevmapper.
+
+Version 2.01.15 - 16th October 2005
+===================================
+  Refuse to run pvcreate/pvremove on devices we can't open exclusively.
+  Use ORPHAN lock definition throughout.
+  Validate chunksize in lvcreate.
+  Reduce chunksize limit to 512k.
+  Fix chunksize field in reports.
+  Don't hide snapshots from default 'lvs' output.
+  Add is_dm_major() for use in duplicate device detection in lvmcache_add().
+  Really switch device number in lvmcache when it says it is doing so.
+  Option for bitset memory allocation using malloc as well as pool.
+  Don't assume exactly two mirrors when parsing mirror status.
+  Suppress fsync() error message on filesystems that don't support it.
+  Fix yes_no_prompt() error handling.
+  Add lvm.conf comment warning against multiple filter lines.
+  Tidy lvmconf.sh.
+  Add format1 dev_write debug messages.
+  Add clustered VG attribute to report.
+  Move lvconvert parameters into struct lvconvert_params.
+  Add clustered VG flag to LV lock requests.
+  Change LV locking macros to take lv instead of lvid.
+  Prepend 'cluster' activation parameter to mirror log when appropriate.
+  Pass exclusive flag to lv_activate and on to target activation code.
+  Prevent snapshot creation in a clustered VG for now.
+  Factor out adjusted_mirror_region_size() and generate_log_name_format().
+  Move compose_log_line() into mirror directory.
+  Factor out _get_library_path().
+  Don't kill idling clvmd threads.
+  clvmd no longer takes out locks for non-clustered LVs.
+  Recognise ATA over Ethernet (aoe) devices.
+
+Version 2.01.14 - 4th August 2005
+=================================
+  Fix lvconvert PV parameter in help string.
+  Prevent snapshots getting activated in a clustered VG.
+  Separate out _build_dev_string.
+  Move zero_lv to toollib.
+  Fix pool format handler to work with pv segment code.
+
+Version 2.01.13 - 13th July 2005
+================================
+  Fix pvmove segment splitting.
+  Abstract vg_validate.
+  Only make one attempt at contiguous allocation.
+  Fix lvm1 format metadata read.
+  Fix lvm1 format non-mirror lvcreate.
+
+Version 2.01.12 - 14th June 2005
+================================
+  Various allocation-related pvmove fixes.
+  Log an error if clvmd can't resolve a host name got from CCS.
+  Fix potential spin loop in clvmd.
+
+Version 2.01.11 - 13th June 2005
+================================
+  Added lvmconf.sh.
+  Use matchpathcon mode parameter.
+  Don't defer closing dead FDs in clvmd.
+  Remove hard-coded 64k text metadata writing restriction.
+  Make VG name restrictions consistent.
+  Introduce lvconvert.  So far only removes mirror images.
+  Allow mirror images to be resized.
+  Allow mirror images to have more than one segment.
+  Centralise restrictions on LV names.
+  Always insert an intermediate layer for mirrors.
+  Suppress hidden LVs from reports unless --all is given.
+  Use square brackets for hidden LVs in reports.
+  Allow the creation of mirrors with contiguous extents.
+  Always perform sanity checks against metadata before committing it to disk.
+  Split lv_extend into two steps: choosing extents + allocation to LV(s).
+  Add mirror log region size to metadata.
+  Use list_iterate_items throughout and add list*back macros.
+  Introduce seg_ macros to access areas.
+  Add segtype_is_ macros.
+  Support tiny metadata areas for pool conversions.
+  Mirror activation handles disk log as well as core.
+  Activation code recognises mirror log dependency.
+  Add mirror_log and regionsize fields to report.
+  Fix non-orphan pvchange -u.
+  Fix vgmerge to handle duplicate LVIDs.
+  Move archiver code from tools into library.
+  vgscan/change/display/vgs automatically create metadata backups if needed.
+  Merge cloned allocation functions.
+  Fix contiguous allocation policy with linear.
+  Cope with missing format1 PVs again.
+  Remove lists of free PV segments.
+  Simplify pv_maps code and remove slow bitset algorithm.
+  Red-Hat-ify the clvmd rhel4 initscript.
+  %Zu->%zu
+  Fix loopfiles alias alloc & mem debugging.
+  Un-inline dbg_strdup.
+  lv_reduce tidying.
+  Remove some unnecessary parameters.
+  Introduce seg_is macros.
+
+Version 2.01.10 - 3rd May 2005
+==============================
+  Don't create backup and archive dirs till needed.
+  Reinstate full PV size when removing from VG.
+  Support loopfiles for testing.
+  Tidy lv_segment interface.
+  pv_segment support.
+  vgchange --physicalextentsize
+  Internal snapshot restructuring.
+  Remove unused internal non-persistent snapshot option.
+  Allow offline extension of snapshot volumes.
+  Move from 2-step to 3-step on-disk metadata commit.
+  Scan ramdisks too and allow non-O_DIRECT fallback.
+  Annotate, tidy and extend list.h.
+  Alignment tidying.
+  Make clvmd work around some "bugs" in gulm's node state notifications.
+  Tidy clvmd's SIGHUP handler
+
+Version 2.01.09 - 4th April 2005
+================================
+  Add --ignorelockingfailure to vgmknodes.
+  clvmd: Don't allow user operations to start until the lvm thread is fully up.
+  clvmd-gulm: set KEEPALIVE on sockets.
+
+Version 2.01.08 - 22nd March 2005
+=================================
+  Add clustered attribute so vgchange can identify clustered VGs w/o locking.
+  Improve detection of external changes affecting internal cache.
+  Add 'already in device cache' debug message.
+  Add -a to pvdisplay -C.
+  Avoid rmdir opendir error messsages when dir was already removed.
+  Tighten signal handlers.
+  Avoid some compiler warnings.
+  Additional rename failure error message.
+  read/write may be macros.
+  clvmd: don't take out lvm thread lock at startup, it only protects jobs list.
+
+Version 2.01.07 - 8th March 2005
+================================
+  Cope with new devices appearing by rescanning /dev if a uuid can't be found.
+  Remove DESTDIR from LVM_SHARED_PATH.
+  clvmd fixes: make FDs close-on-exec
+               gulm unlocks VG & orphan locks at startup in case they are stale
+               gulm now unlocks VG & orphan locks if client dies.
+
+Version 2.01.06 - 1st March 2005
+================================
+  Suppress 'open failed' error messages during scanning.
+  Option to suppress warnings of file descriptors left open.
+  Fix default value of metadatacopies in documentation (2->1).
+  Fix clvmd-gulm locking.
+  ./configure --enable-debug now enables debugging code in clvmd.
+  Fix clvmd-gulm node up/down code so it actually works.
+  clvmd-gulm now releases locks when shut down.
+
+Version 2.01.05 - 18th February 2005
+====================================
+  Static binary invokes dynamic binary if appropriate.
+  Make clvmd config check a little more tolerant.
+  gulm clvmd can now cope with >1 message arriving in a TCP message.
+
+Version 2.01.04 - 9th February 2005
+===================================
+  Add fixed offset to imported pool minor numbers.
+  Update binary pathnames in clvmd_init_rhel4.
+  lvm2cmd.so should skip the check for open fds.
+  Remove unused -f from pvmove.
+  Gulm clvmd doesn't report "connection refused" errors.
+  clvmd does a basic config file sanity check at startup.
+  Fix potential thread shutdown race in clvmd.
+
+Version 2.01.03 - 1st February 2005
+===================================
+  More 64-bit display/report fixes.
+  More informative startup mesg if can't create /etc/lvm.
+  Fix snapshot device size bug (since 2.01.01).
+  clvmd announces startup and cluster connection in syslog.
+  Gulm clvmd doesn't hang trying to talk to a rebooted node.
+  Gulm clvmd doesn't print cman error on startup.
+
+Version 2.01.02 - 21st January 2005
+===================================
+  Update clvmd_init_rhel4: use lvm.static and don't load dlm.
+  Fix some size_t printing.
+  Fix 64 bit xlate consts.
+  Split out pool sptype_names to avoid unused const.
+  Always fail if random id generation fails.
+  Recognise gnbd devices.
+  Fix clvmd startup bug introduced in cman/gulm amalgamation.
+  Improve reporting of node-specific locking errors.
+
+Version 2.01.01 - 19th January 2005
+===================================
+  Fix clvmd lv_info_by_lvid open_count.
+  Store snapshot and origin sizes separately.
+  Update vgcreate man page.
+
+Version 2.01.00 - 17th January 2005
+===================================
+  Fix vgscan metadata auto-correction.
+  Only ask libdevmapper for open_count when we need it.
+  Adjust RHEL4 clvmd init script priority.
+  Enable building of CMAN & GULM versions of clvmd into a single binary
+
+Version 2.00.33 - 7th January 2005
+==================================
+  pvcreate wipes first 4 sectors unless given --zero n.
+  gulm clvmd now uses new ccsd key names.
+  gulm clvmd now doesn't ignore the first node in cluster.conf
+  Improve clvmd failure message if it's already running.
+  Allow user to kill clvmd during initialisation.
+  Fix off-by-one error in cluster_locking that could cause read hangs.
+
+Version 2.00.32 - 22nd December 2004
+====================================
+  Drop static/dl restriction for now.
+  Fix an error fprintf.
+  Fix vgdisplay -s. Breaks (undocumented) lvs/pvs/vgs -s instead for now.
+  Fix device reference counting on re-opens.
+  Ignore sysfs symlinks when DT_UNKNOWN.
+  Add clvmd init script for RHEL4.
+  Skip devices that are too small to be PVs.
+  Fix pvchange -x segfault with lvm2-format orphan.
+  Cope with empty msdos partition tables.
+  Add CONTRIBUTORS file.
+
+Version 2.00.31 - 12th December 2004
+====================================
+  Reopen RO file descriptors RW if necessary.
+
+Version 2.00.30 - 10th December 2004
+====================================
+  Additional device-handling debug messages.
+  Additional verbosity level -vvvv includes line numbers and backtraces.
+  Verbose messages now go to stderr not stdout.
+  Close any stray file descriptors before starting.
+  Refine partitionable checks for certain device types.
+  Allow devices/types to override built-ins.
+  Fix lvreduce man page .i->.I
+  Fix vgsplit man page title.
+  Fix clvmd man makefile.
+  Extend dev_open logging.
+  Make clvmd_fix_conf.sh UNDOable.
+
+Version 2.00.29 - 27th November 2004
+====================================
+  xlate compilation fix.
+
+Version 2.00.28 - 27th November 2004
+====================================
+  Fix partition table & md signature detection.
+  Minor configure/makefile tidy.
+  Export version.h from tools for clvmd.
+
+Version 2.00.27 - 24th November 2004
+====================================
+  Trap large memory allocation requests.
+  Fix to partition table detection code.
+  Improve filter debug mesgs.
+  Make clvmd_fix_conf.sh UNDOable
+
+Version 2.00.26 - 23rd November 2004
+====================================
+  Improve pool debugging stats.
+  Detect partition table signature.
+  pvcreate wipes md superblocks. (With --uuid or --restorefile it prompts.)
+  Separate out md superblock detection code.
+  Prevent snapshot origin resizing.
+  Improve a vgremove error message.
+  Update some man pages.
+  Allow y/n with -ae args (exclusive activation).
+  Fixes to lvcreate vgname parsing.
+  Fix dm_name string size calculation.
+  Improve clvmd error reporting during startup.
+  Make clvmd cope with large gaps in node numbers IDs.
+  Make clvmd initialisation cope better with debugging output.
+  Tidy clvmd socket callbacks so all work happens outside main loop.
+  clvmd -V now displays lvm version too.
+  Add optional gulm build for clvmd
+
+Version 2.00.25 - 29th September 2004
+=====================================
+  Fix return code from rm_link for vgmknodes.
+  Make clvmd LV hash table thread-safe.
+  Fix clvmd locking so it will lock out multiple users on the same node.
+  Fix clvmd VG locking to it can cope with multiple VG locks.
+  Remove spurious trailing dot in lvreduce man page.
+  Fix vgremove locking.
+
+Version 2.00.24 - 16th September 2004
+=====================================
+  Fix pool_empty so it really does empty the memory pool.
+  Rename old segtypes files to segtype.
+  Some fixes to memory debugging code.
+  Exclude internal commands formats & segtypes from install.
+
+Version 2.00.23 - 15th September 2004
+=====================================
+  Export dm name build & split functions.
+  Use O_NOATIME on devices if available.
+  Write log message when each segtype/format gets initialised.
+  New commands 'segtypes' and 'formats'.
+  Suppress pvmove abort message in test mode.
+  Improve pvcreate/remove device not found error message.
+  Allow pvmove to move data within the same PV.
+  Describe how pvmove works on man page.
+  Test for incompatible format/segtype combinations in lv_extend.
+  Fix lvchange example on man page.
+
+Version 2.00.22 - 3rd September 2004
+====================================
+  Fix /dev/vgname perms.
+  Restructure xlate.h.
+  Add clvmd man page.
+
+Version 2.00.21 - 19th August 2004
+==================================
+  Update cnxman-socket.h from cman.
+  Recognise iseries/vd devices.
+  Use 'make install_cluster' to install cluster extensions only.
+  Cope with DT_UNKNOWN in sysfs.
+  Fix extents_moved metadata size comment.
+  Remove duplicate line in pvremove help text.
+  Support variable mirror region size.
+  Support PE ranges in pvmove source PV.
+  Fixes to as-yet-unused LV segment splitting code.
+  Change alloc_areas to pe_ranges and allow suppression of availability checks.
+  Add dev_size column to pvs.
+  Add report columns for in-kernel device number.
+
+Version 2.00.20 - 3 July 2004
+=============================
+  More autoconf fixes.
+  Fix device number handling for 2.6 kernels.
+
+Version 2.00.19 - 29 June 2004
+==============================
+  Reduce severity of setlocale failure message.
+  Recognise argv[0] "initrd-lvm" (pld-linux).
+  Make -O2 configurable.
+  Added --disable-selinux to configure script.
+  LD_FLAGS->LDFLAGS & LD_DEPS->LDDEPS in configure script.
+  Add init_debug to clvmd.
+
+Version 2.00.18 - 24 June 2004
+==============================
+  Fix vgchange activation.
+  Add cluster support.
+
+Version 2.00.17 - 20 June 2004
+==============================
+  configure --enable-fsadm to try out fsadm.  fsadm is not tested yet.
+  Display all filtered devices, not just PVs, with pvs -a.
+  Fix sync_dir() when no / in filename
+  vgcfgbackup -f accepts template with %s for VG name.
+  Extend hash functions to handle non-null-terminated data.
+  Add local activation support.
+  Tidy relative paths in makefile includes.
+  fsadm support for fsck and resizing - needs testing.
+  Add read-only GFS pool support.
+  Add lvm2create_initrd script from http://poochiereds.net/svn/lvm2/
+  Fix rounding of large diplayed sizes.
+  Suppress decimal point when using units of sectors/bytes.
+  Additional kernel target checks before pvmove & snapshot creation.
+  Add i2o_block.
+
+Version 2.00.16 - 24 May 2004
+=============================
+  Set area_count within alloc_lv_segment.
+  Remove error labels from lvresize.
+  Fix a pvs error path.
+  xxchange -ae for exclusive activation.
+  Don't return non-zero status if there aren't any volume groups.
+  Add --alloc argument to tools.
+  Rename allocation policies to contiguous, normal, anywhere, inherit.
+  nextfree becomes normal; anywhere isn't implemented yet.
+  LV inherits allocation policy from VG. Defaults: LV - inherit; VG - normal
+  Additional status character added to vgs to indicate allocation policy.
+  Add reset_fn to external_locking.
+  Ensure presence of virtual targets before attempting activating.
+  Attempt to fix resizing of snapshot origins.
+  Restructure lvresize, bringing it closer to lvcreate.
+  A quick sanity check on vg_disk struct when read in.  More checks needed.
+  Only include visible LVs in active/open counts.
+  Add virtual segment types, zero and error.  A large sparse device can be
+constructed as a writeable snapshot of a large zero segment.
+  Add --type to lvcreate/resize.
+  Push lv_create & alloc policy up to tool level.
+  Fix pvdisplay return code.
+  Detect invalid LV names in arg lists.
+  Reporting uses line-at-a-time output.
+  lvm2 format sets unlimited_vols format flag.
+  Internal-only metadata flag support.
+  Basic checking for presence of device-mapper targets.
+  Separate out polldaemon.
+  Revise internal locking semantics.
+  Move find_pv_by_name to library.
+  Rename move->copy.
+  Add devices to segments report.
+  Begin separating out segment code. There's a lot of change here.
+  Compress any (obsolete) long LVM1 pvids encountered.
+  Support for tagged config files.
+  Don't abort operations if selinux present but disabled.
+  Fix typo in configure which left HAVE_LIBDL unset.
+
+Version 2.00.15 - 19 Apr 2004
+=============================
+  configure --with-owner= --with-group= to avoid -o and -g args to 'install'
+
+Version 2.00.14 - 16 Apr 2004
+=============================
+  Use 64-bit file functions by default.
+
+Version 2.00.13 - 16 Apr 2004
+=============================
+  Set devices/md_component_detection = 1 to ignore devices containing md
+  superblocks. [Luca Berra]
+  Ignore error setting selinux file context if fs doesn't support it.
+
+Version 2.00.12 - 14 Apr 2004
+=============================
+  Install a default lvm.conf into /etc/lvm if there isn't one already.
+  Allow different installation dir for lvm.static (configure --staticdir=)
+  Fix inverted selinux error check.
+  Recognise power2 in /proc/devices.
+  Fix counting in lvs_in_vg_opened. [It ignored devices open more than once.]
+
+Version 2.00.11 - 8 Apr 2004
+============================
+  Set fallback_to_lvm1 in lvm.conf (or configure --enable-lvm1_fallback)
+  to run lvm1 binaries if running a 2.4 kernel without device-mapper.
+
+Version 2.00.10 - 7 Apr 2004
+============================
+  More fixes for static build.
+  Add basic selinux support.
+  Fix sysfs detection.
+
+Version 2.00.09 - 31 Mar 2004
+=============================
+  Update copyright notices for Red Hat.
+  Fix vgmknodes to remove dud /dev/mapper entries. (libdevmapper update reqd).
+  Add LVM1-style colon output to vgdisplay.
+  lvchange --refresh to reload active LVs.
+  Add string display to memory leak dump.
+  Add locking flags & memlock option.
+  Add list_versions to library.
+  Ignore open hidden LVs when checking if deactivation is OK.
+  Suppress move percentage when device inactive.
+  Add lv_info_by_lvid.
+  Various tidy-ups to the build process.
+  Rebaseline internal verbose level.
+  Add --nolocking option for read operations if locking is failing.
+  Add option to compile into a library.
+  When compiled without libdevmapper, only print warning message once.
+  Fix lvreduce PV extent calculations.
+  Fix DESTDIR to work with configure path overrides.
+  Always use / as config file separator & rename internal config file variables.
+  Add support for tagging PV/VG/LVs and hosts.
+  Fix rare bug in recognition of long cmdline argument forms.
+  Add basic internationalisation infrastructure.
+  Don't recurse symlinked dirs such as /dev/fd on 2.6 kernels.
+  Update autoconf files.
+  Add sysfs block device filtering for 2.6 kernels.
+  Update refs for move to sources.redhat.com.
+
+Friday 14th November 2003
+=========================
+Some bug fixes & minor enhancements, including:
+  Backwards compatibility with LVM1 metadata improved.
+  Missing man pages written.
+  Tool error codes made more consistent.
+  vgmknodes written.
+  O_DIRECT can be turned off if it doesn't work in your kernel.
+  dumpconfig to display the active configuration file
+
+You need to update libdevmapper before using 'vgmknodes' or 'vgscan --mknodes'.
+If your root filesystem is on an LV, you should run one of those two
+commands to fix up the special files in /dev in your real root filesystem
+after finishing with your initrd.  Also, remember you can use
+'vgchange --ignorelockingfailure' on your initrd if the tool fails because
+it can't write a lock file to a read-only filesystem.
+
+Wednesday 30th April 2003
+=========================
+A pvmove implementation is now available for the new metadata format.
+
+When running a command that allocates space (e.g. lvcreate), you can now
+restrict not only which disk(s) may be used but also the Physical Extents
+on those disks.  e.g. lvcreate -L 10 vg1 /dev/hda6:1000-2000:3000-4000
+
+
+Monday 18th November 2002
+========================
+
+The new format of LVM metadata is ready for you to test!
+  We expect it to be more efficient and more robust than the original format.
+  It's more compact and supports transactional changes and replication.
+  Should things go wrong on a system, it's human-readable (and editable).
+
+Please report any problems you find to the mailing list,
+linux-lvm@sistina.com.  The software has NOT yet been thoroughly
+tested and so quite possibly there'll still be some bugs in it.
+Be aware of the disclaimer in the COPYING file.
+
+While testing, we recommend turning logging on in the configuration file
+to provide us with diagnostic information:
+  log {
+        file="/tmp/lvm2.log"
+       level=7
+       activation=1
+  }
+
+You should schedule regular backups of your configuration file and
+metadata backups and archives (normally kept under /etc/lvm).
+
+Please read docs/example.conf and "man lvm.conf" to find out more about
+the configuration file.
+
+To convert an existing volume group called vg1 to the new format using
+the default settings, use "vgconvert -M2 vg1".  See "man vgconvert".
+
+-M (or --metadatatype in its long form) is a new flag to indicate which
+format of metadata the command should use for anything it creates.
+Currently, the valid types are "lvm1" and "lvm2" and they can be
+abbreviated to "1" and "2" respectively.  The default value for this
+flag can be changed in the global section in the config file.
+
+Backwards-compatible support for the original LVM1 metadata format is
+maintained, but it can be moved into a shared library or removed
+completely with configure's --with-lvm1 option.
+
+Under LVM2, the basic unit of metadata is the volume group.  Different
+volume groups can use different formats of metadata - vg1 could use
+the original LVM1 format while vg2 used the new format - but you can't
+mix formats within a volume group.  So to add a PV to an LVM2-format
+volume group you must run "pvcreate -M2" on it, followed by "vgextend".
+
+With LVM2-format metadata, lvextend will let you specify striping
+parameters.  So an LV could consist of two or more "segments" - the
+first segment could have 3 stripes while the second segment has just 2.
+
+LVM2 maintains a backup of the current metadata for each volume group
+in /etc/lvm/backup, and puts copies of previous versions in
+/etc/lvm/archive.  "vgcfgbackup" and "vgcfgrestore" can be used to
+create and restore from these files.  If you fully understand what
+you're doing, metadata can be changed by editing a copy of a current
+backup file and using vgcfgrestore to reload it.
+
+Please read the pvcreate man page for more information on the new
+format for metadata.
+
+All tools that can change things have a --test flag which can be used
+to check the effect  of a set of cmdline args without really making the
+changes.
+
+
+What's not finished?
+====================
+The internal cache.  If you turn on debugging output you'll see lots of
+repeated messages, many of which will eventually get optimised out.
+
+--test sometimes causes a command to fail (e.g. vgconvert --test) even
+though the real command would work: again, fixing this is waiting for
+the work on the cache.
+
+Several of the tools do not yet contain the logic to handle full
+recovery: combinations of pvcreate and vgcfgrestore may sometimes be
+needed to restore metadata if a tool gets interrupted or crashes or
+finds something unexpected.  This applies particularly to tools that
+work on more than one volume group at once (e.g. vgsplit).
+
+Display output.  Some metadata information cannot yet be displayed.
+
+Recovery tools to salvage "lost" metadata directly from the disks:
+but we hope the new format will mean such tools are hardly ever needed!
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
new file mode 100644 (file)
index 0000000..c6ce2e6
--- /dev/null
@@ -0,0 +1,593 @@
+Version 1.02.60 - 20th December 2010
+====================================
+  Check for unlink failure in remove_lockfile() in dmeventd.
+  Use dm_free for dm_malloc-ed areas in _clog_ctr/_clog_dtr in cmirrord.
+  Use char* arithmetic in _process_all() & _targets() in dmsetup.
+  Change dm_regex_create() API to accept const char * const *patterns.
+  Add new dm_prepare_selinux_context fn to libdevmapper and use it throughout.
+  Detect existence of new SELinux selabel interface during configure.
+
+Version 1.02.59 - 6th December 2010
+===================================
+  Add backtraces to _process_mapper_dir and  _create_and_load_v4 error paths.
+  Remove superfluous checks for NULL before calling dm_free.
+
+Version 1.02.58 - 22nd November 2010
+====================================
+  Fix _output_field crash from field_id free with DEBUG_MEM. (1.02.57)
+
+Version 1.02.57 - 8th November 2010
+===================================
+  Fix regex optimiser not to ignore RHS of OR nodes in _find_leftmost_common.
+  Add dmeventd -R to restart dmeventd without losing monitoring state. (1.02.56)
+  Fix memory leak of field_id in _output_field function.
+  Allocate buffer for reporting functions dynamically to support long outputs.
+
+Version 1.02.56 - 25th October 2010
+===================================
+  Return const pointer from dm_basename() in libdevmapper.
+  Implement dmeventd -R to restart without state loss.
+  Add dm_zalloc and use it and dm_pool_zalloc throughout.
+  Add --setuuid to dmsetup rename.
+  Add dm_task_set_newuuid to set uuid of mapped device post-creation.
+
+Version 1.02.55 - 24th September 2010
+=====================================
+  Fix the way regions are marked complete to avoid slow --nosync cmirror I/O.
+  Add DM_REPORT_FIELD_TYPE_ID_LEN to libdevmapper.h.
+
+Version 1.02.54 - 18th August 2010
+==================================
+  Fix dm-mod autoloading logic to not assume control node is set correctly.
+  Add dmeventd/executable to lvm.conf to test alternative dmeventd.
+  Export dm_event_handler_set_dmeventd_path to override built-in dmeventd path.
+  Generate libdevmapper-event exported symbols.
+  Remove superfluous NULL pointer tests before dm_free from dmeventd.
+  Assume dm-mod autoloading support is in kernel 2.6.36 and higher, not 2.6.35.
+  Fix udev rules to support udev database content generated by older rules.
+  Reinstate detection of inappropriate uevent with DISK_RO set and suppress it.
+  Fix regex ttree off-by-one error.
+  Add --enable-valgrind-pool to configure.
+  Fix segfault in regex matcher with characters of ordinal value > 127.
+  Fix 'void*' arithmetic warnings in dbg_malloc.c and libdm-iface.c.
+  Wait for node creation before displaying debug info in dmsetup.
+  Fix return status 0 for "dmsetup info -c -o help".
+  Add check for kernel semaphore support and disable udev_sync if not available.
+
+Version 1.02.53 - 28th July 2010
+================================
+  Revert failed table load preparation after "create, load and resume".
+  Switch dmeventd to use dm_create_lockfile and drop duplicate code.
+  Add dm_create_lockfile to libdm to handle pidfiles for all daemons.
+  Replace lookup with next in struct dfa_state & calculate states on demand.
+  Improve the regex matcher, reducing the number of charset nodes used.
+  Add dm_regex_fingerprint to facilitate regex testing.
+  Skip ffs(0) in _test_word in bitset functions.
+  Use "nowatch" udev rule for inappropriate devices.
+
+Version 1.02.52 - 6th July 2010
+===============================
+  Fix dmlosetup snprintf %llu compiler warning.
+  Add parentheses to some libdevmapper.h macro arguments.
+  Add printf format attributes to dm_{sn,as}printf and fix a caller.
+  Move dmeventd man page from install_lvm2 to install_device-mapper. (1.02.50)
+
+Version 1.02.51 - 30th June 2010
+================================
+  Generate libdevmapper exported symbols from header file.
+
+Version 1.02.50 - 23rd June 2010
+================================
+  Fix INTERNAL_ERROR typo in ioctl iface unknown task message.
+  Fix udev rules to handle spurious events properly.
+  Use C99 [] not [0] in dm_ulog_request struct to avoid abort when fortified.
+  Allow use of devmapper header file in C++ mode (extern "C" and __typeof__).
+  Add dmeventd man page.
+
+Version 1.02.49 - 4th June 2010
+===============================
+  Support autoloading of dm-mod module for kernels from 2.6.35.
+  Document 'clear' in dmsetup man page.
+  Fix semctl parameter (union) to avoid misaligned parameter on some arches.
+  Add dm_tree_node_set_presuspend_node() to presuspend child when deactivating.
+  Initial support for replicator target.
+
+Version 1.02.48 - 17th May 2010
+================================
+  Use -d to control level of messages sent to syslog by dmeventd.
+  Change -d to -f to run dmeventd in foreground.
+  Do not print encryption key in message debug output (cryptsetup luksResume).
+  Fix dmeventd static build library dependencies.
+  Fix udev flags on remove in create_and_load error path.
+
+Version 1.02.47 - 30th April 2010
+=================================
+  Add support for new IMPORT{db} udev rule.
+  Add DM_UDEV_PRIMARY_SOURCE_FLAG udev flag to recognize proper DM events.
+  Also include udev libs in libdevmapper.pc when udev_sync is enabled.
+  Cache bitset locations to speed up _calc_states.
+  Add a regex optimisation pass for shared prefixes and suffixes.
+  Add dm_bit_and and dm_bitset_equal to libdevmapper.
+  Simplify dm_bitset_create.
+  Speed up dm_bit_get_next with ffs().
+
+Version 1.02.46 - 14th April 2010
+=================================
+  Change dm_tree_deactivate_children to fail if device is open.
+  Wipe memory buffers for dm-ioctl parameters before releasing.
+  Strictly require libudev if udev_sync is used.
+  Add support for ioctl's DM_UEVENT_GENERATED_FLAG.
+
+Version 1.02.45 - 9th March 2010
+================================
+  Add --showkeys parameter description to dmsetup man page.
+  Add --help option as synonym for help command.
+
+Version 1.02.44 - 15th February 2010
+====================================
+  Add DM_UDEV_DISABLE_LIBRARY_FALLBACK udev flag to rely on udev only.
+  Export dm_udev_create_cookie function to create new cookies on demand.
+  Add --udevcookie, udevcreatecookie and udevreleasecookie to dmsetup.
+  Set udev state automatically instead of using DM_UDEV_DISABLE_CHECKING.
+
+Version 1.02.43 - 21st January 2010
+===================================
+  Remove bitset, hash and pool headers superceded by libdevmapper.h.
+  Fix off-by-one error causing bad cluster mirror table construction.
+
+Version 1.02.42 - 14th January 2010
+===================================
+  Add support for the "snapshot-merge" kernel target (2.6.33-rc1).
+  Introduce a third activation_priority level in dm_tree_activate_children.
+
+Version 1.02.41 - 12th January 2010
+===================================
+  If DM_UDEV_DISABLE_CHECKING is set in environment, disable udev warnings.
+  Add dm_tree_add_dev_with_udev_flags to provide wider support for udev flags.
+  Add --noudevrules option for dmsetup to disable /dev node management by udev.
+  Fix 'dmsetup info -c -o all' to show all fields.
+  Return errors if dm_tree_*_children functions fail.
+  Fix coredump and memory leak for 'dmsetup help -c'.
+  Disable udev rules for change events with DISK_RO set.
+
+Version 1.02.40 - 19th November 2009
+====================================
+  Fix install_device-mapper Makefile target to not build dmeventd plugins.
+  Support udev flags even when udev_sync is disabled or not compiled in.
+  Remove 'last_rule' from udev rules: honour DM_UDEV_DISABLE_OTHER_RULES_FLAG.
+  Add dmsetup --inactive support.
+  Add dm_task_query_inactive_table to libdevmapper for kernel driver >= 4.16.
+  Fix hash lookup segfault when keys compared are different lengths.
+
+Version 1.02.39 - 26th October 2009
+===================================
+  Remove strict default permissions for DM devices from 95-dm-notify.rules.
+  Add dmsetup udevflags command to decode udev flags in given cookie value.
+  Support udev flags in libdevmapper incl. dm_tree_add_new_dev_with_udev_flags.
+  Make libdm ABI consistent when built with/without selinux support.
+
+Version 1.02.38 - 25th September 2009
+=====================================
+  Export DM_DEV_DIR_UMASK, the default umask for /dev directories created.
+  Handle any path supplied to dm_task_set_name by looking up in /dev/mapper.
+  Add several examples to 12-dm-permissions.rules.
+  Add splitname and --yes to dmsetup man page.
+  Fix _mirror_emit_segment_line return code.
+  Fix dmeventd _temporary_log_fn parameters. (2.02.50)
+
+Version 1.02.37 - 15th September 2009
+=====================================
+  Add dmsetup manpage entries for udevcomplete_all and udevcookies.
+  Check udev is running when processing cookies and retain state internally.
+  Add y|--yes option to dmsetup for default 'yes' answer to prompts.
+  Fix tools Makefile to process dmsetup sources separately.
+  Restore umask when device node creation fails.
+  Check kernel vsn to use 'block_on_error' or 'handle_errors' in mirror table.
+  Add dm-log-userspace.h to tree for cmirrord builds.
+
+Version 1.02.36 - 6th August 2009
+=================================
+  Add udevcookies, udevcomplete, udevcomplete_all and --noudevwait to dmsetup.
+  Add libdevmapper functions to support synchronisation with udev.
+
+Version 1.02.35 - 28th July 2009
+================================
+  Add LOG_LINE_WITH_ERRNO macro.
+  Use log_error macro consistently throughout in place of log_err.
+
+Version 1.02.34 - 15th July 2009
+================================
+  Use _exit() not exit() after forking to avoid flushing libc buffers twice.
+  Rename plog macro to LOG_LINE & add LOG_MESG variant for dm_dump_memory_debug.
+  Change plog to use dm_log_with_errno unless deprecated dm_log_init was used.
+  Add dm_log_with_errno and dm_log_with_errno_init, deprecating the old fns.
+  Fix whitespace in linear target line to fix identical table line detection.
+  Add device number to more log messages during activation.
+
+Version 1.02.33 - 30th June 2009
+================================
+  Don't fallback to default major number: use dm_task_set_major_minor. (1.02.31)
+  Do not fork daemon when dmeventd cannot be found.
+  Add crypt target handling to libdevmapper tree nodes.
+  Add splitname command to dmsetup.
+  Add subsystem, vg_name, lv_name, lv_layer fields to dmsetup reports.
+  Make mempool optional in dm_split_lvm_name().
+
+Version 1.02.32 - 21st May 2009
+===============================
+  Only generate libdevmapper.a when configured to link statically.
+  Export dm_tree_node_size_changed() from libdevmapper.
+  Propagate the table size_changed property up the dm device tree.
+  Detect failure to free memory pools when releasing the library.
+  Fix segfault when getopt processes dmsetup -U, -G and -M options.
+
+Version 1.02.31 - 3rd March 2009
+================================
+  If kernel supports only one dm major number, use in place of any supplied.
+
+Version 1.02.30 - 26th January 2009
+====================================
+  Add "all" field to reports expanding to all fields of report type.
+  Enforce device name length and character limitations in libdm.
+  Replace _dm_snprintf with EMIT_PARAMS macro for creating target lines.
+
+Version 1.02.29 - 10th November 2008
+====================================
+  Merge device-mapper into the LVM2 tree.
+  Split out dm-logging.h from log.h.
+  Use lvm-types.h.
+  Add usrsbindir to configure.
+
+Version 1.02.28 - 18th September 2008
+=====================================
+  Only resume devices in dm_tree_preload_children if size changes.
+  Extend deptree buffers so the largest possible device numbers fit.
+  Generate versioned libdevmapper-event.so.
+  Underline longer report help text headings.
+
+Version 1.02.27 - 25th June 2008
+================================
+  Align struct memblock in dbg_malloc for sparc.
+  Add --unquoted and --rows to dmsetup.
+  Avoid compiler warning about cast in dmsetup.c's OFFSET_OF macro.
+  Fix inverted no_flush debug message.
+  Remove --enable-jobs from configure. (Set at runtime instead.)
+  Bring configure.in and list.h into line with the lvm2 versions.
+
+Version 1.02.26 - 6th June 2008
+===============================
+  Initialise params buffer to empty string in _emit_segment.
+  Skip add_dev_node when ioctls disabled.
+  Make dm_hash_iter safe against deletion.
+  Accept a NULL pointer to dm_free silently.
+  Add tables_loaded, readonly and suspended columns to reports.
+  Add --nameprefixes to dmsetup.
+  Add field name prefix option to reporting functions.
+  Calculate string size within dm_pool_grow_object.
+
+Version 1.02.25 - 10th April 2008
+=================================
+  Remove redundant if-before-free tests.
+  Use log_warn for reporting field help text instead of log_print.
+  Change cluster mirror log type name (s/clustered_/clustered-/)
+
+Version 1.02.24 - 20th December 2007
+====================================
+  Fix deptree to pass new name to _resume_node after a rename.
+  Suppress other node operations if node is deleted.
+  Add node operation stack debug messages.
+  Report error when empty device name passed to readahead functions.
+  Fix minimum readahead debug message.
+
+Version 1.02.23 - 5th December 2007
+===================================
+  Update dm-ioctl.h after removal of compat code.
+  Add readahead support to libdevmapper and dmsetup.
+  Fix double free in a libdevmapper-event error path.
+  Fix configure --with-dmeventd-path substitution.
+  Allow a DM_DEV_DIR environment variable to override /dev in dmsetup.
+  Create a libdevmapper.so.$LIB_VERSION symlink within the build tree.
+  Avoid static link failure with some SELinux libraries that require libpthread.
+  Remove obsolete dmfs code from tree and update INSTALL.
+
+Version 1.02.22 - 21st August 2007
+==================================
+  Fix inconsistent licence notices: executables are GPLv2; libraries LGPLv2.1.
+  Update to use autoconf 2.61, while still supporting 2.57.
+  Avoid repeated dm_task free on some dm_event_get_registered_device errors.
+  Introduce log_sys_* macros from LVM2.
+  Export dm_fclose and dm_create_dir; remove libdm-file.h.
+  Don't log EROFS mkdir failures in _create_dir_recursive (for LVM2).
+  Add fclose wrapper dm_fclose that catches write failures (using ferror).
+
+Version 1.02.21 - 13th July 2007
+================================
+  Introduce _LOG_STDERR to send log_warn() messages to stderr not stdout.
+  Fix dmsetup -o devno string termination. (1.02.20)
+
+Version 1.02.20 - 15th June 2007
+================================
+  Fix default dmsetup report buffering and add --unbuffered.
+  Add tree-based and dependency fields to dmsetup reports.
+
+Version 1.02.19 - 27th April 2007
+=================================
+  Standardise protective include file #defines.
+  Add regex functions to library.
+  Avoid trailing separator in reports when there are hidden sort fields.
+  Fix segfault in 'dmsetup status' without --showkeys against crypt target.
+  Deal with some more compiler warnings.
+  Introduce _add_field() and _is_same_field() to libdm-report.c.
+  Fix some libdevmapper-event and dmeventd memory leaks.
+  Remove unnecessary memset() return value checks.
+  Fix a few leaks in reporting error paths. [1.02.15+]
+
+Version 1.02.18 - 13th February 2007
+====================================
+  Improve dmeventd messaging protocol: drain pipe and tag messages.
+
+Version 1.02.17 - 29th January 2007
+===================================
+  Add recent reporting options to dmsetup man page.
+  Revise some report fields names.
+  Add dmsetup 'help' command and update usage text.
+  Use fixed-size fields in report interface and reorder.
+
+Version 1.02.16 - 25th January 2007
+===================================
+  Add some missing close() and fclose() return value checks.
+  Migrate dmsetup column-based output over to new libdevmapper report framework.
+  Add descriptions to reporting field definitions.
+  Add a dso-private variable to dmeventd dso interface.
+  Add dm_event_handler_[gs]et_timeout functions.
+  Streamline dm_report_field_* interface.
+  Add cmdline debug & version options to dmeventd.
+  Add DM_LIB_VERSION definition to configure.h.
+  Suppress 'Unrecognised field' error if report field is 'help'.
+  Add --separator and --sort to dmsetup (unused).
+  Make alignment flag optional when specifying report fields.
+
+Version 1.02.15 - 17th January 2007
+===================================
+  Add basic reporting functions to libdevmapper.
+  Fix a malloc error path in dmsetup message.
+  More libdevmapper-event interface changes and fixes.
+  Rename dm_saprintf() to dm_asprintf().
+  Report error if NULL pointer is supplied to dm_strdup_aux().
+  Reinstate dm_event_get_registered_device.
+
+Version 1.02.14 - 11th January 2007
+===================================
+  Add dm_saprintf().
+  Use CFLAGS when linking so mixed sparc builds can supply -m64.
+  Add dm_tree_use_no_flush_suspend().
+  Lots of dmevent changes including revised interface.
+  Export dm_basename().
+  Cope with a trailing space when comparing tables prior to possible reload.
+  Fix dmeventd to cope if monitored device disappears.
+
+Version 1.02.13 - 28 Nov 2006
+=============================
+  Update dmsetup man page (setgeometry & message).
+  Fix dmsetup free after getline with debug.
+  Suppress encryption key in 'dmsetup table' output unless --showkeys supplied.
+
+Version 1.02.12 - 13 Oct 2006
+=============================
+  Avoid deptree attempting to suspend a device that's already suspended.
+
+Version 1.02.11 -  12 Oct 2006
+==============================
+  Add suspend noflush support.
+  Add basic dmsetup loop support.
+  Switch dmsetup to use dm_malloc and dm_free.
+
+Version 1.02.10 - 19 Sep 2006
+=============================
+  Add dm_snprintf(), dm_split_words() and dm_split_lvm_name() to libdevmapper.
+  Reorder mm bounds_check code to reduce window for a dmeventd race.
+
+Version 1.02.09 - 15 Aug 2006
+=============================
+  Add --table argument to dmsetup for a one-line table.
+  Abort if errors are found during cmdline option processing.
+  Add lockfs indicator to debug output.
+
+Version 1.02.08 - 17 July 2006
+==============================
+  Append full patch to check in emails.
+  Avoid duplicate dmeventd subdir with 'make distclean'.
+  Update dmsetup man page.
+  Add --force to dmsetup remove* to load error target.
+  dmsetup remove_all also performs mknodes.
+  Don't suppress identical table reloads if permission changes.
+  Fix corelog segment line.
+  Suppress some compiler warnings.
+
+Version 1.02.07 - 11 May 2006
+=============================
+  Add DM_CORELOG flag to dm_tree_node_add_mirror_target().
+  Avoid a dmeventd compiler warning.
+
+Version 1.02.06 - 10 May 2006
+=============================
+  Move DEFS into configure.h.
+  Fix leaks in error paths found by coverity.
+  Remove dmsetup line buffer limitation.
+
+Version 1.02.05 - 19 Apr 2006
+=============================
+  Separate install_include target in makefiles.
+  Separate out DEFS from CFLAGS.
+  Support pkg-config.
+  Check for libsepol.
+
+Version 1.02.04 - 14 Apr 2006
+=============================
+  Bring dmsetup man page up-to-date.
+  Use name-based device refs if kernel doesn't support device number refs.
+  Fix memory leak (struct dm_ioctl) when struct dm_task is reused.
+  If _create_and_load_v4 fails part way through, revert the creation.
+  dmeventd thread/fifo fixes.
+  Add file & line to dm_strdup_aux().
+  Add setgeometry.
+
+Version 1.02.03 - 7 Feb 2006
+============================
+  Add exported functions to set uid, gid and mode.
+  Rename _log to dm_log and export.
+  Add dm_tree_skip_lockfs.
+  Fix dm_strdup debug definition.
+  Fix hash function to avoid using a negative array offset.
+  Don't inline _find in hash.c and tidy signed/unsigned etc.
+  Fix libdevmapper.h #endif.
+  Fix dmsetup version driver version.
+  Add sync, nosync and block_on_error mirror log parameters.
+  Add hweight32.
+  Fix dmeventd build.
+
+Version 1.02.02 - 2 Dec 2005
+============================
+  dmeventd added.
+  Export dm_task_update_nodes.
+  Use names instead of numbers in messages when ioctls fail.
+
+Version 1.02.01 - 23 Nov 2005
+=============================
+  Resume snapshot-origins last.
+  Drop leading zeros from dm_format_dev.
+  Suppress attempt to reload identical table.
+  Additional LVM- prefix matching for transitional period.
+
+Version 1.02.00 - 10 Nov 2005
+=============================
+  Added activation functions to library.
+  Added return macros.
+  Also suppress error if device doesn't exist with DM_DEVICE_STATUS.
+  Export dm_set_selinux_context().
+  Add dm_driver_version().
+  Added dependency tree functions to library.
+  Added hash, bitset, pool, dbg_malloc to library.
+  Added ls --tree to dmsetup.
+  Added dmsetup --nolockfs support for suspend/reload.
+
+Version 1.01.05 - 26 Sep 2005
+=============================
+  Resync list.h with LVM2.
+  Remember increased buffer size and use for subsequent calls.
+  On 'buffer full' condition, double buffer size and repeat ioctl.
+  Fix termination of getopt_long() option array.
+  Report 'buffer full' condition with v4 ioctl as well as with v1.
+
+Version 1.01.04 - 2 Aug 2005
+============================
+  Fix dmsetup ls -j and status --target with empty table.
+
+Version 1.01.03 - 13 Jun 2005
+=============================
+  Use matchpathcon mode parameter.
+  Fix configure script to re-enable selinux.
+
+Version 1.01.02 - 17 May 2005
+=============================
+  Call dm_lib_exit() and dm_lib_release() automatically now.
+  Add --target <target_type> filter to dmsetup table/status/ls.
+  Add --exec <command> to dmsetup ls.
+  Fix dmsetup getopt_long usage.
+
+Version 1.01.01 - 29 Mar 2005
+=============================
+  Update dmsetup man page.
+  Drop-in devmap_name replacement.
+  Add option to compile without ioctl for testing.
+  Fix DM_LIB_VERSION sed.
+
+Version 1.01.00 - 17 Jan 2005
+=============================
+  Add dm_task_no_open_count() to skip getting open_count.
+
+Version 1.00.21 - 7 Jan 2005
+============================
+  Fix /proc/devices parsing.
+
+Version 1.00.20 - 6 Jan 2005
+============================
+  Attempt to fix /dev/mapper/control transparently if it's wrong.
+  Configuration-time option for setting uid/gid/mode for /dev/mapper nodes.
+  Update kernel patches for 2.4.27/2.4.28-pre-4 (includes minor fixes).
+  Add --noheadings columns option for colon-separated dmsetup output.
+  Support device referencing by uuid or major/minor.
+  Warn if kernel data didn't fit in buffer.
+  Fix a printf.
+
+Version 1.00.19 - 3 July 2004
+=============================
+  More autoconf fixes.
+  Fix a dmsetup newline.
+  Fix device number handling for 2.6 kernels.
+
+Version 1.00.18 - 20 Jun 2004
+=============================
+  Fix a uuid free in libdm-iface.
+  Fix a targets string size calc in driver.
+  Add -c to dmsetup for column-based output.
+  Add target message-passing ioctl.
+
+Version 1.00.17 - 17 Apr 2004
+=============================
+  configure --with-owner= --with-group= to avoid -o and -g args to 'install'
+  Fix library selinux linking.
+
+Version 1.00.16 - 16 Apr 2004
+=============================
+  Ignore error setting selinux file context if fs doesn't support it.
+
+Version 1.00.15 - 7 Apr 2004
+============================
+  Fix status overflow check in kernel patches.
+
+Version 1.00.14 - 6 Apr 2004
+============================
+  Fix static selinux build.
+
+Version 1.00.13 - 6 Apr 2004
+============================
+  Add some basic selinux support.
+
+Version 1.00.12 - 6 Apr 2004
+============================
+  Fix dmsetup.static install.
+
+Version 1.00.11 - 5 Apr 2004
+============================
+  configure --enable-static_link does static build in addition to dynamic.
+  Moved Makefile library targets definition into template.
+
+Version 1.00.10 - 2 Apr 2004
+============================
+  Fix DESTDIR handling.
+  Static build installs to dmsetup.static.
+  Basic support for internationalisation.
+  Minor Makefile tidy-ups/fixes.
+
+Version 1.00.09 - 31 Mar 2004
+=============================
+  Update copyright notices to Red Hat.
+  Move full mknodes functionality from dmsetup into libdevmapper.
+  Avoid sscanf %as for uClibc compatibility.
+  Cope if DM_LIST_VERSIONS is not defined.
+  Add DM_LIST_VERSIONS functionality to kernel patches.
+  Generate new kernel patches for 2.4.26-rc1.
+
+Version 1.00.08 - 27 Feb 2004
+=============================
+  Added 'dmsetup targets'.
+  Added event_nr support to 'dmsetup wait'.
+  Updated dmsetup man page.
+  Allow logging function to be reset to use internal one.
+  Bring log macros in line with LVM2 ones.
+  Added 'make install_static_lib' which installs libdevmapper.a.
+  Made configure/makefiles closer to LVM2 versions.
+  Fixed DESTDIR for make install/install_static_lib.
+  Updated README/INSTALL to reflect move to sources.redhat.com.
+  Updated autoconf files to 2003-06-17.
diff --git a/autoconf/config.guess b/autoconf/config.guess
new file mode 100755 (executable)
index 0000000..f32079a
--- /dev/null
@@ -0,0 +1,1526 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+#   Free Software Foundation, Inc.
+
+timestamp='2008-01-23'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           sh5el) machine=sh5le-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep __ELF__ >/dev/null
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+       exit ;;
+    *:ekkoBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       exit ;;
+    *:SolidBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+       exit ;;
+    macppc:MirBSD:*:*)
+       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    *:MirBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    alpha:OSF1:*:*)
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit ;;
+    *:z/VM:*:*)
+       echo s390-ibm-zvmoe
+       exit ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+       exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+       echo arm-unknown-riscos
+       exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit ;;
+    DRS?6000:unix:4.0:6*)
+       echo sparc-icl-nx6
+       exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7; exit ;;
+       esac ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit ;;
+    m68k:machten:*:*)
+       echo m68k-apple-machten${UNAME_RELEASE}
+       exit ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c &&
+         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`$dummy $dummyarg` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+               then
+                       echo "$SYSTEM_NAME"
+               else
+                       echo rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit ;;
+    *:AIX:*:[456])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           eval $set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep __LP64__ >/dev/null
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       echo unknown-hitachi-hiuxwe2
+       exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    *:UNICOS/mp:*:*)
+       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:FreeBSD:*:*)
+       case ${UNAME_MACHINE} in
+           pc98)
+               echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           amd64)
+               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           *)
+               echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+       esac
+       exit ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit ;;
+    *:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit ;;
+    i*:windows32*:*)
+       # uname -m includes "-pc" on this system.
+       echo ${UNAME_MACHINE}-mingw32
+       exit ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit ;;
+    *:Interix*:[3456]*)
+       case ${UNAME_MACHINE} in
+           x86)
+               echo i586-pc-interix${UNAME_RELEASE}
+               exit ;;
+           EM64T | authenticamd)
+               echo x86_64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+           IA64)
+               echo ia64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+       esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       echo x86_64-unknown-cygwin
+       exit ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    *:GNU:*:*)
+       # the GNU system
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+       exit ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit ;;
+    arm*:Linux:*:*)
+       eval $set_cc_for_build
+       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_EABI__
+       then
+           echo ${UNAME_MACHINE}-unknown-linux-gnu
+       else
+           echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+       fi
+       exit ;;
+    avr32*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    cris:Linux:*:*)
+       echo cris-axis-linux-gnu
+       exit ;;
+    crisv32:Linux:*:*)
+       echo crisv32-axis-linux-gnu
+       exit ;;
+    frv:Linux:*:*)
+       echo frv-unknown-linux-gnu
+       exit ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m32r*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    mips:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips
+       #undef mipsel
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mipsel
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+           /^CPU/{
+               s: ::g
+               p
+           }'`"
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips64
+       #undef mips64el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mips64el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips64
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+           /^CPU/{
+               s: ::g
+               p
+           }'`"
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    or32:Linux:*:*)
+       echo or32-unknown-linux-gnu
+       exit ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit ;;
+    sh64*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    vax:Linux:*:*)
+       echo ${UNAME_MACHINE}-dec-linux-gnu
+       exit ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-gnu
+       exit ;;
+    xtensa*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    i*86:Linux:*:*)
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       # Set LC_ALL=C to ensure ld outputs messages in English.
+       ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+                        | sed -ne '/supported targets:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported targets: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_targets" in
+         elf32-i386)
+               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+               ;;
+         a.out-i386-linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit ;;
+         coff-i386)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit ;;
+         "")
+               # Either a pre-BFD a.out linker (linux-gnuoldld) or
+               # one that does not give us useful --help.
+               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+               exit ;;
+       esac
+       # Determine whether the default compiler is a.out or elf
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <features.h>
+       #ifdef __ELF__
+       # ifdef __GLIBC__
+       #  if __GLIBC__ >= 2
+       LIBC=gnu
+       #  else
+       LIBC=gnulibc1
+       #  endif
+       # else
+       LIBC=gnulibc1
+       # endif
+       #else
+       #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+       LIBC=gnu
+       #else
+       LIBC=gnuaout
+       #endif
+       #endif
+       #ifdef __dietlibc__
+       LIBC=dietlibc
+       #endif
+EOF
+       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+           /^LIBC/{
+               s: ::g
+               p
+           }'`"
+       test x"${LIBC}" != x && {
+               echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+               exit
+       }
+       test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit ;;
+    i*86:syllable:*:*)
+       echo ${UNAME_MACHINE}-pc-syllable
+       exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && { echo i486-ncr-sysv4; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo ${UNAME_MACHINE}-stratus-vos
+       exit ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-7:SUPER-UX:*:*)
+       echo sx7-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8:SUPER-UX:*:*)
+       echo sx8-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8R:SUPER-UX:*:*)
+       echo sx8r-nec-superux${UNAME_RELEASE}
+       exit ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+       case $UNAME_PROCESSOR in
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+       echo nse-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+       exit ;;
+    *:DragonFly:*:*)
+       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case "${UNAME_MACHINE}" in
+           A*) echo alpha-dec-vms ; exit ;;
+           I*) echo ia64-dec-vms ; exit ;;
+           V*) echo vax-dec-vms ; exit ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       echo i386-pc-xenix
+       exit ;;
+    i*86:skyos:*:*)
+       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+       exit ;;
+    i*86:rdos:*:*)
+       echo ${UNAME_MACHINE}-pc-rdos
+       exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    c34*)
+       echo c34-convex-bsd
+       exit ;;
+    c38*)
+       echo c38-convex-bsd
+       exit ;;
+    c4*)
+       echo c4-convex-bsd
+       exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/autoconf/config.sub b/autoconf/config.sub
new file mode 100755 (executable)
index 0000000..6759825
--- /dev/null
@@ -0,0 +1,1658 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+#   Free Software Foundation, Inc.
+
+timestamp='2008-01-16'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+  uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+  storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis | -knuth | -cray)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco6)
+               os=-sco5v6
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5v6*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | am33_2.0 \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+       | bfin \
+       | c4x | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | fido | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k | iq2000 \
+       | m32c | m32r | m32rle | m68000 | m68k | m88k \
+       | maxq | mb | microblaze | mcore | mep \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64vr | mips64vrel \
+       | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mips64vr5900 | mips64vr5900el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | mt \
+       | msp430 \
+       | nios | nios2 \
+       | ns16k | ns32k \
+       | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | score \
+       | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+       | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+       | spu | strongarm \
+       | tahoe | thumb | tic4x | tic80 | tron \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+       | z8k)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+       ms1)
+               basic_machine=mt-unknown
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* | avr32-* \
+       | bfin-* | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+       | clipper-* | craynv-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* | iq2000-* \
+       | m32c-* | m32r-* | m32rle-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | maxq-* | mcore-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mips64vr5900-* | mips64vr5900el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | mmix-* \
+       | mt-* \
+       | msp430-* \
+       | nios-* | nios2-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+       | sparclite-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+       | tron-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+       | xstormy16-* | xtensa*-* \
+       | ymp-* \
+       | z8k-*)
+               ;;
+       # Recognize the basic CPU types without company name, with glob match.
+       xtensa*)
+               basic_machine=$basic_machine-unknown
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       abacus)
+               basic_machine=abacus-unknown
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amd64)
+               basic_machine=x86_64-pc
+               ;;
+       amd64-*)
+               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       blackfin)
+               basic_machine=bfin-unknown
+               os=-linux
+               ;;
+       blackfin-*)
+               basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       craynv)
+               basic_machine=craynv-cray
+               os=-unicosmp
+               ;;
+       cr16)
+               basic_machine=cr16-unknown
+               os=-elf
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       crisv32 | crisv32-* | etraxfs*)
+               basic_machine=crisv32-axis
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       crx)
+               basic_machine=crx-unknown
+               os=-elf
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       djgpp)
+               basic_machine=i586-pc
+               os=-msdosdjgpp
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m68knommu)
+               basic_machine=m68k-unknown
+               os=-linux
+               ;;
+       m68knommu-*)
+               basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       mingw32ce)
+               basic_machine=arm-unknown
+               os=-mingw32ce
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       ms1-*)
+               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       openrisc | openrisc-*)
+               basic_machine=or32-unknown
+               ;;
+       os400)
+               basic_machine=powerpc-ibm
+               os=-os400
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       parisc)
+               basic_machine=hppa-unknown
+               os=-linux
+               ;;
+       parisc-*)
+               basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pc98)
+               basic_machine=i386-pc
+               ;;
+       pc98-*)
+               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2 | pentiumiii | pentium3)
+               basic_machine=i686-pc
+               ;;
+       pentium4)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium4-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rdos)
+               basic_machine=i386-pc
+               os=-rdos
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sde)
+               basic_machine=mipsisa32-sde
+               os=-elf
+               ;;
+       sei)
+               basic_machine=mips-sei
+               os=-seiux
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sh5el)
+               basic_machine=sh5le-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tic55x | c55x*)
+               basic_machine=tic55x-unknown
+               os=-coff
+               ;;
+       tic6x | c6x*)
+               basic_machine=tic6x-unknown
+               os=-coff
+               ;;
+       tile*)
+               basic_machine=tile-unknown
+               os=-linux-gnu
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       tpf)
+               basic_machine=s390x-ibm
+               os=-tpf
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xbox)
+               basic_machine=i686-pc
+               os=-mingw32
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       mmix)
+               basic_machine=mmix-knuth
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+             | -openbsd* | -solidbsd* \
+             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+             | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+             | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux-dietlibc)
+               os=-linux-dietlibc
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+        -os400*)
+               os=-os400
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -syllable*)
+               os=-syllable
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+        -tpf*)
+               os=-tpf
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -zvmoe)
+               os=-zvmoe
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+        score-*)
+               os=-elf
+               ;;
+        spu-*)
+               os=-elf
+               ;;
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+        c4x-* | tic4x-*)
+               os=-coff
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+        mep-*)
+               os=-elf
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-haiku)
+               os=-haiku
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-knuth)
+               os=-mmixware
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -os400*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -tpf*)
+                               vendor=ibm
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/autoconf/install-sh b/autoconf/install-sh
new file mode 100755 (executable)
index 0000000..4fbbae7
--- /dev/null
@@ -0,0 +1,507 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2006-10-14.15
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" ""       $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+if test -z "$doit"; then
+  doit_exec=exec
+else
+  doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+posix_glob=
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chmodcmd=$chmodprog
+chowncmd=
+chgrpcmd=
+stripcmd=
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=
+dst=
+dir_arg=
+dstarg=
+no_target_directory=
+
+usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+-c         (ignored)
+-d         create directories instead of installing files.
+-g GROUP   $chgrpprog installed files to GROUP.
+-m MODE    $chmodprog installed files to MODE.
+-o USER    $chownprog installed files to USER.
+-s         $stripprog installed files.
+-t DIRECTORY  install into DIRECTORY.
+-T         report an error if DSTFILE is a directory.
+--help     display this help and exit.
+--version  display version info and exit.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) shift
+        continue;;
+
+    -d) dir_arg=true
+        shift
+        continue;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift
+        shift
+        continue;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        shift
+        shift
+       case $mode in
+         *' '* | *'    '* | *'
+'*       | *'*'* | *'?'* | *'['*)
+           echo "$0: invalid mode: $mode" >&2
+           exit 1;;
+       esac
+        continue;;
+
+    -o) chowncmd="$chownprog $2"
+        shift
+        shift
+        continue;;
+
+    -s) stripcmd=$stripprog
+        shift
+        continue;;
+
+    -t) dstarg=$2
+       shift
+       shift
+       continue;;
+
+    -T) no_target_directory=true
+       shift
+       continue;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --)        shift
+       break;;
+
+    -*)        echo "$0: invalid option: $1" >&2
+       exit 1;;
+
+    *)  break;;
+  esac
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dstarg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dstarg"
+      shift # fnord
+    fi
+    shift # arg
+    dstarg=$arg
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call `install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  trap '(exit $?); exit' 1 2 13 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+       u_plus_rw=
+      else
+       u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+       u_plus_rw=
+      else
+       u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names starting with `-'.
+  case $src in
+    -*) src=./$src ;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dstarg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+
+    dst=$dstarg
+    # Protect names starting with `-'.
+    case $dst in
+      -*) dst=./$dst ;;
+    esac
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test -n "$no_target_directory"; then
+       echo "$0: $dstarg: Is a directory" >&2
+       exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      # Prefer dirname, but fall back on a substitute if dirname fails.
+      dstdir=`
+       (dirname "$dst") 2>/dev/null ||
+       expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+            X"$dst" : 'X\(//\)[^/]' \| \
+            X"$dst" : 'X\(//\)$' \| \
+            X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+       echo X"$dst" |
+           sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\/\)[^/].*/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\/\)$/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\).*/{
+                  s//\1/
+                  q
+                }
+                s/.*/./; q'
+      `
+
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+       # Create intermediate dirs using mode 755 as modified by the umask.
+       # This is like FreeBSD 'install' as of 1997-10-28.
+       umask=`umask`
+       case $stripcmd.$umask in
+         # Optimize common cases.
+         *[2367][2367]) mkdir_umask=$umask;;
+         .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+         *[0-7])
+           mkdir_umask=`expr $umask + 22 \
+             - $umask % 100 % 40 + $umask % 20 \
+             - $umask % 10 % 4 + $umask % 2
+           `;;
+         *) mkdir_umask=$umask,go-w;;
+       esac
+
+       # With -d, create the new directory with the user-specified mode.
+       # Otherwise, rely on $mkdir_umask.
+       if test -n "$dir_arg"; then
+         mkdir_mode=-m$mode
+       else
+         mkdir_mode=
+       fi
+
+       posix_mkdir=false
+       case $umask in
+         *[123567][0-7][0-7])
+           # POSIX mkdir -p sets u+wx bits regardless of umask, which
+           # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+           ;;
+         *)
+           tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+           trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+           if (umask $mkdir_umask &&
+               exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+           then
+             if test -z "$dir_arg" || {
+                  # Check for POSIX incompatibilities with -m.
+                  # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                  # other-writeable bit of parent directory when it shouldn't.
+                  # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                  ls_ld_tmpdir=`ls -ld "$tmpdir"`
+                  case $ls_ld_tmpdir in
+                    d????-?r-*) different_mode=700;;
+                    d????-?--*) different_mode=755;;
+                    *) false;;
+                  esac &&
+                  $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+                    ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+                    test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                  }
+                }
+             then posix_mkdir=:
+             fi
+             rmdir "$tmpdir/d" "$tmpdir"
+           else
+             # Remove any dirs left behind by ancient mkdir implementations.
+             rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+           fi
+           trap '' 0;;
+       esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+       umask $mkdir_umask &&
+       $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+       /*) prefix=/ ;;
+       -*) prefix=./ ;;
+       *)  prefix= ;;
+      esac
+
+      case $posix_glob in
+        '')
+         if (set -f) 2>/dev/null; then
+           posix_glob=true
+         else
+           posix_glob=false
+         fi ;;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      $posix_glob && set -f
+      set fnord $dstdir
+      shift
+      $posix_glob && set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+       test -z "$d" && continue
+
+       prefix=$prefix$d
+       if test -d "$prefix"; then
+         prefixes=
+       else
+         if $posix_mkdir; then
+           (umask=$mkdir_umask &&
+            $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+           # Don't fail if two instances are running concurrently.
+           test -d "$prefix" || exit 1
+         else
+           case $prefix in
+             *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+             *) qprefix=$prefix;;
+           esac
+           prefixes="$prefixes '$qprefix'"
+         fi
+       fi
+       prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+       # Don't fail if two instances are running concurrently.
+       (umask $mkdir_umask &&
+        eval "\$doit_exec \$mkdirprog $prefixes") ||
+         test -d "$dstdir" || exit 1
+       obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
+      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
+      && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
+      && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # Now rename the file to the real destination.
+    { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \
+      || {
+          # The rename failed, perhaps because mv can't rename something else
+          # to itself, or perhaps because mv is so ancient that it does not
+          # support -f.
+
+          # Now remove or move aside any old file at destination location.
+          # We try this two ways since rm can't unlink itself on some
+          # systems and the destination file might be busy for other
+          # reasons.  In this case, the final cleanup might fail but the new
+          # file should still install successfully.
+          {
+            if test -f "$dst"; then
+              $doit $rmcmd -f "$dst" 2>/dev/null \
+              || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
+                    && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
+              || {
+                echo "$0: cannot unlink or rename $dst" >&2
+                (exit 1); exit 1
+              }
+            else
+              :
+            fi
+          } &&
+
+          # Now rename the file to the real destination.
+          $doit $mvcmd "$dsttmp" "$dst"
+        }
+    } || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..ef343f5
--- /dev/null
+++ b/configure
@@ -0,0 +1,19637 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.63.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+  if (eval ":") 2>/dev/null; then
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+
+  if test $as_have_required = yes &&    (eval ":
+(as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=\$LINENO
+  as_lineno_2=\$LINENO
+  test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+  test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+  :
+else
+  as_candidate_shells=
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  case $as_dir in
+        /*)
+          for as_base in sh bash ksh sh5; do
+            as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+          done;;
+       esac
+done
+IFS=$as_save_IFS
+
+
+      for as_shell in $as_candidate_shells $SHELL; do
+        # Try only shells that exist, to save several forks.
+        if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+               { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+  CONFIG_SHELL=$as_shell
+              as_have_required=yes
+              if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+  (exit $1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+  break
+fi
+
+fi
+
+      done
+
+      if test "x$CONFIG_SHELL" != x; then
+  for as_var in BASH_ENV ENV
+       do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+       done
+       export CONFIG_SHELL
+       exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+    if test $as_have_required = no; then
+  echo This script requires a shell more modern than all the
+      echo shells that I found on your system.  Please install a
+      echo modern shell, or manually run the script under such a
+      echo shell if you do have one.
+      { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+  (exit \$1)
+}
+as_func_success () {
+  as_func_return 0
+}
+as_func_failure () {
+  as_func_return 1
+}
+as_func_ret_success () {
+  return 0
+}
+as_func_ret_failure () {
+  return 1
+}
+
+exitcode=0
+if as_func_success; then
+  :
+else
+  exitcode=1
+  echo as_func_success failed.
+fi
+
+if as_func_failure; then
+  exitcode=1
+  echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+  :
+else
+  exitcode=1
+  echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+  exitcode=1
+  echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+  :
+else
+  exitcode=1
+  echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+  echo No shell found that supports shell functions.
+  echo Please tell bug-autoconf@gnu.org about your system,
+  echo including any error possibly output before this message.
+  echo This can help us improve future autoconf versions.
+  echo Configuration will now proceed without shell functions.
+}
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -p'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -p'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -p'
+  fi
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+       test -d "$1/.";
+      else
+       case $1 in
+       -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="lib/device/dev-cache.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_default_prefix=/usr
+ac_subst_vars='LTLIBOBJS
+usrsbindir
+usrlibdir
+udevdir
+udev_prefix
+tmpdir
+kernelvsn
+missingkernel
+kerneldir
+interface
+WRITE_INSTALL
+UDEV_SYNC
+UDEV_RULES
+UDEV_PC
+UDEV_LIBS
+TESTING
+STATIC_LINK
+STATICDIR
+SNAPSHOTS
+SELINUX_PC
+SELINUX_LIBS
+READLINE_LIBS
+PTHREAD_LIBS
+POOL
+PKGCONFIG
+REPLICATORS
+OCF
+MIRRORS
+LVM_RELEASE_DATE
+LVM_RELEASE
+LVM_PATCHLEVEL
+LVM_MINOR
+LVM_MAJOR
+LVM_LIBAPI
+LVM_VERSION
+LVM1_FALLBACK
+LVM1
+LOCALEDIR
+LIB_SUFFIX
+LDDEPS
+JOBS
+INTL_PACKAGE
+INTL
+HAVE_REALTIME
+HAVE_LIBDL
+FSADM
+DM_LIB_PATCHLEVEL
+DM_LIB_VERSION
+DM_IOCTLS
+DM_DEVICE_UID
+DM_DEVICE_MODE
+DM_DEVICE_GID
+DM_COMPAT
+DMEVENTD_PATH
+DMEVENTD
+DL_LIBS
+DEVMAPPER
+DEFAULT_RUN_DIR
+DEFAULT_LOCK_DIR
+DEFAULT_DATA_ALIGNMENT
+DEFAULT_CACHE_SUBDIR
+DEFAULT_BACKUP_SUBDIR
+DEFAULT_ARCHIVE_SUBDIR
+DEFAULT_SYS_DIR
+DEBUG
+COPTIMISE_FLAG
+CONFDIR
+CMDLIB
+CLVMD_CMANAGERS
+CLVMD
+CLUSTER
+CLDWHOLEARCHIVE
+CLDNOWHOLEARCHIVE
+CLDFLAGS
+BUILD_DMEVENTD
+BUILD_CMIRRORD
+APPLIB
+MODPROBE_CMD
+MSGFMT
+LVM2CMD_LIB
+LVM2APP_LIB
+VALGRIND
+RUBY19
+GENPNG
+GENHTML
+LCOV
+SACKPT_LIBS
+SACKPT_CFLAGS
+DLM_LIBS
+DLM_CFLAGS
+CPG_LIBS
+CPG_CFLAGS
+CONFDB_LIBS
+CONFDB_CFLAGS
+SALCK_LIBS
+SALCK_CFLAGS
+QUORUM_LIBS
+QUORUM_CFLAGS
+COROSYNC_LIBS
+COROSYNC_CFLAGS
+CMAN_LIBS
+CMAN_CFLAGS
+GULM_LIBS
+GULM_CFLAGS
+CCS_LIBS
+CCS_CFLAGS
+PKGCONFIGINIT_LIBS
+PKGCONFIGINIT_CFLAGS
+PKG_CONFIG
+POW_LIB
+LIBOBJS
+ALLOCA
+CSCOPE_CMD
+CFLOW_CMD
+RANLIB
+MKDIR_P
+SET_MAKE
+LN_S
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+EGREP
+GREP
+CPP
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+AWK
+SED
+target_os
+target_vendor
+target_cpu
+target
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_static_link
+with_user
+with_group
+with_device_uid
+with_device_gid
+with_device_mode
+enable_lvm1_fallback
+with_lvm1
+with_pool
+with_cluster
+with_snapshots
+with_mirrors
+with_replicators
+enable_readline
+enable_realtime
+enable_ocf
+with_clvmd
+with_clvmd_pidfile
+enable_cmirrord
+with_cmirrord_pidfile
+enable_debug
+with_optimisation
+enable_profiling
+enable_testing
+enable_valgrind_pool
+enable_devmapper
+enable_udev_sync
+enable_udev_rules
+enable_compat
+enable_units_compat
+enable_ioctl
+enable_o_direct
+enable_applib
+enable_cmdlib
+enable_pkgconfig
+enable_write_install
+enable_fsadm
+enable_dmeventd
+enable_selinux
+enable_nls
+with_localedir
+with_confdir
+with_staticdir
+with_usrlibdir
+with_usrsbindir
+with_udev_prefix
+with_udevdir
+with_dmeventd_pidfile
+with_dmeventd_path
+with_default_run_dir
+with_default_system_dir
+with_default_archive_subdir
+with_default_backup_subdir
+with_default_cache_subdir
+with_default_locking_dir
+with_default_data_alignment
+with_interface
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+PKG_CONFIG
+PKGCONFIGINIT_CFLAGS
+PKGCONFIGINIT_LIBS
+CCS_CFLAGS
+CCS_LIBS
+GULM_CFLAGS
+GULM_LIBS
+CMAN_CFLAGS
+CMAN_LIBS
+COROSYNC_CFLAGS
+COROSYNC_LIBS
+QUORUM_CFLAGS
+QUORUM_LIBS
+SALCK_CFLAGS
+SALCK_LIBS
+CONFDB_CFLAGS
+CONFDB_LIBS
+CPG_CFLAGS
+CPG_LIBS
+DLM_CFLAGS
+DLM_LIBS
+SACKPT_CFLAGS
+SACKPT_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *)   ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+   { (exit 1); exit 1; }; }
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { $as_echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { $as_echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
+   { (exit 1); exit 1; }; } ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  { $as_echo "$as_me: error: working directory cannot be determined" >&2
+   { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  { $as_echo "$as_me: error: pwd does not report name of working directory" >&2
+   { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2
+   { (exit 1); exit 1; }; }
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/PACKAGE]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+  --target=TARGET   configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-static_link    use this to link the tools to their libraries
+                          statically (default is dynamic linking
+  --enable-lvm1_fallback  use this to fall back and use LVM1 binaries if
+                          device-mapper is missing from the kernel
+  --disable-readline      disable readline support
+  --enable-realtime       enable realtime clock support
+  --enable-ocf            enable Open Cluster Framework (OCF) compliant
+                          resource agents
+  --enable-cmirrord       enable the cluster mirror log daemon
+  --enable-debug          enable debugging
+  --enable-profiling      gather gcov profiling data
+  --enable-testing        enable testing targets in the makefile
+  --enable-valgrind-pool  enable valgrind awareness of pools
+  --disable-devmapper     disable LVM2 device-mapper interaction
+  --enable-udev_sync      enable synchronisation with udev processing
+  --enable-udev_rules     install rule files needed for udev synchronisation
+  --enable-compat         enable support for old device-mapper versions
+  --enable-units-compat   enable output compatibility with old versions that
+                          that do not use KiB-style unit suffixes
+  --disable-driver        disable calls to device-mapper in the kernel
+  --disable-o_direct      disable O_DIRECT
+  --enable-applib         build application library
+  --enable-cmdlib         build shared command library
+  --enable-pkgconfig      install pkgconfig support
+  --enable-write_install  install user writable files
+  --disable-fsadm         disable fsadm
+  --enable-dmeventd       enable the device-mapper event daemon
+  --disable-selinux       disable selinux support
+  --enable-nls            enable Native Language Support
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-user=USER        set the owner of installed files [USER=]
+  --with-group=GROUP      set the group owner of installed files [GROUP=]
+  --with-device-uid=UID   set the owner used for new device nodes [UID=0]
+  --with-device-gid=GID   set the group used for new device nodes [GID=0]
+  --with-device-mode=MODE set the mode used for new device nodes [MODE=0600]
+  --with-lvm1=TYPE        LVM1 metadata support: internal/shared/none
+                          [TYPE=internal]
+  --with-pool=TYPE        GFS pool read-only support: internal/shared/none
+                          [TYPE=internal]
+  --with-cluster=TYPE     cluster LVM locking support: internal/shared/none
+                          [TYPE=internal]
+  --with-snapshots=TYPE   snapshot support: internal/shared/none
+                          [TYPE=internal]
+  --with-mirrors=TYPE     mirror support: internal/shared/none
+                          [TYPE=internal]
+  --with-replicators=TYPE replicator support: internal/shared/none
+                          [TYPE=none]
+  --with-clvmd=TYPE       build cluster LVM Daemon
+                          The following cluster manager combinations are valid:
+                           * cman,gulm             (RHEL4 or equivalent)
+                           * cman                  (RHEL5 or equivalent)
+                           * cman,corosync,openais (or selection of them)
+                           * singlenode            (localhost only)
+                           * all                   (autodetect)
+                           * none                  (disable build)
+                          [TYPE=none]
+  --with-clvmd-pidfile=PATH
+                          clvmd pidfile [/var/run/clvmd.pid]
+  --with-cmirrord-pidfile=PATH
+                          cmirrord pidfile [/var/run/cmirrord.pid]
+  --with-optimisation=OPT C optimisation flag [OPT=-O2]
+  --with-localedir=DIR    translation files in DIR [PREFIX/share/locale]
+  --with-confdir=DIR      configuration files in DIR [/etc]
+  --with-staticdir=DIR    static binaries in DIR [EPREFIX/sbin]
+  --with-usrlibdir=DIR    usrlib in DIR [PREFIX/lib]
+  --with-usrsbindir=DIR   usrsbin executables in DIR [PREFIX/sbin]
+  --with-udev-prefix=UPREFIX
+                          install udev rule files in UPREFIX [EPREFIX]
+  --with-udevdir=DIR      udev rules in DIR [UPREFIX/lib/udev/rules.d]
+  --with-dmeventd-pidfile=PATH
+                          dmeventd pidfile [/var/run/dmeventd.pid]
+  --with-dmeventd-path=PATH
+                          dmeventd path [EPREFIX/sbin/dmeventd]
+  --with-default-run-dir=DIR       Default run directory [/var/run/lvm]
+  --with-default-system-dir=DIR
+                          default LVM system directory [/etc/lvm]
+  --with-default-archive-subdir=SUBDIR
+                          default metadata archive subdir [archive]
+  --with-default-backup-subdir=SUBDIR
+                          default metadata backup subdir [backup]
+  --with-default-cache-subdir=SUBDIR
+                          default metadata cache subdir [cache]
+  --with-default-locking-dir=DIR
+                          default locking directory [/var/lock/lvm]
+  --with-default-data-alignment=NUM
+                          set the default data alignment in MiB [1]
+  --with-interface=IFACE  choose kernel interface (ioctl) [ioctl]
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  PKG_CONFIG  path to pkg-config utility
+  PKGCONFIGINIT_CFLAGS
+              C compiler flags for PKGCONFIGINIT, overriding pkg-config
+  PKGCONFIGINIT_LIBS
+              linker flags for PKGCONFIGINIT, overriding pkg-config
+  CCS_CFLAGS  C compiler flags for CCS, overriding pkg-config
+  CCS_LIBS    linker flags for CCS, overriding pkg-config
+  GULM_CFLAGS C compiler flags for GULM, overriding pkg-config
+  GULM_LIBS   linker flags for GULM, overriding pkg-config
+  CMAN_CFLAGS C compiler flags for CMAN, overriding pkg-config
+  CMAN_LIBS   linker flags for CMAN, overriding pkg-config
+  COROSYNC_CFLAGS
+              C compiler flags for COROSYNC, overriding pkg-config
+  COROSYNC_LIBS
+              linker flags for COROSYNC, overriding pkg-config
+  QUORUM_CFLAGS
+              C compiler flags for QUORUM, overriding pkg-config
+  QUORUM_LIBS linker flags for QUORUM, overriding pkg-config
+  SALCK_CFLAGS
+              C compiler flags for SALCK, overriding pkg-config
+  SALCK_LIBS  linker flags for SALCK, overriding pkg-config
+  CONFDB_CFLAGS
+              C compiler flags for CONFDB, overriding pkg-config
+  CONFDB_LIBS linker flags for CONFDB, overriding pkg-config
+  CPG_CFLAGS  C compiler flags for CPG, overriding pkg-config
+  CPG_LIBS    linker flags for CPG, overriding pkg-config
+  DLM_CFLAGS  C compiler flags for DLM, overriding pkg-config
+  DLM_LIBS    linker flags for DLM, overriding pkg-config
+  SACKPT_CFLAGS
+              C compiler flags for SACKPT, overriding pkg-config
+  SACKPT_LIBS linker flags for SACKPT, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.63
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.63.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  $as_echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      ac_configure_args="$ac_configure_args '$ac_arg'"
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  ac_site_file1=$CONFIG_SITE
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test -r "$ac_site_file"; then
+    { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:$LINENO:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:$LINENO:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers lib/misc/configure.h"
+
+
+################################################################################
+ac_aux_dir=
+for ac_dir in autoconf "$srcdir"/autoconf; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in autoconf \"$srcdir\"/autoconf" >&5
+$as_echo "$as_me: error: cannot find install-sh or install.sh in autoconf \"$srcdir\"/autoconf" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+
+################################################################################
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+{ $as_echo "$as_me:$LINENO: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if test "${ac_cv_build+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+$as_echo "$as_me: error: invalid value of canonical build" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if test "${ac_cv_host+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+$as_echo "$as_me: error: invalid value of canonical host" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking target system type" >&5
+$as_echo_n "checking target system type... " >&6; }
+if test "${ac_cv_target+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$target_alias" = x; then
+  ac_cv_target=$ac_cv_host
+else
+  ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` ||
+    { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_target" >&5
+$as_echo "$ac_cv_target" >&6; }
+case $ac_cv_target in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical target" >&5
+$as_echo "$as_me: error: invalid value of canonical target" >&2;}
+   { (exit 1); exit 1; }; };;
+esac
+target=$ac_cv_target
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_target
+shift
+target_cpu=$1
+target_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+target_os=$*
+IFS=$ac_save_IFS
+case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+
+case "$host_os" in
+       linux*)
+               CFLAGS="$CFLAGS"
+               COPTIMISE_FLAG="-O2"
+               CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
+               CLDWHOLEARCHIVE="-Wl,-whole-archive"
+               CLDNOWHOLEARCHIVE="-Wl,-no-whole-archive"
+               LDDEPS="$LDDEPS .export.sym"
+               LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+               LIB_SUFFIX=so
+               DEVMAPPER=yes
+               ODIRECT=yes
+               DM_IOCTLS=yes
+               SELINUX=yes
+               CLUSTER=internal
+               FSADM=yes
+               ;;
+       darwin*)
+               CFLAGS="$CFLAGS -no-cpp-precomp -fno-common"
+               COPTIMISE_FLAG="-O2"
+               CLDFLAGS="$CLDFLAGS"
+               CLDWHOLEARCHIVE="-all_load"
+               CLDNOWHOLEARCHIVE=
+               LIB_SUFFIX=dylib
+               DEVMAPPER=yes
+               ODIRECT=no
+               DM_IOCTLS=no
+               SELINUX=no
+               CLUSTER=none
+               FSADM=no
+               ;;
+esac
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if test "${ac_cv_path_SED+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     $as_unset ac_script || ac_script=
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_SED_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_SED"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable sed could be found in \$PATH" >&5
+$as_echo "$as_me: error: no acceptable sed could be found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+  rm -f conftest.sed
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_AWK="$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { $as_echo "$as_me:$LINENO: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler --version >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler -v >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compiler -V >&5") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+        if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+if test -z "$ac_file"; then
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_compiler_gnu=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       CFLAGS=""
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_g=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_prog_cc_c89=$ac_arg
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:$LINENO: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:$LINENO: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  # Broken: success on invalid input.
+continue
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    ac_count=`expr $ac_count + 1`
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+if test $ac_cv_c_compiler_gnu = yes; then
+    { $as_echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5
+$as_echo_n "checking whether $CC needs -traditional... " >&6; }
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+    ac_pattern="Autoconf.*'x'"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+else
+  ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+  if test $ac_cv_prog_gcc_traditional = no; then
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5
+$as_echo "$ac_cv_prog_gcc_traditional" >&6; }
+  if test $ac_cv_prog_gcc_traditional = yes; then
+    CC="$CC -traditional"
+  fi
+fi
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           rm -rf conftest.one conftest.two conftest.dir
+           echo one > conftest.one
+           echo two > conftest.two
+           mkdir conftest.dir
+           if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+             test -s conftest.one && test -s conftest.two &&
+             test -s conftest.dir/conftest.one &&
+             test -s conftest.dir/conftest.two
+           then
+             ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+             break 3
+           fi
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+       @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if test "${ac_cv_path_mkdir+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in mkdir gmkdir; do
+        for ac_exec_ext in '' $ac_executable_extensions; do
+          { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+          case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+            'mkdir (GNU coreutils) '* | \
+            'mkdir (coreutils) '* | \
+            'mkdir (fileutils) '4.1*)
+              ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+              break 3;;
+          esac
+        done
+       done
+done
+IFS=$as_save_IFS
+
+fi
+
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    test -d ./--version && rmdir ./--version
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:$LINENO: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+# Extract the first word of "cflow", so it can be a program name with args.
+set dummy cflow; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_CFLOW_CMD+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $CFLOW_CMD in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_CFLOW_CMD="$CFLOW_CMD" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_CFLOW_CMD="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+CFLOW_CMD=$ac_cv_path_CFLOW_CMD
+if test -n "$CFLOW_CMD"; then
+  { $as_echo "$as_me:$LINENO: result: $CFLOW_CMD" >&5
+$as_echo "$CFLOW_CMD" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "cscope", so it can be a program name with args.
+set dummy cscope; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_CSCOPE_CMD+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $CSCOPE_CMD in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_CSCOPE_CMD="$CSCOPE_CMD" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_CSCOPE_CMD="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+CSCOPE_CMD=$ac_cv_path_CSCOPE_CMD
+if test -n "$CSCOPE_CMD"; then
+  { $as_echo "$as_me:$LINENO: result: $CSCOPE_CMD" >&5
+$as_echo "$CSCOPE_CMD" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+################################################################################
+
+
+
+
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+  as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+  { $as_echo "$as_me:$LINENO: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if test "${ac_cv_search_opendir+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_search_opendir=$ac_res
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_opendir+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_opendir+set}" = set; then
+  :
+else
+  ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+  { $as_echo "$as_me:$LINENO: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if test "${ac_cv_search_opendir+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_search_opendir=$ac_res
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_opendir+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_opendir+set}" = set; then
+  :
+else
+  ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_stdc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  eval "$as_ac_Header=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+{ $as_echo "$as_me:$LINENO: checking whether sys/types.h defines makedev" >&5
+$as_echo_n "checking whether sys/types.h defines makedev... " >&6; }
+if test "${ac_cv_header_sys_types_h_makedev+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+int
+main ()
+{
+return makedev(0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_header_sys_types_h_makedev=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_sys_types_h_makedev=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_types_h_makedev" >&5
+$as_echo "$ac_cv_header_sys_types_h_makedev" >&6; }
+
+if test $ac_cv_header_sys_types_h_makedev = no; then
+if test "${ac_cv_header_sys_mkdev_h+set}" = set; then
+  { $as_echo "$as_me:$LINENO: checking for sys/mkdev.h" >&5
+$as_echo_n "checking for sys/mkdev.h... " >&6; }
+if test "${ac_cv_header_sys_mkdev_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_mkdev_h" >&5
+$as_echo "$ac_cv_header_sys_mkdev_h" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking sys/mkdev.h usability" >&5
+$as_echo_n "checking sys/mkdev.h usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <sys/mkdev.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking sys/mkdev.h presence" >&5
+$as_echo_n "checking sys/mkdev.h presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/mkdev.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for sys/mkdev.h" >&5
+$as_echo_n "checking for sys/mkdev.h... " >&6; }
+if test "${ac_cv_header_sys_mkdev_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_header_sys_mkdev_h=$ac_header_preproc
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_mkdev_h" >&5
+$as_echo "$ac_cv_header_sys_mkdev_h" >&6; }
+
+fi
+if test "x$ac_cv_header_sys_mkdev_h" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define MAJOR_IN_MKDEV 1
+_ACEOF
+
+fi
+
+
+
+  if test $ac_cv_header_sys_mkdev_h = no; then
+    if test "${ac_cv_header_sys_sysmacros_h+set}" = set; then
+  { $as_echo "$as_me:$LINENO: checking for sys/sysmacros.h" >&5
+$as_echo_n "checking for sys/sysmacros.h... " >&6; }
+if test "${ac_cv_header_sys_sysmacros_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_sysmacros_h" >&5
+$as_echo "$ac_cv_header_sys_sysmacros_h" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking sys/sysmacros.h usability" >&5
+$as_echo_n "checking sys/sysmacros.h usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <sys/sysmacros.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking sys/sysmacros.h presence" >&5
+$as_echo_n "checking sys/sysmacros.h presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/sysmacros.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for sys/sysmacros.h" >&5
+$as_echo_n "checking for sys/sysmacros.h... " >&6; }
+if test "${ac_cv_header_sys_sysmacros_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_header_sys_sysmacros_h=$ac_header_preproc
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_sysmacros_h" >&5
+$as_echo "$ac_cv_header_sys_sysmacros_h" >&6; }
+
+fi
+if test "x$ac_cv_header_sys_sysmacros_h" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define MAJOR_IN_SYSMACROS 1
+_ACEOF
+
+fi
+
+
+  fi
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_stdc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test "${ac_cv_header_sys_wait_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+  int s;
+  wait (&s);
+  s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_sys_wait_h=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_sys_wait_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_WAIT_H 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if test "${ac_cv_header_time+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_header_time=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_header_time=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TIME_WITH_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in locale.h stddef.h syslog.h sys/file.h sys/time.h assert.h \
+  langinfo.h libgen.h signal.h sys/mman.h sys/resource.h sys/utsname.h \
+  sys/wait.h time.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+case "$host_os" in
+       linux*)
+
+
+
+for ac_header in asm/byteorder.h linux/fs.h malloc.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+ ;;
+       darwin*)
+
+
+for ac_header in machine/endian.h sys/disk.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in ctype.h dirent.h errno.h fcntl.h getopt.h inttypes.h limits.h \
+  stdarg.h stdio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/stat.h \
+  sys/types.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+
+for ac_header in termios.h sys/statvfs.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
+if test "${ac_cv_c_const+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+  /* Ultrix mips cc rejects this.  */
+  typedef int charset[2];
+  const charset cs;
+  /* SunOS 4.1.1 cc rejects this.  */
+  char const *const *pcpcc;
+  char **ppc;
+  /* NEC SVR4.0.2 mips cc rejects this.  */
+  struct point {int x, y;};
+  static struct point const zero = {0,0};
+  /* AIX XL C 1.02.0.0 rejects this.
+     It does not let you subtract one const X* pointer from another in
+     an arm of an if-expression whose if-part is not a constant
+     expression */
+  const char *g = "string";
+  pcpcc = &g + (g ? g-g : 0);
+  /* HPUX 7.0 cc rejects these. */
+  ++pcpcc;
+  ppc = (char**) pcpcc;
+  pcpcc = (char const *const *) ppc;
+  { /* SCO 3.2v4 cc rejects this.  */
+    char *t;
+    char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+    *t++ = 0;
+    if (s) return 0;
+  }
+  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+    int x[] = {25, 17};
+    const int *foo = &x[0];
+    ++foo;
+  }
+  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+    typedef const int *iptr;
+    iptr p = 0;
+    ++p;
+  }
+  { /* AIX XL C 1.02.0.0 rejects this saying
+       "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+    struct s { int j; const int *ap[3]; };
+    struct s *b; b->j = 5;
+  }
+  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+    const int foo = 10;
+    if (!foo) return 0;
+  }
+  return !cs[0] && !zero.x;
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_c_const=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_c_const=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+$as_echo "$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const /**/
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for inline" >&5
+$as_echo_n "checking for inline... " >&6; }
+if test "${ac_cv_c_inline+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_c_inline=$ac_kw
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5
+$as_echo "$ac_cv_c_inline" >&6; }
+
+
+case $ac_cv_c_inline in
+  inline | yes) ;;
+  *)
+    case $ac_cv_c_inline in
+      no) ac_val=;;
+      *) ac_val=$ac_cv_c_inline;;
+    esac
+    cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+    ;;
+esac
+
+{ $as_echo "$as_me:$LINENO: checking for struct stat.st_rdev" >&5
+$as_echo_n "checking for struct stat.st_rdev... " >&6; }
+if test "${ac_cv_member_struct_stat_st_rdev+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (ac_aggr.st_rdev)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct_stat_st_rdev=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (sizeof ac_aggr.st_rdev)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct_stat_st_rdev=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_member_struct_stat_st_rdev=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_rdev" >&5
+$as_echo "$ac_cv_member_struct_stat_st_rdev" >&6; }
+if test "x$ac_cv_member_struct_stat_st_rdev" = x""yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+_ACEOF
+
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for off_t" >&5
+$as_echo_n "checking for off_t... " >&6; }
+if test "${ac_cv_type_off_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_type_off_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (off_t))
+       return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((off_t)))
+         return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_off_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5
+$as_echo "$ac_cv_type_off_t" >&6; }
+if test "x$ac_cv_type_off_t" = x""yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for pid_t" >&5
+$as_echo_n "checking for pid_t... " >&6; }
+if test "${ac_cv_type_pid_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_type_pid_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (pid_t))
+       return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((pid_t)))
+         return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_pid_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5
+$as_echo "$ac_cv_type_pid_t" >&6; }
+if test "x$ac_cv_type_pid_t" = x""yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if test "${ac_cv_type_signal+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_type_signal=int
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_signal=void
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+{ $as_echo "$as_me:$LINENO: checking for size_t" >&5
+$as_echo_n "checking for size_t... " >&6; }
+if test "${ac_cv_type_size_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_type_size_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (size_t))
+       return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((size_t)))
+         return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_size_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5
+$as_echo "$ac_cv_type_size_t" >&6; }
+if test "x$ac_cv_type_size_t" = x""yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for mode_t" >&5
+$as_echo_n "checking for mode_t... " >&6; }
+if test "${ac_cv_type_mode_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_type_mode_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (mode_t))
+       return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((mode_t)))
+         return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_mode_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_mode_t" >&5
+$as_echo "$ac_cv_type_mode_t" >&6; }
+if test "x$ac_cv_type_mode_t" = x""yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define mode_t int
+_ACEOF
+
+fi
+
+
+  { $as_echo "$as_me:$LINENO: checking for int8_t" >&5
+$as_echo_n "checking for int8_t... " >&6; }
+if test "${ac_cv_c_int8_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_int8_t=no
+     for ac_type in 'int8_t' 'int' 'long int' \
+        'long long int' 'short int' 'signed char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1)
+                < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       case $ac_type in
+  int8_t) ac_cv_c_int8_t=yes ;;
+  *) ac_cv_c_int8_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_int8_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int8_t" >&5
+$as_echo "$ac_cv_c_int8_t" >&6; }
+  case $ac_cv_c_int8_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<_ACEOF
+#define int8_t $ac_cv_c_int8_t
+_ACEOF
+;;
+  esac
+
+
+  { $as_echo "$as_me:$LINENO: checking for int16_t" >&5
+$as_echo_n "checking for int16_t... " >&6; }
+if test "${ac_cv_c_int16_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_int16_t=no
+     for ac_type in 'int16_t' 'int' 'long int' \
+        'long long int' 'short int' 'signed char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1)
+                < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       case $ac_type in
+  int16_t) ac_cv_c_int16_t=yes ;;
+  *) ac_cv_c_int16_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_int16_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int16_t" >&5
+$as_echo "$ac_cv_c_int16_t" >&6; }
+  case $ac_cv_c_int16_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<_ACEOF
+#define int16_t $ac_cv_c_int16_t
+_ACEOF
+;;
+  esac
+
+
+  { $as_echo "$as_me:$LINENO: checking for int32_t" >&5
+$as_echo_n "checking for int32_t... " >&6; }
+if test "${ac_cv_c_int32_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_int32_t=no
+     for ac_type in 'int32_t' 'int' 'long int' \
+        'long long int' 'short int' 'signed char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1)
+                < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       case $ac_type in
+  int32_t) ac_cv_c_int32_t=yes ;;
+  *) ac_cv_c_int32_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_int32_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int32_t" >&5
+$as_echo "$ac_cv_c_int32_t" >&6; }
+  case $ac_cv_c_int32_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<_ACEOF
+#define int32_t $ac_cv_c_int32_t
+_ACEOF
+;;
+  esac
+
+
+  { $as_echo "$as_me:$LINENO: checking for int64_t" >&5
+$as_echo_n "checking for int64_t... " >&6; }
+if test "${ac_cv_c_int64_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_int64_t=no
+     for ac_type in 'int64_t' 'int' 'long int' \
+        'long long int' 'short int' 'signed char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 1)
+                < ($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       case $ac_type in
+  int64_t) ac_cv_c_int64_t=yes ;;
+  *) ac_cv_c_int64_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_int64_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int64_t" >&5
+$as_echo "$ac_cv_c_int64_t" >&6; }
+  case $ac_cv_c_int64_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<_ACEOF
+#define int64_t $ac_cv_c_int64_t
+_ACEOF
+;;
+  esac
+
+{ $as_echo "$as_me:$LINENO: checking for ssize_t" >&5
+$as_echo_n "checking for ssize_t... " >&6; }
+if test "${ac_cv_type_ssize_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_type_ssize_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (ssize_t))
+       return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((ssize_t)))
+         return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  :
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_type_ssize_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_ssize_t" >&5
+$as_echo "$ac_cv_type_ssize_t" >&6; }
+if test "x$ac_cv_type_ssize_t" = x""yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define ssize_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if test "${ac_cv_type_uid_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "uid_t" >/dev/null 2>&1; then
+  ac_cv_type_uid_t=yes
+else
+  ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define uid_t int
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define gid_t int
+_ACEOF
+
+fi
+
+
+  { $as_echo "$as_me:$LINENO: checking for uint8_t" >&5
+$as_echo_n "checking for uint8_t... " >&6; }
+if test "${ac_cv_c_uint8_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_uint8_t=no
+     for ac_type in 'uint8_t' 'unsigned int' 'unsigned long int' \
+        'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (8 - 1) == 1)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  case $ac_type in
+  uint8_t) ac_cv_c_uint8_t=yes ;;
+  *) ac_cv_c_uint8_t=$ac_type ;;
+esac
+
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_uint8_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint8_t" >&5
+$as_echo "$ac_cv_c_uint8_t" >&6; }
+  case $ac_cv_c_uint8_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT8_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint8_t $ac_cv_c_uint8_t
+_ACEOF
+;;
+  esac
+
+
+  { $as_echo "$as_me:$LINENO: checking for uint16_t" >&5
+$as_echo_n "checking for uint16_t... " >&6; }
+if test "${ac_cv_c_uint16_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_uint16_t=no
+     for ac_type in 'uint16_t' 'unsigned int' 'unsigned long int' \
+        'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (16 - 1) == 1)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  case $ac_type in
+  uint16_t) ac_cv_c_uint16_t=yes ;;
+  *) ac_cv_c_uint16_t=$ac_type ;;
+esac
+
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_uint16_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint16_t" >&5
+$as_echo "$ac_cv_c_uint16_t" >&6; }
+  case $ac_cv_c_uint16_t in #(
+  no|yes) ;; #(
+  *)
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint16_t $ac_cv_c_uint16_t
+_ACEOF
+;;
+  esac
+
+
+  { $as_echo "$as_me:$LINENO: checking for uint32_t" >&5
+$as_echo_n "checking for uint32_t... " >&6; }
+if test "${ac_cv_c_uint32_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_uint32_t=no
+     for ac_type in 'uint32_t' 'unsigned int' 'unsigned long int' \
+        'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (32 - 1) == 1)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  case $ac_type in
+  uint32_t) ac_cv_c_uint32_t=yes ;;
+  *) ac_cv_c_uint32_t=$ac_type ;;
+esac
+
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_uint32_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint32_t" >&5
+$as_echo "$ac_cv_c_uint32_t" >&6; }
+  case $ac_cv_c_uint32_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT32_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint32_t $ac_cv_c_uint32_t
+_ACEOF
+;;
+  esac
+
+
+  { $as_echo "$as_me:$LINENO: checking for uint64_t" >&5
+$as_echo_n "checking for uint64_t... " >&6; }
+if test "${ac_cv_c_uint64_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_uint64_t=no
+     for ac_type in 'uint64_t' 'unsigned int' 'unsigned long int' \
+        'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (64 - 1) == 1)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  case $ac_type in
+  uint64_t) ac_cv_c_uint64_t=yes ;;
+  *) ac_cv_c_uint64_t=$ac_type ;;
+esac
+
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+       test "$ac_cv_c_uint64_t" != no && break
+     done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint64_t" >&5
+$as_echo "$ac_cv_c_uint64_t" >&6; }
+  case $ac_cv_c_uint64_t in #(
+  no|yes) ;; #(
+  *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT64_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint64_t $ac_cv_c_uint64_t
+_ACEOF
+;;
+  esac
+
+{ $as_echo "$as_me:$LINENO: checking for struct stat.st_rdev" >&5
+$as_echo_n "checking for struct stat.st_rdev... " >&6; }
+if test "${ac_cv_member_struct_stat_st_rdev+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (ac_aggr.st_rdev)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct_stat_st_rdev=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (sizeof ac_aggr.st_rdev)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_member_struct_stat_st_rdev=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_member_struct_stat_st_rdev=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_rdev" >&5
+$as_echo "$ac_cv_member_struct_stat_st_rdev" >&6; }
+if test "x$ac_cv_member_struct_stat_st_rdev" = x""yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+_ACEOF
+
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if test "${ac_cv_struct_tm+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+                                    int *p = &tm.tm_sec;
+                                    return !p;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_struct_tm=time.h
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_struct_tm=sys/time.h
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TM_IN_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+################################################################################
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_func in ftruncate gethostname getpagesize \
+  gettimeofday memset mkdir mkfifo rmdir munmap nl_langinfo setenv setlocale \
+  strcasecmp strchr strcspn strspn strdup strncasecmp strerror strrchr \
+  strstr strtol strtoul uname
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+done
+
+
+for ac_func in siginterrupt
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments.  Useless!
+{ $as_echo "$as_me:$LINENO: checking for working alloca.h" >&5
+$as_echo_n "checking for working alloca.h... " >&6; }
+if test "${ac_cv_working_alloca_h+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <alloca.h>
+int
+main ()
+{
+char *p = (char *) alloca (2 * sizeof (int));
+                         if (p) return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_working_alloca_h=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_working_alloca_h=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5
+$as_echo "$ac_cv_working_alloca_h" >&6; }
+if test $ac_cv_working_alloca_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA_H 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for alloca" >&5
+$as_echo_n "checking for alloca... " >&6; }
+if test "${ac_cv_func_alloca_works+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+#  include <malloc.h>
+#  define alloca _alloca
+# else
+#  ifdef HAVE_ALLOCA_H
+#   include <alloca.h>
+#  else
+#   ifdef _AIX
+ #pragma alloca
+#   else
+#    ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#    endif
+#   endif
+#  endif
+# endif
+#endif
+
+int
+main ()
+{
+char *p = (char *) alloca (1);
+                                   if (p) return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_func_alloca_works=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_alloca_works=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5
+$as_echo "$ac_cv_func_alloca_works" >&6; }
+
+if test $ac_cv_func_alloca_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA 1
+_ACEOF
+
+else
+  # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+# that cause trouble.  Some versions do not even contain alloca or
+# contain a buggy version.  If you still want to use their alloca,
+# use ar to extract alloca.o from them instead of compiling alloca.c.
+
+ALLOCA=\${LIBOBJDIR}alloca.$ac_objext
+
+cat >>confdefs.h <<\_ACEOF
+#define C_ALLOCA 1
+_ACEOF
+
+
+{ $as_echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5
+$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; }
+if test "${ac_cv_os_cray+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined CRAY && ! defined CRAY2
+webecray
+#else
+wenotbecray
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "webecray" >/dev/null 2>&1; then
+  ac_cv_os_cray=yes
+else
+  ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5
+$as_echo "$ac_cv_os_cray" >&6; }
+if test $ac_cv_os_cray = yes; then
+  for ac_func in _getb67 GETB67 getb67; do
+    as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define CRAY_STACKSEG_END $ac_func
+_ACEOF
+
+    break
+fi
+
+  done
+fi
+
+{ $as_echo "$as_me:$LINENO: checking stack direction for C alloca" >&5
+$as_echo_n "checking stack direction for C alloca... " >&6; }
+if test "${ac_cv_c_stack_direction+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_c_stack_direction=0
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+find_stack_direction ()
+{
+  static char *addr = 0;
+  auto char dummy;
+  if (addr == 0)
+    {
+      addr = &dummy;
+      return find_stack_direction ();
+    }
+  else
+    return (&dummy > addr) ? 1 : -1;
+}
+
+int
+main ()
+{
+  return find_stack_direction () < 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_stack_direction=1
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_stack_direction=-1
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5
+$as_echo "$ac_cv_c_stack_direction" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+_ACEOF
+
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether closedir returns void" >&5
+$as_echo_n "checking whether closedir returns void... " >&6; }
+if test "${ac_cv_func_closedir_void+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_closedir_void=yes
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header_dirent>
+#ifndef __cplusplus
+int closedir ();
+#endif
+
+int
+main ()
+{
+return closedir (opendir (".")) != 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_closedir_void=no
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_closedir_void=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_closedir_void" >&5
+$as_echo "$ac_cv_func_closedir_void" >&6; }
+if test $ac_cv_func_closedir_void = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define CLOSEDIR_VOID 1
+_ACEOF
+
+fi
+
+
+for ac_header in unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking for working chown" >&5
+$as_echo_n "checking for working chown... " >&6; }
+if test "${ac_cv_func_chown_works+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_chown_works=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <fcntl.h>
+
+int
+main ()
+{
+  char *f = "conftest.chown";
+  struct stat before, after;
+
+  if (creat (f, 0600) < 0)
+    return 1;
+  if (stat (f, &before) < 0)
+    return 1;
+  if (chown (f, (uid_t) -1, (gid_t) -1) == -1)
+    return 1;
+  if (stat (f, &after) < 0)
+    return 1;
+  return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid);
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_chown_works=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_chown_works=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+rm -f conftest.chown
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_chown_works" >&5
+$as_echo "$ac_cv_func_chown_works" >&6; }
+if test $ac_cv_func_chown_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CHOWN 1
+_ACEOF
+
+fi
+
+
+for ac_header in vfork.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_func in fork vfork
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+  { $as_echo "$as_me:$LINENO: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if test "${ac_cv_func_fork_works+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_fork_works=cross
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+         /* By Ruediger Kuhlmann. */
+         return fork () < 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_fork_works=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_fork_works=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+  ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+  case $host in
+    *-*-amigaos* | *-*-msdosdjgpp*)
+      # Override, as these systems have only a dummy fork() stub
+      ac_cv_func_fork_works=no
+      ;;
+    *)
+      ac_cv_func_fork_works=yes
+      ;;
+  esac
+  { $as_echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+  { $as_echo "$as_me:$LINENO: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if test "${ac_cv_func_vfork_works+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_vfork_works=cross
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Thanks to Paul Eggert for this test.  */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+   argument registers are propagated back to the parent.  The compiler
+   is told about this with #include <vfork.h>, but some compilers
+   (e.g. gcc -O) don't grok <vfork.h>.  Test for this by using a
+   static variable whose address is put into a register that is
+   clobbered by the vfork.  */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+  static pid_t child;
+  if (!child) {
+    child = vfork ();
+    if (child < 0) {
+      perror ("vfork");
+      _exit(2);
+    }
+    if (!child) {
+      arg = getpid();
+      write(-1, "", 0);
+      _exit (arg);
+    }
+  }
+}
+
+int
+main ()
+{
+  pid_t parent = getpid ();
+  pid_t child;
+
+  sparc_address_test (0);
+
+  child = vfork ();
+
+  if (child == 0) {
+    /* Here is another test for sparc vfork register problems.  This
+       test uses lots of local variables, at least as many local
+       variables as main has allocated so far including compiler
+       temporaries.  4 locals are enough for gcc 1.40.3 on a Solaris
+       4.1.3 sparc, but we use 8 to be safe.  A buggy compiler should
+       reuse the register of parent for one of the local variables,
+       since it will think that parent can't possibly be used any more
+       in this routine.  Assigning to the local variable will thus
+       munge parent in the parent process.  */
+    pid_t
+      p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+      p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+    /* Convince the compiler that p..p7 are live; otherwise, it might
+       use the same hardware register for all 8 local variables.  */
+    if (p != p1 || p != p2 || p != p3 || p != p4
+       || p != p5 || p != p6 || p != p7)
+      _exit(1);
+
+    /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+       from child file descriptors.  If the child closes a descriptor
+       before it execs or exits, this munges the parent's descriptor
+       as well.  Test for this by closing stdout in the child.  */
+    _exit(close(fileno(stdout)) != 0);
+  } else {
+    int status;
+    struct stat st;
+
+    while (wait(&status) != child)
+      ;
+    return (
+        /* Was there some problem with vforking?  */
+        child < 0
+
+        /* Did the child fail?  (This shouldn't happen.)  */
+        || status
+
+        /* Did the vfork/compiler bug occur?  */
+        || parent != getpid()
+
+        /* Did the file descriptor bug occur?  */
+        || fstat(fileno(stdout), &st) != 0
+        );
+  }
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_vfork_works=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_vfork_works=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+  ac_cv_func_vfork_works=$ac_cv_func_vfork
+  { $as_echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_VFORK 1
+_ACEOF
+
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define vfork fork
+_ACEOF
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_FORK 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5
+$as_echo_n "checking whether lstat dereferences a symlink specified with a trailing slash... " >&6; }
+if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  rm -f conftest.sym conftest.file
+echo >conftest.file
+if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_lstat_dereferences_slashed_symlink=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+     /* Linux will dereference the symlink and fail.
+       That is better in the sense that it means we will not
+       have to compile and use the lstat wrapper.  */
+     return lstat ("conftest.sym/", &sbuf) == 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_lstat_dereferences_slashed_symlink=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+else
+  # If the `ln -s' command failed, then we probably don't even
+  # have an lstat function.
+  ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -f conftest.sym conftest.file
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5
+$as_echo "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; }
+
+test $ac_cv_func_lstat_dereferences_slashed_symlink = yes &&
+
+cat >>confdefs.h <<_ACEOF
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+_ACEOF
+
+
+if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then
+  case " $LIBOBJS " in
+  *" lstat.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether lstat accepts an empty string" >&5
+$as_echo_n "checking whether lstat accepts an empty string... " >&6; }
+if test "${ac_cv_func_lstat_empty_string_bug+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_lstat_empty_string_bug=yes
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+  return lstat ("", &sbuf) == 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_lstat_empty_string_bug=no
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_lstat_empty_string_bug=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_lstat_empty_string_bug" >&5
+$as_echo "$ac_cv_func_lstat_empty_string_bug" >&6; }
+if test $ac_cv_func_lstat_empty_string_bug = yes; then
+  case " $LIBOBJS " in
+  *" lstat.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LSTAT_EMPTY_STRING_BUG 1
+_ACEOF
+
+fi
+
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5
+$as_echo_n "checking for GNU libc compatible malloc... " >&6; }
+if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_malloc_0_nonnull=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *malloc ();
+#endif
+
+int
+main ()
+{
+return ! malloc (0);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_malloc_0_nonnull=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_malloc_0_nonnull=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5
+$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; }
+if test $ac_cv_func_malloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 1
+_ACEOF
+
+else
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 0
+_ACEOF
+
+   case " $LIBOBJS " in
+  *" malloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define malloc rpl_malloc
+_ACEOF
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for working memcmp" >&5
+$as_echo_n "checking for working memcmp... " >&6; }
+if test "${ac_cv_func_memcmp_working+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_memcmp_working=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+  /* Some versions of memcmp are not 8-bit clean.  */
+  char c0 = '\100', c1 = '\200', c2 = '\201';
+  if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+    return 1;
+
+  /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+     or more and with at least one buffer not starting on a 4-byte boundary.
+     William Lewis provided this test program.   */
+  {
+    char foo[21];
+    char bar[21];
+    int i;
+    for (i = 0; i < 4; i++)
+      {
+       char *a = foo + i;
+       char *b = bar + i;
+       strcpy (a, "--------01111111");
+       strcpy (b, "--------10000000");
+       if (memcmp (a, b, 16) >= 0)
+         return 1;
+      }
+    return 0;
+  }
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_memcmp_working=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_memcmp_working=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5
+$as_echo "$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+  *" memcmp.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+
+
+for ac_header in stdlib.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_func in getpagesize
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+{ $as_echo "$as_me:$LINENO: checking for working mmap" >&5
+$as_echo_n "checking for working mmap... " >&6; }
+if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_mmap_fixed_mapped=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+/* malloc might have been renamed as rpl_malloc. */
+#undef malloc
+
+/* Thanks to Mike Haertel and Jim Avera for this test.
+   Here is a matrix of mmap possibilities:
+       mmap private not fixed
+       mmap private fixed at somewhere currently unmapped
+       mmap private fixed at somewhere already mapped
+       mmap shared not fixed
+       mmap shared fixed at somewhere currently unmapped
+       mmap shared fixed at somewhere already mapped
+   For private mappings, we should verify that changes cannot be read()
+   back from the file, nor mmap's back from the file at a different
+   address.  (There have been systems where private was not correctly
+   implemented like the infamous i386 svr4.0, and systems where the
+   VM page cache was not coherent with the file system buffer cache
+   like early versions of FreeBSD and possibly contemporary NetBSD.)
+   For shared mappings, we should conversely verify that changes get
+   propagated back to all the places they're supposed to be.
+
+   Grep wants private fixed already mapped.
+   The main things grep needs to know about mmap are:
+   * does it exist and is it safe to write into the mmap'd area
+   * how to use it (BSD variants)  */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#if !defined STDC_HEADERS && !defined HAVE_STDLIB_H
+char *malloc ();
+#endif
+
+/* This mess was copied from the GNU getpagesize.h.  */
+#ifndef HAVE_GETPAGESIZE
+/* Assume that all systems that can run configure have sys/param.h.  */
+# ifndef HAVE_SYS_PARAM_H
+#  define HAVE_SYS_PARAM_H 1
+# endif
+
+# ifdef _SC_PAGESIZE
+#  define getpagesize() sysconf(_SC_PAGESIZE)
+# else /* no _SC_PAGESIZE */
+#  ifdef HAVE_SYS_PARAM_H
+#   include <sys/param.h>
+#   ifdef EXEC_PAGESIZE
+#    define getpagesize() EXEC_PAGESIZE
+#   else /* no EXEC_PAGESIZE */
+#    ifdef NBPG
+#     define getpagesize() NBPG * CLSIZE
+#     ifndef CLSIZE
+#      define CLSIZE 1
+#     endif /* no CLSIZE */
+#    else /* no NBPG */
+#     ifdef NBPC
+#      define getpagesize() NBPC
+#     else /* no NBPC */
+#      ifdef PAGESIZE
+#       define getpagesize() PAGESIZE
+#      endif /* PAGESIZE */
+#     endif /* no NBPC */
+#    endif /* no NBPG */
+#   endif /* no EXEC_PAGESIZE */
+#  else /* no HAVE_SYS_PARAM_H */
+#   define getpagesize() 8192  /* punt totally */
+#  endif /* no HAVE_SYS_PARAM_H */
+# endif /* no _SC_PAGESIZE */
+
+#endif /* no HAVE_GETPAGESIZE */
+
+int
+main ()
+{
+  char *data, *data2, *data3;
+  int i, pagesize;
+  int fd;
+
+  pagesize = getpagesize ();
+
+  /* First, make a file with some known garbage in it. */
+  data = (char *) malloc (pagesize);
+  if (!data)
+    return 1;
+  for (i = 0; i < pagesize; ++i)
+    *(data + i) = rand ();
+  umask (0);
+  fd = creat ("conftest.mmap", 0600);
+  if (fd < 0)
+    return 1;
+  if (write (fd, data, pagesize) != pagesize)
+    return 1;
+  close (fd);
+
+  /* Next, try to mmap the file at a fixed address which already has
+     something else allocated at it.  If we can, also make sure that
+     we see the same garbage.  */
+  fd = open ("conftest.mmap", O_RDWR);
+  if (fd < 0)
+    return 1;
+  data2 = (char *) malloc (2 * pagesize);
+  if (!data2)
+    return 1;
+  data2 += (pagesize - ((long int) data2 & (pagesize - 1))) & (pagesize - 1);
+  if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_FIXED, fd, 0L))
+    return 1;
+  for (i = 0; i < pagesize; ++i)
+    if (*(data + i) != *(data2 + i))
+      return 1;
+
+  /* Finally, make sure that changes to the mapped area do not
+     percolate back to the file as seen by read().  (This is a bug on
+     some variants of i386 svr4.0.)  */
+  for (i = 0; i < pagesize; ++i)
+    *(data2 + i) = *(data2 + i) + 1;
+  data3 = (char *) malloc (pagesize);
+  if (!data3)
+    return 1;
+  if (read (fd, data3, pagesize) != pagesize)
+    return 1;
+  for (i = 0; i < pagesize; ++i)
+    if (*(data + i) != *(data3 + i))
+      return 1;
+  close (fd);
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_mmap_fixed_mapped=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_mmap_fixed_mapped=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5
+$as_echo "$ac_cv_func_mmap_fixed_mapped" >&6; }
+if test $ac_cv_func_mmap_fixed_mapped = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MMAP 1
+_ACEOF
+
+fi
+rm -f conftest.mmap
+
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5
+$as_echo_n "checking for GNU libc compatible realloc... " >&6; }
+if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_realloc_0_nonnull=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *realloc ();
+#endif
+
+int
+main ()
+{
+return ! realloc (0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_realloc_0_nonnull=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_realloc_0_nonnull=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5
+$as_echo "$ac_cv_func_realloc_0_nonnull" >&6; }
+if test $ac_cv_func_realloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALLOC 1
+_ACEOF
+
+else
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALLOC 0
+_ACEOF
+
+   case " $LIBOBJS " in
+  *" realloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS realloc.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define realloc rpl_realloc
+_ACEOF
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5
+$as_echo_n "checking whether stat accepts an empty string... " >&6; }
+if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_stat_empty_string_bug=yes
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+  return stat ("", &sbuf) == 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_stat_empty_string_bug=no
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_stat_empty_string_bug=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5
+$as_echo "$ac_cv_func_stat_empty_string_bug" >&6; }
+if test $ac_cv_func_stat_empty_string_bug = yes; then
+  case " $LIBOBJS " in
+  *" stat.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS stat.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STAT_EMPTY_STRING_BUG 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for working strtod" >&5
+$as_echo_n "checking for working strtod... " >&6; }
+if test "${ac_cv_func_strtod+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_strtod=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+$ac_includes_default
+#ifndef strtod
+double strtod ();
+#endif
+int
+main()
+{
+  {
+    /* Some versions of Linux strtod mis-parse strings with leading '+'.  */
+    char *string = " +69";
+    char *term;
+    double value;
+    value = strtod (string, &term);
+    if (value != 69 || term != (string + 4))
+      return 1;
+  }
+
+  {
+    /* Under Solaris 2.4, strtod returns the wrong value for the
+       terminating character under some conditions.  */
+    char *string = "NaN";
+    char *term;
+    strtod (string, &term);
+    if (term != string && *(term - 1) == 0)
+      return 1;
+  }
+  return 0;
+}
+
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_strtod=yes
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_strtod=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_strtod" >&5
+$as_echo "$ac_cv_func_strtod" >&6; }
+if test $ac_cv_func_strtod = no; then
+  case " $LIBOBJS " in
+  *" strtod.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS strtod.$ac_objext"
+ ;;
+esac
+
+{ $as_echo "$as_me:$LINENO: checking for pow" >&5
+$as_echo_n "checking for pow... " >&6; }
+if test "${ac_cv_func_pow+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define pow to an innocuous variant, in case <limits.h> declares pow.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define pow innocuous_pow
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char pow (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef pow
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_pow || defined __stub___pow
+choke me
+#endif
+
+int
+main ()
+{
+return pow ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_func_pow=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func_pow=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_pow" >&5
+$as_echo "$ac_cv_func_pow" >&6; }
+
+if test $ac_cv_func_pow = no; then
+  { $as_echo "$as_me:$LINENO: checking for pow in -lm" >&5
+$as_echo_n "checking for pow in -lm... " >&6; }
+if test "${ac_cv_lib_m_pow+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+int
+main ()
+{
+return pow ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_m_pow=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_m_pow=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5
+$as_echo "$ac_cv_lib_m_pow" >&6; }
+if test "x$ac_cv_lib_m_pow" = x""yes; then
+  POW_LIB=-lm
+else
+  { $as_echo "$as_me:$LINENO: WARNING: cannot find library containing definition of pow" >&5
+$as_echo "$as_me: WARNING: cannot find library containing definition of pow" >&2;}
+fi
+
+fi
+
+fi
+
+
+for ac_func in vprintf
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+{ $as_echo "$as_me:$LINENO: checking for _doprnt" >&5
+$as_echo_n "checking for _doprnt... " >&6; }
+if test "${ac_cv_func__doprnt+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub__doprnt || defined __stub____doprnt
+choke me
+#endif
+
+int
+main ()
+{
+return _doprnt ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_func__doprnt=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_func__doprnt=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+$as_echo "$ac_cv_func__doprnt" >&6; }
+if test "x$ac_cv_func__doprnt" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to use static linking" >&5
+$as_echo_n "checking whether to use static linking... " >&6; }
+# Check whether --enable-static_link was given.
+if test "${enable_static_link+set}" = set; then
+  enableval=$enable_static_link; STATIC_LINK=$enableval
+else
+  STATIC_LINK=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $STATIC_LINK" >&5
+$as_echo "$STATIC_LINK" >&6; }
+
+################################################################################
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking file owner" >&5
+$as_echo_n "checking file owner... " >&6; }
+
+# Check whether --with-user was given.
+if test "${with_user+set}" = set; then
+  withval=$with_user; OWNER=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $OWNER" >&5
+$as_echo "$OWNER" >&6; }
+
+if test x$OWNER != x; then
+       INSTALL="$INSTALL -o $OWNER"
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking group owner" >&5
+$as_echo_n "checking group owner... " >&6; }
+
+# Check whether --with-group was given.
+if test "${with_group+set}" = set; then
+  withval=$with_group; GROUP=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $GROUP" >&5
+$as_echo "$GROUP" >&6; }
+
+if test x$GROUP != x; then
+       INSTALL="$INSTALL -g $GROUP"
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking device node uid" >&5
+$as_echo_n "checking device node uid... " >&6; }
+
+
+# Check whether --with-device-uid was given.
+if test "${with_device_uid+set}" = set; then
+  withval=$with_device_uid; DM_DEVICE_UID=$withval
+else
+  DM_DEVICE_UID=0
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DM_DEVICE_UID" >&5
+$as_echo "$DM_DEVICE_UID" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking device node gid" >&5
+$as_echo_n "checking device node gid... " >&6; }
+
+
+# Check whether --with-device-gid was given.
+if test "${with_device_gid+set}" = set; then
+  withval=$with_device_gid; DM_DEVICE_GID=$withval
+else
+  DM_DEVICE_GID=0
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DM_DEVICE_GID" >&5
+$as_echo "$DM_DEVICE_GID" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking device node mode" >&5
+$as_echo_n "checking device node mode... " >&6; }
+
+
+# Check whether --with-device-mode was given.
+if test "${with_device_mode+set}" = set; then
+  withval=$with_device_mode; DM_DEVICE_MODE=$withval
+else
+  DM_DEVICE_MODE=0600
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DM_DEVICE_MODE" >&5
+$as_echo "$DM_DEVICE_MODE" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable lvm1 fallback" >&5
+$as_echo_n "checking whether to enable lvm1 fallback... " >&6; }
+# Check whether --enable-lvm1_fallback was given.
+if test "${enable_lvm1_fallback+set}" = set; then
+  enableval=$enable_lvm1_fallback; LVM1_FALLBACK=$enableval
+else
+  LVM1_FALLBACK=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $LVM1_FALLBACK" >&5
+$as_echo "$LVM1_FALLBACK" >&6; }
+
+if test x$LVM1_FALLBACK = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define LVM1_FALLBACK 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include support for lvm1 metadata" >&5
+$as_echo_n "checking whether to include support for lvm1 metadata... " >&6; }
+
+# Check whether --with-lvm1 was given.
+if test "${with_lvm1+set}" = set; then
+  withval=$with_lvm1; LVM1=$withval
+else
+  LVM1=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $LVM1" >&5
+$as_echo "$LVM1" >&6; }
+
+if [ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ];
+ then  { { $as_echo "$as_me:$LINENO: error: --with-lvm1 parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-lvm1 parameter invalid
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi;
+
+if test x$LVM1 = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define LVM1_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include support for GFS pool metadata" >&5
+$as_echo_n "checking whether to include support for GFS pool metadata... " >&6; }
+
+# Check whether --with-pool was given.
+if test "${with_pool+set}" = set; then
+  withval=$with_pool; POOL=$withval
+else
+  POOL=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $POOL" >&5
+$as_echo "$POOL" >&6; }
+
+if [ "x$POOL" != xnone -a "x$POOL" != xinternal -a "x$POOL" != xshared ];
+ then  { { $as_echo "$as_me:$LINENO: error: --with-pool parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-pool parameter invalid
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi;
+
+if test x$POOL = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define POOL_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include support for cluster locking" >&5
+$as_echo_n "checking whether to include support for cluster locking... " >&6; }
+
+# Check whether --with-cluster was given.
+if test "${with_cluster+set}" = set; then
+  withval=$with_cluster; CLUSTER=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $CLUSTER" >&5
+$as_echo "$CLUSTER" >&6; }
+
+if [ "x$CLUSTER" != xnone -a "x$CLUSTER" != xinternal -a "x$CLUSTER" != xshared ];
+ then  { { $as_echo "$as_me:$LINENO: error: --with-cluster parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-cluster parameter invalid
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi;
+
+if test x$CLUSTER = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define CLUSTER_LOCKING_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include snapshots" >&5
+$as_echo_n "checking whether to include snapshots... " >&6; }
+
+# Check whether --with-snapshots was given.
+if test "${with_snapshots+set}" = set; then
+  withval=$with_snapshots; SNAPSHOTS=$withval
+else
+  SNAPSHOTS=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $SNAPSHOTS" >&5
+$as_echo "$SNAPSHOTS" >&6; }
+
+if [ "x$SNAPSHOTS" != xnone -a "x$SNAPSHOTS" != xinternal -a "x$SNAPSHOTS" != xshared ];
+ then  { { $as_echo "$as_me:$LINENO: error: --with-snapshots parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-snapshots parameter invalid
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi;
+
+if test x$SNAPSHOTS = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define SNAPSHOT_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include mirrors" >&5
+$as_echo_n "checking whether to include mirrors... " >&6; }
+
+# Check whether --with-mirrors was given.
+if test "${with_mirrors+set}" = set; then
+  withval=$with_mirrors; MIRRORS=$withval
+else
+  MIRRORS=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $MIRRORS" >&5
+$as_echo "$MIRRORS" >&6; }
+
+if [ "x$MIRRORS" != xnone -a "x$MIRRORS" != xinternal -a "x$MIRRORS" != xshared ];
+ then  { { $as_echo "$as_me:$LINENO: error: --with-mirrors parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-mirrors parameter invalid
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi;
+
+if test x$MIRRORS = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define MIRRORED_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include replicators" >&5
+$as_echo_n "checking whether to include replicators... " >&6; }
+
+# Check whether --with-replicators was given.
+if test "${with_replicators+set}" = set; then
+  withval=$with_replicators; REPLICATORS=$withval
+else
+  REPLICATORS=none
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $REPLICATORS" >&5
+$as_echo "$REPLICATORS" >&6; }
+
+case "$REPLICATORS" in
+  none|shared) ;;
+  internal)
+cat >>confdefs.h <<\_ACEOF
+#define REPLICATOR_INTERNAL 1
+_ACEOF
+ ;;
+  *) { { $as_echo "$as_me:$LINENO: error: --with-replicators parameter invalid ($REPLICATORS)" >&5
+$as_echo "$as_me: error: --with-replicators parameter invalid ($REPLICATORS)" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable readline" >&5
+$as_echo_n "checking whether to enable readline... " >&6; }
+# Check whether --enable-readline was given.
+if test "${enable_readline+set}" = set; then
+  enableval=$enable_readline; READLINE=$enableval
+else
+  READLINE=maybe
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $READLINE" >&5
+$as_echo "$READLINE" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable realtime support" >&5
+$as_echo_n "checking whether to enable realtime support... " >&6; }
+# Check whether --enable-realtime was given.
+if test "${enable_realtime+set}" = set; then
+  enableval=$enable_realtime; REALTIME=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $REALTIME" >&5
+$as_echo "$REALTIME" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable OCF resource agents" >&5
+$as_echo_n "checking whether to enable OCF resource agents... " >&6; }
+# Check whether --enable-ocf was given.
+if test "${enable_ocf+set}" = set; then
+  enableval=$enable_ocf; OCF=$enableval
+else
+  OCF=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $OCF" >&5
+$as_echo "$OCF" >&6; }
+
+################################################################################
+pkg_config_init() {
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=0.9.0
+       { $as_echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       else
+               { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+               PKG_CONFIG=""
+       fi
+
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for PKGCONFIGINIT" >&5
+$as_echo_n "checking for PKGCONFIGINIT... " >&6; }
+
+if test -n "$PKGCONFIGINIT_CFLAGS"; then
+    pkg_cv_PKGCONFIGINIT_CFLAGS="$PKGCONFIGINIT_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"pkgconfiginit\"") >&5
+  ($PKG_CONFIG --exists --print-errors "pkgconfiginit") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_PKGCONFIGINIT_CFLAGS=`$PKG_CONFIG --cflags "pkgconfiginit" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$PKGCONFIGINIT_LIBS"; then
+    pkg_cv_PKGCONFIGINIT_LIBS="$PKGCONFIGINIT_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"pkgconfiginit\"") >&5
+  ($PKG_CONFIG --exists --print-errors "pkgconfiginit") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_PKGCONFIGINIT_LIBS=`$PKG_CONFIG --libs "pkgconfiginit" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               PKGCONFIGINIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "pkgconfiginit" 2>&1`
+        else
+               PKGCONFIGINIT_PKG_ERRORS=`$PKG_CONFIG --print-errors "pkgconfiginit" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$PKGCONFIGINIT_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { $as_echo "$as_me:$LINENO: result: pkg-config initialized" >&5
+$as_echo "pkg-config initialized" >&6; }
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:$LINENO: result: pkg-config initialized" >&5
+$as_echo "pkg-config initialized" >&6; }
+else
+       PKGCONFIGINIT_CFLAGS=$pkg_cv_PKGCONFIGINIT_CFLAGS
+       PKGCONFIGINIT_LIBS=$pkg_cv_PKGCONFIGINIT_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       :
+fi
+       PKGCONFIG_INIT=1
+}
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to build cluster LVM daemon" >&5
+$as_echo_n "checking whether to build cluster LVM daemon... " >&6; }
+
+# Check whether --with-clvmd was given.
+if test "${with_clvmd+set}" = set; then
+  withval=$with_clvmd; CLVMD=$withval
+else
+  CLVMD=none
+fi
+
+if test x$CLVMD = xyes; then
+       CLVMD=all
+fi
+{ $as_echo "$as_me:$LINENO: result: $CLVMD" >&5
+$as_echo "$CLVMD" >&6; }
+
+if  test x$CLVMD != xnone && test x$CLUSTER = xnone; then
+       CLUSTER=internal
+fi
+
+if test x$CLVMD != xnone && test x$PKGCONFIG_INIT != x1; then
+       pkg_config_init
+fi
+
+CLVMD_CMANAGERS=""
+CLVMD_NEEDS_QDISKD=no
+
+if [ `expr x"$CLVMD" : '.*gulm.*'` != 0 ]; then
+       BUILDGULM=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+       CLVMD_NEEDS_QDISKD=yes
+fi
+if [ `expr x"$CLVMD" : '.*cman.*'` != 0 ]; then
+       BUILDCMAN=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+       CLVMD_NEEDS_QDISKD=yes
+fi
+if [ `expr x"$CLVMD" : '.*corosync.*'` != 0 ]; then
+       BUILDCOROSYNC=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+fi
+if [ `expr x"$CLVMD" : '.*openais.*'` != 0 ]; then
+       BUILDOPENAIS=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+fi
+if test x$CLVMD_NEEDS_QDISKD != xno; then
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+fi
+
+if test x$BUILDGULM = xyes; then
+       if test x$BUILDCOROSYNC = xyes || \
+          test x$BUILDOPENAIS = xyes; then
+               { { $as_echo "$as_me:$LINENO: error: requested clvmd configuration is not valid" >&5
+$as_echo "$as_me: error: requested clvmd configuration is not valid" >&2;}
+   { (exit 1); exit 1; }; }
+       fi
+fi
+
+soft_bailout() {
+       NOTFOUND=1
+}
+
+hard_bailout() {
+       { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+}
+
+if test x$CLVMD = xall; then
+       bailout=soft_bailout
+       BUILDGULM=yes
+       BUILDCMAN=yes
+       BUILDCOROSYNC=yes
+       BUILDOPENAIS=yes
+else
+       bailout=hard_bailout
+fi
+
+check_lib_no_libs() {
+       lib_no_libs_arg1=$1
+       shift
+       lib_no_libs_arg2=$1
+       shift
+       lib_no_libs_args=$@
+
+as_ac_Lib=`$as_echo "ac_cv_lib_$lib_no_libs_arg1''_$lib_no_libs_arg2" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $lib_no_libs_arg2 in -l$lib_no_libs_arg1" >&5
+$as_echo_n "checking for $lib_no_libs_arg2 in -l$lib_no_libs_arg1... " >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$lib_no_libs_arg1 $lib_no_libs_args $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $lib_no_libs_arg2 ();
+int
+main ()
+{
+return $lib_no_libs_arg2 ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_Lib=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_Lib=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval 'as_val=${'$as_ac_Lib'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Lib'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_LIB$lib_no_libs_arg1" | $as_tr_cpp` 1
+_ACEOF
+
+  LIBS="-l$lib_no_libs_arg1 $LIBS"
+
+else
+  $bailout
+fi
+
+       LIBS=$ac_check_lib_save_LIBS
+}
+
+if test x$BUILDGULM = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CCS" >&5
+$as_echo_n "checking for CCS... " >&6; }
+
+if test -n "$CCS_CFLAGS"; then
+    pkg_cv_CCS_CFLAGS="$CCS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libccs\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libccs") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CCS_CFLAGS=`$PKG_CONFIG --cflags "libccs" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CCS_LIBS"; then
+    pkg_cv_CCS_LIBS="$CCS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libccs\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libccs") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CCS_LIBS=`$PKG_CONFIG --libs "libccs" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CCS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libccs" 2>&1`
+        else
+               CCS_PKG_ERRORS=`$PKG_CONFIG --print-errors "libccs" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CCS_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                NOTFOUND=0
+
+for ac_header in ccs.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs ccs ccs_connect
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libccs, using -lccs" >&5
+$as_echo "no pkg for libccs, using -lccs" >&6; }
+                       CCS_LIBS="-lccs"
+                       HAVE_CCS=yes
+               fi
+elif test $pkg_failed = untried; then
+       NOTFOUND=0
+
+for ac_header in ccs.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs ccs ccs_connect
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libccs, using -lccs" >&5
+$as_echo "no pkg for libccs, using -lccs" >&6; }
+                       CCS_LIBS="-lccs"
+                       HAVE_CCS=yes
+               fi
+else
+       CCS_CFLAGS=$pkg_cv_CCS_CFLAGS
+       CCS_LIBS=$pkg_cv_CCS_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_CCS=yes
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GULM" >&5
+$as_echo_n "checking for GULM... " >&6; }
+
+if test -n "$GULM_CFLAGS"; then
+    pkg_cv_GULM_CFLAGS="$GULM_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgulm\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libgulm") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GULM_CFLAGS=`$PKG_CONFIG --cflags "libgulm" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GULM_LIBS"; then
+    pkg_cv_GULM_LIBS="$GULM_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgulm\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libgulm") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GULM_LIBS=`$PKG_CONFIG --libs "libgulm" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GULM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libgulm" 2>&1`
+        else
+               GULM_PKG_ERRORS=`$PKG_CONFIG --print-errors "libgulm" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GULM_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                NOTFOUND=0
+
+for ac_header in libgulm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs gulm lg_core_login
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libgulm, using -lgulm" >&5
+$as_echo "no pkg for libgulm, using -lgulm" >&6; }
+                       GULM_LIBS="-lgulm"
+                       HAVE_GULM=yes
+               fi
+elif test $pkg_failed = untried; then
+       NOTFOUND=0
+
+for ac_header in libgulm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs gulm lg_core_login
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libgulm, using -lgulm" >&5
+$as_echo "no pkg for libgulm, using -lgulm" >&6; }
+                       GULM_LIBS="-lgulm"
+                       HAVE_GULM=yes
+               fi
+else
+       GULM_CFLAGS=$pkg_cv_GULM_CFLAGS
+       GULM_LIBS=$pkg_cv_GULM_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_GULM=yes
+fi
+fi
+
+if test x$BUILDCMAN = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CMAN" >&5
+$as_echo_n "checking for CMAN... " >&6; }
+
+if test -n "$CMAN_CFLAGS"; then
+    pkg_cv_CMAN_CFLAGS="$CMAN_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcman\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcman") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CMAN_CFLAGS=`$PKG_CONFIG --cflags "libcman" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CMAN_LIBS"; then
+    pkg_cv_CMAN_LIBS="$CMAN_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcman\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcman") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CMAN_LIBS=`$PKG_CONFIG --libs "libcman" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CMAN_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcman" 2>&1`
+        else
+               CMAN_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcman" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CMAN_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                NOTFOUND=0
+
+for ac_header in libcman.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs cman cman_init
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libcman, using -lcman" >&5
+$as_echo "no pkg for libcman, using -lcman" >&6; }
+                       CMAN_LIBS="-lcman"
+                       HAVE_CMAN=yes
+               fi
+elif test $pkg_failed = untried; then
+       NOTFOUND=0
+
+for ac_header in libcman.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs cman cman_init
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libcman, using -lcman" >&5
+$as_echo "no pkg for libcman, using -lcman" >&6; }
+                       CMAN_LIBS="-lcman"
+                       HAVE_CMAN=yes
+               fi
+else
+       CMAN_CFLAGS=$pkg_cv_CMAN_CFLAGS
+       CMAN_LIBS=$pkg_cv_CMAN_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_CMAN=yes
+fi
+       CHECKCONFDB=yes
+       CHECKDLM=yes
+fi
+
+if test x$BUILDCOROSYNC = xyes || \
+   test x$BUILDOPENAIS = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for COROSYNC" >&5
+$as_echo_n "checking for COROSYNC... " >&6; }
+
+if test -n "$COROSYNC_CFLAGS"; then
+    pkg_cv_COROSYNC_CFLAGS="$COROSYNC_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"corosync\"") >&5
+  ($PKG_CONFIG --exists --print-errors "corosync") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_COROSYNC_CFLAGS=`$PKG_CONFIG --cflags "corosync" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$COROSYNC_LIBS"; then
+    pkg_cv_COROSYNC_LIBS="$COROSYNC_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"corosync\"") >&5
+  ($PKG_CONFIG --exists --print-errors "corosync") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_COROSYNC_LIBS=`$PKG_CONFIG --libs "corosync" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               COROSYNC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "corosync" 2>&1`
+        else
+               COROSYNC_PKG_ERRORS=`$PKG_CONFIG --print-errors "corosync" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$COROSYNC_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                $bailout
+elif test $pkg_failed = untried; then
+       $bailout
+else
+       COROSYNC_CFLAGS=$pkg_cv_COROSYNC_CFLAGS
+       COROSYNC_LIBS=$pkg_cv_COROSYNC_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_COROSYNC=yes
+fi
+       CHECKCONFDB=yes
+fi
+
+if test x$BUILDCOROSYNC = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for QUORUM" >&5
+$as_echo_n "checking for QUORUM... " >&6; }
+
+if test -n "$QUORUM_CFLAGS"; then
+    pkg_cv_QUORUM_CFLAGS="$QUORUM_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libquorum\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libquorum") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_QUORUM_CFLAGS=`$PKG_CONFIG --cflags "libquorum" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$QUORUM_LIBS"; then
+    pkg_cv_QUORUM_LIBS="$QUORUM_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libquorum\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libquorum") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_QUORUM_LIBS=`$PKG_CONFIG --libs "libquorum" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               QUORUM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libquorum" 2>&1`
+        else
+               QUORUM_PKG_ERRORS=`$PKG_CONFIG --print-errors "libquorum" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$QUORUM_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                $bailout
+elif test $pkg_failed = untried; then
+       $bailout
+else
+       QUORUM_CFLAGS=$pkg_cv_QUORUM_CFLAGS
+       QUORUM_LIBS=$pkg_cv_QUORUM_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_QUORUM=yes
+fi
+       CHECKCPG=yes
+       CHECKDLM=yes
+fi
+
+if test x$BUILDOPENAIS = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for SALCK" >&5
+$as_echo_n "checking for SALCK... " >&6; }
+
+if test -n "$SALCK_CFLAGS"; then
+    pkg_cv_SALCK_CFLAGS="$SALCK_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaLck\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libSaLck") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_SALCK_CFLAGS=`$PKG_CONFIG --cflags "libSaLck" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SALCK_LIBS"; then
+    pkg_cv_SALCK_LIBS="$SALCK_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaLck\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libSaLck") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_SALCK_LIBS=`$PKG_CONFIG --libs "libSaLck" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               SALCK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libSaLck" 2>&1`
+        else
+               SALCK_PKG_ERRORS=`$PKG_CONFIG --print-errors "libSaLck" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$SALCK_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                $bailout
+elif test $pkg_failed = untried; then
+       $bailout
+else
+       SALCK_CFLAGS=$pkg_cv_SALCK_CFLAGS
+       SALCK_LIBS=$pkg_cv_SALCK_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_SALCK=yes
+fi
+       CHECKCPG=yes
+fi
+
+
+
+if test x$CHECKCONFDB = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CONFDB" >&5
+$as_echo_n "checking for CONFDB... " >&6; }
+
+if test -n "$CONFDB_CFLAGS"; then
+    pkg_cv_CONFDB_CFLAGS="$CONFDB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libconfdb\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libconfdb") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CONFDB_CFLAGS=`$PKG_CONFIG --cflags "libconfdb" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CONFDB_LIBS"; then
+    pkg_cv_CONFDB_LIBS="$CONFDB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libconfdb\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libconfdb") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CONFDB_LIBS=`$PKG_CONFIG --libs "libconfdb" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CONFDB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libconfdb" 2>&1`
+        else
+               CONFDB_PKG_ERRORS=`$PKG_CONFIG --print-errors "libconfdb" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CONFDB_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                HAVE_CONFDB=no
+elif test $pkg_failed = untried; then
+       HAVE_CONFDB=no
+else
+       CONFDB_CFLAGS=$pkg_cv_CONFDB_CFLAGS
+       CONFDB_LIBS=$pkg_cv_CONFDB_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_CONFDB=yes
+fi
+
+
+for ac_header in corosync/confdb.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ HAVE_CONFDB_H=yes
+else
+  HAVE_CONFDB_H=no
+fi
+
+done
+
+
+       if test x$HAVE_CONFDB != xyes && \
+          test x$HAVE_CONFDB_H = xyes; then
+               check_lib_no_libs confdb confdb_initialize
+               { $as_echo "$as_me:$LINENO: result: no pkg for confdb, using -lconfdb" >&5
+$as_echo "no pkg for confdb, using -lconfdb" >&6; }
+               CONFDB_LIBS="-lconfdb"
+               HAVE_CONFDB=yes
+       fi
+
+       if test x$BUILDCOROSYNC = xyes && \
+          test x$HAVE_CONFDB != xyes &&
+          test x$CLVMD != xall; then
+               { { $as_echo "$as_me:$LINENO: error: bailing out... confdb library is required" >&5
+$as_echo "$as_me: error: bailing out... confdb library is required" >&2;}
+   { (exit 1); exit 1; }; }
+       fi
+fi
+
+if test x$CHECKCPG = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CPG" >&5
+$as_echo_n "checking for CPG... " >&6; }
+
+if test -n "$CPG_CFLAGS"; then
+    pkg_cv_CPG_CFLAGS="$CPG_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CPG_CFLAGS=`$PKG_CONFIG --cflags "libcpg" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CPG_LIBS"; then
+    pkg_cv_CPG_LIBS="$CPG_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CPG_LIBS=`$PKG_CONFIG --libs "libcpg" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CPG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcpg" 2>&1`
+        else
+               CPG_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcpg" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CPG_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                $bailout
+elif test $pkg_failed = untried; then
+       $bailout
+else
+       CPG_CFLAGS=$pkg_cv_CPG_CFLAGS
+       CPG_LIBS=$pkg_cv_CPG_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_CPG=yes
+fi
+fi
+
+if test x$CHECKDLM = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for DLM" >&5
+$as_echo_n "checking for DLM... " >&6; }
+
+if test -n "$DLM_CFLAGS"; then
+    pkg_cv_DLM_CFLAGS="$DLM_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libdlm\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libdlm") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_DLM_CFLAGS=`$PKG_CONFIG --cflags "libdlm" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$DLM_LIBS"; then
+    pkg_cv_DLM_LIBS="$DLM_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libdlm\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libdlm") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_DLM_LIBS=`$PKG_CONFIG --libs "libdlm" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               DLM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libdlm" 2>&1`
+        else
+               DLM_PKG_ERRORS=`$PKG_CONFIG --print-errors "libdlm" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$DLM_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                NOTFOUND=0
+
+for ac_header in libdlm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs dlm dlm_lock -lpthread
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libdlm, using -ldlm" >&5
+$as_echo "no pkg for libdlm, using -ldlm" >&6; }
+                       DLM_LIBS="-ldlm -lpthread"
+                       HAVE_DLM=yes
+               fi
+elif test $pkg_failed = untried; then
+       NOTFOUND=0
+
+for ac_header in libdlm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  $bailout
+fi
+
+done
+
+               check_lib_no_libs dlm dlm_lock -lpthread
+               if test $NOTFOUND = 0; then
+                       { $as_echo "$as_me:$LINENO: result: no pkg for libdlm, using -ldlm" >&5
+$as_echo "no pkg for libdlm, using -ldlm" >&6; }
+                       DLM_LIBS="-ldlm -lpthread"
+                       HAVE_DLM=yes
+               fi
+else
+       DLM_CFLAGS=$pkg_cv_DLM_CFLAGS
+       DLM_LIBS=$pkg_cv_DLM_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       HAVE_DLM=yes
+fi
+fi
+
+if test x$CLVMD = xall; then
+       CLVMD=none
+       CLVMD_CMANAGERS=""
+       CLVMD_NEEDS_QDISKD=no
+       if test x$HAVE_CCS = xyes && \
+          test x$HAVE_GULM = xyes; then
+               { $as_echo "$as_me:$LINENO: result: Enabling clvmd gulm cluster manager" >&5
+$as_echo "Enabling clvmd gulm cluster manager" >&6; }
+               CLVMD="$CLVMD,gulm"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+               CLVMD_NEEDS_QDISKD=yes
+       fi
+       if test x$HAVE_CMAN = xyes && \
+          test x$HAVE_DLM = xyes; then
+               { $as_echo "$as_me:$LINENO: result: Enabling clvmd cman cluster manager" >&5
+$as_echo "Enabling clvmd cman cluster manager" >&6; }
+               CLVMD="$CLVMD,cman"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+               CLVMD_NEEDS_QDISKD=yes
+       fi
+       if test x$HAVE_COROSYNC = xyes && \
+          test x$HAVE_QUORUM = xyes && \
+          test x$HAVE_CPG = xyes && \
+          test x$HAVE_DLM = xyes && \
+          test x$HAVE_CONFDB = xyes; then
+               { $as_echo "$as_me:$LINENO: result: Enabling clvmd corosync cluster manager" >&5
+$as_echo "Enabling clvmd corosync cluster manager" >&6; }
+               CLVMD="$CLVMD,corosync"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+       fi
+       if test x$HAVE_COROSYNC = xyes && \
+          test x$HAVE_CPG = xyes && \
+          test x$HAVE_SALCK = xyes; then
+               { $as_echo "$as_me:$LINENO: result: Enabling clvmd openais cluster manager" >&5
+$as_echo "Enabling clvmd openais cluster manager" >&6; }
+               CLVMD="$CLVMD,openais"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+       fi
+       if test x$CLVMD_NEEDS_QDISKD != xno; then
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+       fi
+       if test x$CLVMD = xnone; then
+               { $as_echo "$as_me:$LINENO: result: Disabling clvmd build. No cluster manager detected." >&5
+$as_echo "Disabling clvmd build. No cluster manager detected." >&6; }
+       fi
+fi
+
+################################################################################
+if test "x$CLVMD" != xnone; then
+
+# Check whether --with-clvmd-pidfile was given.
+if test "${with_clvmd_pidfile+set}" = set; then
+  withval=$with_clvmd_pidfile; CLVMD_PIDFILE=$withval
+else
+  CLVMD_PIDFILE="/var/run/clvmd.pid"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CLVMD_PIDFILE "$CLVMD_PIDFILE"
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to build cluster mirror log daemon" >&5
+$as_echo_n "checking whether to build cluster mirror log daemon... " >&6; }
+# Check whether --enable-cmirrord was given.
+if test "${enable_cmirrord+set}" = set; then
+  enableval=$enable_cmirrord; CMIRRORD=$enableval
+else
+  CMIRRORD=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $CMIRRORD" >&5
+$as_echo "$CMIRRORD" >&6; }
+
+BUILD_CMIRRORD=$CMIRRORD
+
+################################################################################
+if test "x$BUILD_CMIRRORD" = xyes; then
+
+# Check whether --with-cmirrord-pidfile was given.
+if test "${with_cmirrord_pidfile+set}" = set; then
+  withval=$with_cmirrord_pidfile; CMIRRORD_PIDFILE=$withval
+else
+  CMIRRORD_PIDFILE="/var/run/cmirrord.pid"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CMIRRORD_PIDFILE "$CMIRRORD_PIDFILE"
+_ACEOF
+
+fi
+
+################################################################################
+if [ "x$BUILD_CMIRRORD" = xyes ]; then
+               if  test x$PKGCONFIG_INIT != x1; then
+               pkg_config_init
+       fi
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for SACKPT" >&5
+$as_echo_n "checking for SACKPT... " >&6; }
+
+if test -n "$SACKPT_CFLAGS"; then
+    pkg_cv_SACKPT_CFLAGS="$SACKPT_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaCkpt\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libSaCkpt") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_SACKPT_CFLAGS=`$PKG_CONFIG --cflags "libSaCkpt" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SACKPT_LIBS"; then
+    pkg_cv_SACKPT_LIBS="$SACKPT_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaCkpt\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libSaCkpt") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_SACKPT_LIBS=`$PKG_CONFIG --libs "libSaCkpt" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               SACKPT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libSaCkpt" 2>&1`
+        else
+               SACKPT_PKG_ERRORS=`$PKG_CONFIG --print-errors "libSaCkpt" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$SACKPT_PKG_ERRORS" >&5
+
+       { { $as_echo "$as_me:$LINENO: error: Package requirements (libSaCkpt) were not met:
+
+$SACKPT_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&5
+$as_echo "$as_me: error: Package requirements (libSaCkpt) were not met:
+
+$SACKPT_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+else
+       SACKPT_CFLAGS=$pkg_cv_SACKPT_CFLAGS
+       SACKPT_LIBS=$pkg_cv_SACKPT_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       :
+fi
+       if test x$HAVE_CPG != xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CPG" >&5
+$as_echo_n "checking for CPG... " >&6; }
+
+if test -n "$CPG_CFLAGS"; then
+    pkg_cv_CPG_CFLAGS="$CPG_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CPG_CFLAGS=`$PKG_CONFIG --cflags "libcpg" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CPG_LIBS"; then
+    pkg_cv_CPG_LIBS="$CPG_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_CPG_LIBS=`$PKG_CONFIG --libs "libcpg" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CPG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcpg" 2>&1`
+        else
+               CPG_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcpg" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CPG_PKG_ERRORS" >&5
+
+       { { $as_echo "$as_me:$LINENO: error: Package requirements (libcpg) were not met:
+
+$CPG_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&5
+$as_echo "$as_me: error: Package requirements (libcpg) were not met:
+
+$CPG_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }; }
+else
+       CPG_CFLAGS=$pkg_cv_CPG_CFLAGS
+       CPG_LIBS=$pkg_cv_CPG_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+       :
+fi
+       fi
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable debugging" >&5
+$as_echo_n "checking whether to enable debugging... " >&6; }
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then
+  enableval=$enable_debug; DEBUG=$enableval
+else
+  DEBUG=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DEBUG" >&5
+$as_echo "$DEBUG" >&6; }
+
+if test x$DEBUG = xyes; then
+       COPTIMISE_FLAG=
+else
+       CSCOPE_CMD=
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for C optimisation flag" >&5
+$as_echo_n "checking for C optimisation flag... " >&6; }
+
+# Check whether --with-optimisation was given.
+if test "${with_optimisation+set}" = set; then
+  withval=$with_optimisation; COPTIMISE_FLAG=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $COPTIMISE_FLAG" >&5
+$as_echo "$COPTIMISE_FLAG" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to gather gcov profiling data" >&5
+$as_echo_n "checking whether to gather gcov profiling data... " >&6; }
+# Check whether --enable-profiling was given.
+if test "${enable_profiling+set}" = set; then
+  enableval=$enable_profiling; PROFILING=$enableval
+else
+  PROFILING=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $PROFILING" >&5
+$as_echo "$PROFILING" >&6; }
+
+if test "x$PROFILING" = xyes; then
+  COPTIMISE_FLAG="$COPTIMISE_FLAG -fprofile-arcs -ftest-coverage"
+  # Extract the first word of "lcov", so it can be a program name with args.
+set dummy lcov; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_LCOV+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $LCOV in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_LCOV="$LCOV" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_LCOV="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+LCOV=$ac_cv_path_LCOV
+if test -n "$LCOV"; then
+  { $as_echo "$as_me:$LINENO: result: $LCOV" >&5
+$as_echo "$LCOV" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  # Extract the first word of "genhtml", so it can be a program name with args.
+set dummy genhtml; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_GENHTML+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $GENHTML in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_GENHTML="$GENHTML" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_GENHTML="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+GENHTML=$ac_cv_path_GENHTML
+if test -n "$GENHTML"; then
+  { $as_echo "$as_me:$LINENO: result: $GENHTML" >&5
+$as_echo "$GENHTML" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  if test -z "$LCOV" -o -z "$GENHTML"; then
+    { { $as_echo "$as_me:$LINENO: error: lcov and genhtml are required for profiling" >&5
+$as_echo "$as_me: error: lcov and genhtml are required for profiling" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  # Extract the first word of "genpng", so it can be a program name with args.
+set dummy genpng; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_GENPNG+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $GENPNG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_GENPNG="$GENPNG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_GENPNG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+GENPNG=$ac_cv_path_GENPNG
+if test -n "$GENPNG"; then
+  { $as_echo "$as_me:$LINENO: result: $GENPNG" >&5
+$as_echo "$GENPNG" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  if test -n "$GENPNG"; then
+    { $as_echo "$as_me:$LINENO: checking whether $GENPNG has all required modules" >&5
+$as_echo_n "checking whether $GENPNG has all required modules... " >&6; }
+    if $GENPNG --help > /dev/null 2>&1 ; then
+      { $as_echo "$as_me:$LINENO: result: ok" >&5
+$as_echo "ok" >&6; }
+      GENHTML="$GENHTML --frames"
+    else
+      { $as_echo "$as_me:$LINENO: result: not supported" >&5
+$as_echo "not supported" >&6; }
+      { $as_echo "$as_me:$LINENO: WARNING: GD.pm perl module is not installed" >&5
+$as_echo "$as_me: WARNING: GD.pm perl module is not installed" >&2;}
+      GENPNG=
+    fi
+  fi
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable unit testing" >&5
+$as_echo_n "checking whether to enable unit testing... " >&6; }
+# Check whether --enable-testing was given.
+if test "${enable_testing+set}" = set; then
+  enableval=$enable_testing; TESTING=$enableval
+else
+  TESTING=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $TESTING" >&5
+$as_echo "$TESTING" >&6; }
+
+if test "$TESTING" = yes; then
+   # Extract the first word of "ruby1.9", so it can be a program name with args.
+set dummy ruby1.9; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_RUBY19+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $RUBY19 in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_RUBY19="$RUBY19" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_RUBY19="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+RUBY19=$ac_cv_path_RUBY19
+if test -n "$RUBY19"; then
+  { $as_echo "$as_me:$LINENO: result: $RUBY19" >&5
+$as_echo "$RUBY19" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+   # Extract the first word of "valgrind", so it can be a program name with args.
+set dummy valgrind; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_VALGRIND+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $VALGRIND in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_VALGRIND="$VALGRIND" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_VALGRIND="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+VALGRIND=$ac_cv_path_VALGRIND
+if test -n "$VALGRIND"; then
+  { $as_echo "$as_me:$LINENO: result: $VALGRIND" >&5
+$as_echo "$VALGRIND" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+   if test -z "$RUBY19" -o -z "$VALGRIND"; then
+       { { $as_echo "$as_me:$LINENO: error: ruby1.9 and valgrind are required for testing" >&5
+$as_echo "$as_me: error: ruby1.9 and valgrind are required for testing" >&2;}
+   { (exit 1); exit 1; }; }
+   fi
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable valgrind awareness of pools" >&5
+$as_echo_n "checking whether to enable valgrind awareness of pools... " >&6; }
+# Check whether --enable-valgrind_pool was given.
+if test "${enable_valgrind_pool+set}" = set; then
+  enableval=$enable_valgrind_pool; VALGRIND_POOL=$enableval
+else
+  VALGRIND_POOL=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $VALGRIND_POOL" >&5
+$as_echo "$VALGRIND_POOL" >&6; }
+
+if test "$VALGRIND_POOL" = yes; then
+
+for ac_header in valgrind/memcheck.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+cat >>confdefs.h <<\_ACEOF
+#define VALGRIND_POOL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to use device-mapper" >&5
+$as_echo_n "checking whether to use device-mapper... " >&6; }
+# Check whether --enable-devmapper was given.
+if test "${enable_devmapper+set}" = set; then
+  enableval=$enable_devmapper; DEVMAPPER=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DEVMAPPER" >&5
+$as_echo "$DEVMAPPER" >&6; }
+
+if test x$DEVMAPPER = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DEVMAPPER_SUPPORT 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable synchronisation with udev processing" >&5
+$as_echo_n "checking whether to enable synchronisation with udev processing... " >&6; }
+# Check whether --enable-udev_sync was given.
+if test "${enable_udev_sync+set}" = set; then
+  enableval=$enable_udev_sync; UDEV_SYNC=$enableval
+else
+  UDEV_SYNC=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $UDEV_SYNC" >&5
+$as_echo "$UDEV_SYNC" >&6; }
+
+if test x$UDEV_SYNC = xyes; then
+       { $as_echo "$as_me:$LINENO: checking for udev_queue_get_udev_is_active in -ludev" >&5
+$as_echo_n "checking for udev_queue_get_udev_is_active in -ludev... " >&6; }
+if test "${ac_cv_lib_udev_udev_queue_get_udev_is_active+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ludev  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char udev_queue_get_udev_is_active ();
+int
+main ()
+{
+return udev_queue_get_udev_is_active ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_udev_udev_queue_get_udev_is_active=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_udev_udev_queue_get_udev_is_active=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_udev_udev_queue_get_udev_is_active" >&5
+$as_echo "$ac_cv_lib_udev_udev_queue_get_udev_is_active" >&6; }
+if test "x$ac_cv_lib_udev_udev_queue_get_udev_is_active" = x""yes; then
+  UDEV_PC="libudev"; UDEV_LIBS="-ludev"
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out... libudev library is required" >&5
+$as_echo "$as_me: error: bailing out... libudev library is required" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+cat >>confdefs.h <<\_ACEOF
+#define UDEV_SYNC_SUPPORT 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether to enable installation of udev rules required for synchronisation" >&5
+$as_echo_n "checking whether to enable installation of udev rules required for synchronisation... " >&6; }
+# Check whether --enable-udev_rules was given.
+if test "${enable_udev_rules+set}" = set; then
+  enableval=$enable_udev_rules; UDEV_RULES=$enableval
+else
+  UDEV_RULES=$UDEV_SYNC
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $UDEV_RULES" >&5
+$as_echo "$UDEV_RULES" >&6; }
+
+################################################################################
+# Check whether --enable-compat was given.
+if test "${enable_compat+set}" = set; then
+  enableval=$enable_compat; DM_COMPAT=$enableval
+else
+  DM_COMPAT=no
+fi
+
+
+################################################################################
+# Check whether --enable-units-compat was given.
+if test "${enable_units_compat+set}" = set; then
+  enableval=$enable_units_compat; UNITS_COMPAT=$enableval
+else
+  UNITS_COMPAT=no
+fi
+
+
+if test x$UNITS_COMPAT = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DEFAULT_SI_UNIT_CONSISTENCY 0
+_ACEOF
+
+fi
+
+################################################################################
+# Check whether --enable-ioctl was given.
+if test "${enable_ioctl+set}" = set; then
+  enableval=$enable_ioctl; DM_IOCTLS=$enableval
+fi
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable O_DIRECT" >&5
+$as_echo_n "checking whether to enable O_DIRECT... " >&6; }
+# Check whether --enable-o_direct was given.
+if test "${enable_o_direct+set}" = set; then
+  enableval=$enable_o_direct; ODIRECT=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ODIRECT" >&5
+$as_echo "$ODIRECT" >&6; }
+
+if test x$ODIRECT = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define O_DIRECT_SUPPORT 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to build liblvm2app.so application library" >&5
+$as_echo_n "checking whether to build liblvm2app.so application library... " >&6; }
+# Check whether --enable-applib was given.
+if test "${enable_applib+set}" = set; then
+  enableval=$enable_applib; APPLIB=$enableval
+else
+  APPLIB=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $APPLIB" >&5
+$as_echo "$APPLIB" >&6; }
+
+test x$APPLIB = xyes \
+  && LVM2APP_LIB=-llvm2app \
+  || LVM2APP_LIB=
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to compile liblvm2cmd.so" >&5
+$as_echo_n "checking whether to compile liblvm2cmd.so... " >&6; }
+# Check whether --enable-cmdlib was given.
+if test "${enable_cmdlib+set}" = set; then
+  enableval=$enable_cmdlib; CMDLIB=$enableval
+else
+  CMDLIB=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $CMDLIB" >&5
+$as_echo "$CMDLIB" >&6; }
+
+test x$CMDLIB = xyes \
+  && LVM2CMD_LIB=-llvm2cmd \
+  || LVM2CMD_LIB=
+
+################################################################################
+# Check whether --enable-pkgconfig was given.
+if test "${enable_pkgconfig+set}" = set; then
+  enableval=$enable_pkgconfig; PKGCONFIG=$enableval
+else
+  PKGCONFIG=no
+fi
+
+
+################################################################################
+# Check whether --enable-write_install was given.
+if test "${enable_write_install+set}" = set; then
+  enableval=$enable_write_install; WRITE_INSTALL=$enableval
+else
+  WRITE_INSTALL=no
+fi
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to install fsadm" >&5
+$as_echo_n "checking whether to install fsadm... " >&6; }
+# Check whether --enable-fsadm was given.
+if test "${enable_fsadm+set}" = set; then
+  enableval=$enable_fsadm; FSADM=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $FSADM" >&5
+$as_echo "$FSADM" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to use dmeventd" >&5
+$as_echo_n "checking whether to use dmeventd... " >&6; }
+# Check whether --enable-dmeventd was given.
+if test "${enable_dmeventd+set}" = set; then
+  enableval=$enable_dmeventd; DMEVENTD=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DMEVENTD" >&5
+$as_echo "$DMEVENTD" >&6; }
+
+BUILD_DMEVENTD=$DMEVENTD
+
+if test x$DMEVENTD = xyes; then
+   if test x$MIRRORS != xinternal; then
+      { { $as_echo "$as_me:$LINENO: error: --enable-dmeventd currently requires --with-mirrors=internal
+      " >&5
+$as_echo "$as_me: error: --enable-dmeventd currently requires --with-mirrors=internal
+      " >&2;}
+   { (exit 1); exit 1; }; }
+   fi
+   if test x$CMDLIB = xno; then
+      { { $as_echo "$as_me:$LINENO: error: --enable-dmeventd requires --enable-cmdlib to be used as well
+      " >&5
+$as_echo "$as_me: error: --enable-dmeventd requires --enable-cmdlib to be used as well
+      " >&2;}
+   { (exit 1); exit 1; }; }
+   fi
+fi
+
+if test x$DMEVENTD = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DMEVENTD 1
+_ACEOF
+
+fi
+
+################################################################################
+
+{ $as_echo "$as_me:$LINENO: checking for getline in -lc" >&5
+$as_echo_n "checking for getline in -lc... " >&6; }
+if test "${ac_cv_lib_c_getline+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getline ();
+int
+main ()
+{
+return getline ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_c_getline=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_c_getline=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_c_getline" >&5
+$as_echo "$ac_cv_lib_c_getline" >&6; }
+if test "x$ac_cv_lib_c_getline" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETLINE 1
+_ACEOF
+
+fi
+
+
+################################################################################
+
+{ $as_echo "$as_me:$LINENO: checking for canonicalize_file_name in -lc" >&5
+$as_echo_n "checking for canonicalize_file_name in -lc... " >&6; }
+if test "${ac_cv_lib_c_canonicalize_file_name+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char canonicalize_file_name ();
+int
+main ()
+{
+return canonicalize_file_name ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_c_canonicalize_file_name=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_c_canonicalize_file_name=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_c_canonicalize_file_name" >&5
+$as_echo "$ac_cv_lib_c_canonicalize_file_name" >&6; }
+if test "x$ac_cv_lib_c_canonicalize_file_name" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CANONICALIZE_FILE_NAME 1
+_ACEOF
+
+fi
+
+
+################################################################################
+if [ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ];
+ then  exec_prefix="";
+fi;
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LIBDL 1
+_ACEOF
+
+       DL_LIBS="-ldl"
+       HAVE_LIBDL=yes
+else
+
+       DL_LIBS=
+       HAVE_LIBDL=no
+fi
+
+
+################################################################################
+if [ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \
+      -o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \
+      \) -a "x$STATIC_LINK" = xyes ];
+ then  { { $as_echo "$as_me:$LINENO: error: Features cannot be 'shared' when building statically
+" >&5
+$as_echo "$as_me: error: Features cannot be 'shared' when building statically
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+################################################################################
+if [ "$DMEVENTD" = yes -o "$CLVMD" != none ] ; then
+       { $as_echo "$as_me:$LINENO: checking for pthread_mutex_lock in -lpthread" >&5
+$as_echo_n "checking for pthread_mutex_lock in -lpthread... " >&6; }
+if test "${ac_cv_lib_pthread_pthread_mutex_lock+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_mutex_lock ();
+int
+main ()
+{
+return pthread_mutex_lock ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_pthread_pthread_mutex_lock=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_pthread_pthread_mutex_lock=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_mutex_lock" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_mutex_lock" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_mutex_lock" = x""yes; then
+  PTHREAD_LIBS="-lpthread"
+else
+  hard_bailout
+fi
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable selinux support" >&5
+$as_echo_n "checking whether to enable selinux support... " >&6; }
+# Check whether --enable-selinux was given.
+if test "${enable_selinux+set}" = set; then
+  enableval=$enable_selinux; SELINUX=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $SELINUX" >&5
+$as_echo "$SELINUX" >&6; }
+
+################################################################################
+if test x$SELINUX = xyes; then
+       { $as_echo "$as_me:$LINENO: checking for sepol_check_context in -lsepol" >&5
+$as_echo_n "checking for sepol_check_context in -lsepol... " >&6; }
+if test "${ac_cv_lib_sepol_sepol_check_context+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsepol  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sepol_check_context ();
+int
+main ()
+{
+return sepol_check_context ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_sepol_sepol_check_context=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_sepol_sepol_check_context=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_sepol_sepol_check_context" >&5
+$as_echo "$ac_cv_lib_sepol_sepol_check_context" >&6; }
+if test "x$ac_cv_lib_sepol_sepol_check_context" = x""yes; then
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SEPOL 1
+_ACEOF
+
+               SELINUX_LIBS="-lsepol"
+fi
+
+
+       { $as_echo "$as_me:$LINENO: checking for is_selinux_enabled in -lselinux" >&5
+$as_echo_n "checking for is_selinux_enabled in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_is_selinux_enabled+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char is_selinux_enabled ();
+int
+main ()
+{
+return is_selinux_enabled ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_selinux_is_selinux_enabled=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_selinux_is_selinux_enabled=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_is_selinux_enabled" >&5
+$as_echo "$ac_cv_lib_selinux_is_selinux_enabled" >&6; }
+if test "x$ac_cv_lib_selinux_is_selinux_enabled" = x""yes; then
+
+
+for ac_header in selinux/selinux.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  hard_bailout
+fi
+
+done
+
+
+for ac_header in selinux/label.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SELINUX 1
+_ACEOF
+
+               SELINUX_LIBS="-lselinux $SELINUX_LIBS"
+               SELINUX_PC="libselinux"
+               HAVE_SELINUX=yes
+else
+
+               { $as_echo "$as_me:$LINENO: WARNING: Disabling selinux" >&5
+$as_echo "$as_me: WARNING: Disabling selinux" >&2;}
+               SELINUX_LIBS=
+               SELINUX_PC=
+               HAVE_SELINUX=no
+fi
+
+fi
+
+################################################################################
+if test x$REALTIME = xyes; then
+       { $as_echo "$as_me:$LINENO: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if test "${ac_cv_lib_rt_clock_gettime+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_rt_clock_gettime=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_rt_clock_gettime=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = x""yes; then
+  HAVE_REALTIME=yes
+else
+  HAVE_REALTIME=no
+fi
+
+
+       if test x$HAVE_REALTIME = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALTIME 1
+_ACEOF
+
+               LIBS="-lrt $LIBS"
+       else
+               { $as_echo "$as_me:$LINENO: WARNING: Disabling realtime clock" >&5
+$as_echo "$as_me: WARNING: Disabling realtime clock" >&2;}
+       fi
+fi
+
+################################################################################
+
+for ac_header in getopt.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETOPTLONG 1
+_ACEOF
+
+fi
+
+done
+
+
+################################################################################
+if test x$READLINE != xno; then
+       lvm_saved_libs=$LIBS
+       { $as_echo "$as_me:$LINENO: checking for library containing tgetent" >&5
+$as_echo_n "checking for library containing tgetent... " >&6; }
+if test "${ac_cv_search_tgetent+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' tinfo ncurses curses termcap termlib; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_search_tgetent=$ac_res
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_tgetent+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_tgetent+set}" = set; then
+  :
+else
+  ac_cv_search_tgetent=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_tgetent" >&5
+$as_echo "$ac_cv_search_tgetent" >&6; }
+ac_res=$ac_cv_search_tgetent
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  READLINE_LIBS=$ac_cv_search_tgetent
+else
+
+               if test "$READLINE" = yes; then
+                       { { $as_echo "$as_me:$LINENO: error: termcap could not be found which is required for the
+--enable-readline option (which is enabled by default).  Either disable readline
+support with --disable-readline or download and install termcap from:
+       ftp.gnu.org/gnu/termcap
+Note: if you are using precompiled packages you will also need the development
+  package as well (which may be called termcap-devel or something similar).
+Note: (n)curses also seems to work as a substitute for termcap.  This was
+  not found either - but you could try installing that as well." >&5
+$as_echo "$as_me: error: termcap could not be found which is required for the
+--enable-readline option (which is enabled by default).  Either disable readline
+support with --disable-readline or download and install termcap from:
+       ftp.gnu.org/gnu/termcap
+Note: if you are using precompiled packages you will also need the development
+  package as well (which may be called termcap-devel or something similar).
+Note: (n)curses also seems to work as a substitute for termcap.  This was
+  not found either - but you could try installing that as well." >&2;}
+   { (exit 1); exit 1; }; }
+               fi
+fi
+
+               { $as_echo "$as_me:$LINENO: checking for readline in -lreadline" >&5
+$as_echo_n "checking for readline in -lreadline... " >&6; }
+if test "${ac_cv_lib_readline_readline+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char readline ();
+int
+main ()
+{
+return readline ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_readline_readline=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_readline_readline=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_readline_readline" >&5
+$as_echo "$ac_cv_lib_readline_readline" >&6; }
+if test "x$ac_cv_lib_readline_readline" = x""yes; then
+
+
+cat >>confdefs.h <<\_ACEOF
+#define READLINE_SUPPORT 1
+_ACEOF
+
+                               LIBS=$lvm_saved_libs
+               { $as_echo "$as_me:$LINENO: checking for rl_line_buffer in -lreadline" >&5
+$as_echo_n "checking for rl_line_buffer in -lreadline... " >&6; }
+if test "${ac_cv_lib_readline_rl_line_buffer+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char rl_line_buffer ();
+int
+main ()
+{
+return rl_line_buffer ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_readline_rl_line_buffer=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_cv_lib_readline_rl_line_buffer=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_readline_rl_line_buffer" >&5
+$as_echo "$ac_cv_lib_readline_rl_line_buffer" >&6; }
+if test "x$ac_cv_lib_readline_rl_line_buffer" = x""yes; then
+   READLINE_LIBS="-lreadline"
+else
+
+                         { $as_echo "$as_me:$LINENO: result: linking -lreadline with $READLINE_LIBS needed" >&5
+$as_echo "linking -lreadline with $READLINE_LIBS needed" >&6; }
+                         READLINE_LIBS="-lreadline $READLINE_LIBS"
+
+fi
+
+else
+
+               READLINE_LIBS=
+               if test "$READLINE" = yes; then
+                       { { $as_echo "$as_me:$LINENO: error: GNU Readline could not be found which is required for the
+--enable-readline option (which is enabled by default).  Either disable readline
+support with --disable-readline or download and install readline from:
+       ftp.gnu.org/gnu/readline
+Note: if you are using precompiled packages you will also need the development
+package as well (which may be called readline-devel or something similar)." >&5
+$as_echo "$as_me: error: GNU Readline could not be found which is required for the
+--enable-readline option (which is enabled by default).  Either disable readline
+support with --disable-readline or download and install readline from:
+       ftp.gnu.org/gnu/readline
+Note: if you are using precompiled packages you will also need the development
+package as well (which may be called readline-devel or something similar)." >&2;}
+   { (exit 1); exit 1; }; }
+               fi
+fi
+
+       LIBS="$READLINE_LIBS $lvm_saved_libs"
+
+for ac_func in rl_completion_matches
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+       LIBS=$lvm_saved_libs
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable internationalisation" >&5
+$as_echo_n "checking whether to enable internationalisation... " >&6; }
+# Check whether --enable-nls was given.
+if test "${enable_nls+set}" = set; then
+  enableval=$enable_nls; INTL=$enableval
+else
+  INTL=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $INTL" >&5
+$as_echo "$INTL" >&6; }
+
+if test x$INTL = xyes; then
+# FIXME - Move this - can be device-mapper too
+       INTL_PACKAGE="lvm2"
+       # Extract the first word of "msgfmt", so it can be a program name with args.
+set dummy msgfmt; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_MSGFMT+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $MSGFMT in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_MSGFMT="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+MSGFMT=$ac_cv_path_MSGFMT
+if test -n "$MSGFMT"; then
+  { $as_echo "$as_me:$LINENO: result: $MSGFMT" >&5
+$as_echo "$MSGFMT" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+       if [ "x$MSGFMT" == x ];
+               then  { { $as_echo "$as_me:$LINENO: error: msgfmt not found in path $PATH
+               " >&5
+$as_echo "$as_me: error: msgfmt not found in path $PATH
+               " >&2;}
+   { (exit 1); exit 1; }; }
+       fi;
+
+
+# Check whether --with-localedir was given.
+if test "${with_localedir+set}" = set; then
+  withval=$with_localedir; LOCALEDIR=$withval
+else
+  LOCALEDIR='${prefix}/share/locale'
+fi
+
+fi
+
+################################################################################
+
+# Check whether --with-confdir was given.
+if test "${with_confdir+set}" = set; then
+  withval=$with_confdir; CONFDIR=$withval
+else
+  CONFDIR="/etc"
+fi
+
+
+
+# Check whether --with-staticdir was given.
+if test "${with_staticdir+set}" = set; then
+  withval=$with_staticdir; STATICDIR=$withval
+else
+  STATICDIR='${exec_prefix}/sbin'
+fi
+
+
+
+# Check whether --with-usrlibdir was given.
+if test "${with_usrlibdir+set}" = set; then
+  withval=$with_usrlibdir; usrlibdir=$withval
+else
+  usrlibdir='${prefix}/lib'
+fi
+
+
+
+# Check whether --with-usrsbindir was given.
+if test "${with_usrsbindir+set}" = set; then
+  withval=$with_usrsbindir; usrsbindir=$withval
+else
+  usrsbindir='${prefix}/sbin'
+fi
+
+
+################################################################################
+
+# Check whether --with-udev_prefix was given.
+if test "${with_udev_prefix+set}" = set; then
+  withval=$with_udev_prefix; udev_prefix=$withval
+else
+  udev_prefix='${exec_prefix}'
+fi
+
+
+
+# Check whether --with-udevdir was given.
+if test "${with_udevdir+set}" = set; then
+  withval=$with_udevdir; udevdir=$withval
+else
+  udevdir='${udev_prefix}/lib/udev/rules.d'
+fi
+
+
+################################################################################
+if test x$READLINE = xyes; then
+
+
+for ac_header in readline/readline.h readline/history.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$CLVMD != xnone; then
+
+
+
+
+
+
+
+
+
+
+for ac_header in mntent.h netdb.h netinet/in.h pthread.h search.h sys/mount.h sys/socket.h sys/uio.h sys/un.h utmpx.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+
+
+
+
+for ac_func in dup2 getmntent memmove select socket
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+done
+
+       # getmntent is in the standard C library on UNICOS, in -lsun on Irix 4,
+# -lseq on Dynix/PTX, -lgen on Unixware.
+{ $as_echo "$as_me:$LINENO: checking for library containing getmntent" >&5
+$as_echo_n "checking for library containing getmntent... " >&6; }
+if test "${ac_cv_search_getmntent+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getmntent ();
+int
+main ()
+{
+return getmntent ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' sun seq gen; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_search_getmntent=$ac_res
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext
+  if test "${ac_cv_search_getmntent+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_getmntent+set}" = set; then
+  :
+else
+  ac_cv_search_getmntent=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_getmntent" >&5
+$as_echo "$ac_cv_search_getmntent" >&6; }
+ac_res=$ac_cv_search_getmntent
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  ac_cv_func_getmntent=yes
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETMNTENT 1
+_ACEOF
+
+else
+  ac_cv_func_getmntent=no
+fi
+
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if test "${ac_cv_func_select_args+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+  for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+                                           $ac_arg234, $ac_arg234, $ac_arg234,
+                                           $ac_arg5);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+ done
+done
+# Provide a safe default value.
+: ${ac_cv_func_select_args='int,int *,struct timeval *'}
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+fi
+
+if test x$CLUSTER != xnone; then
+
+
+for ac_header in sys/socket.h sys/un.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+for ac_func in socket
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        $as_test_x conftest$ac_exeext
+       }; then
+  eval "$as_ac_var=yes"
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+done
+
+fi
+
+if test x$DMEVENTD = xyes; then
+
+for ac_header in arpa/inet.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$HAVE_LIBDL = xyes; then
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$INTL = xyes; then
+
+for ac_header in libintl.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$UDEV_SYNC = xyes; then
+
+
+for ac_header in sys/ipc.h sys/sem.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  ac_header_compiler=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then
+  ac_header_preproc=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  $as_echo_n "(cached) " >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+              { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+                $as_echo "$as_val"'`
+   if test "x$as_val" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+################################################################################
+# Extract the first word of "modprobe", so it can be a program name with args.
+set dummy modprobe; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_MODPROBE_CMD+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  case $MODPROBE_CMD in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_MODPROBE_CMD="$MODPROBE_CMD" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_MODPROBE_CMD="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+MODPROBE_CMD=$ac_cv_path_MODPROBE_CMD
+if test -n "$MODPROBE_CMD"; then
+  { $as_echo "$as_me:$LINENO: result: $MODPROBE_CMD" >&5
+$as_echo "$MODPROBE_CMD" >&6; }
+else
+  { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+if test x$MODPROBE_CMD != x; then
+
+cat >>confdefs.h <<_ACEOF
+#define MODPROBE_CMD "$MODPROBE_CMD"
+_ACEOF
+
+fi
+
+
+lvm_exec_prefix=$exec_prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$ac_default_prefix
+
+cat >>confdefs.h <<_ACEOF
+#define LVM_PATH "$lvm_exec_prefix/sbin/lvm"
+_ACEOF
+
+
+if  test "$CLVMD" != none; then
+        clvmd_prefix=$ac_default_prefix
+        test "$prefix" != NONE && clvmd_prefix=$prefix
+
+cat >>confdefs.h <<_ACEOF
+#define CLVMD_PATH "$clvmd_prefix/sbin/clvmd"
+_ACEOF
+
+fi
+
+################################################################################
+if test "$BUILD_DMEVENTD" = yes; then
+
+# Check whether --with-dmeventd-pidfile was given.
+if test "${with_dmeventd_pidfile+set}" = set; then
+  withval=$with_dmeventd_pidfile; DMEVENTD_PIDFILE=$withval
+else
+  DMEVENTD_PIDFILE="/var/run/dmeventd.pid"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PIDFILE "$DMEVENTD_PIDFILE"
+_ACEOF
+
+fi
+
+if test "$BUILD_DMEVENTD" = yes; then
+
+# Check whether --with-dmeventd-path was given.
+if test "${with_dmeventd_path+set}" = set; then
+  withval=$with_dmeventd_path; DMEVENTD_PATH=$withval
+else
+  DMEVENTD_PATH="$lvm_exec_prefix/sbin/dmeventd"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PATH "$DMEVENTD_PATH"
+_ACEOF
+
+fi
+
+
+
+
+# Check whether --with-default-run-dir was given.
+if test "${with_default_run_dir+set}" = set; then
+  withval=$with_default_run_dir;  DEFAULT_RUN_DIR="$withval"
+else
+   DEFAULT_RUN_DIR="/var/run/lvm"
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_RUN_DIR "$DEFAULT_RUN_DIR"
+_ACEOF
+
+
+################################################################################
+
+# Check whether --with-default-system-dir was given.
+if test "${with_default_system_dir+set}" = set; then
+  withval=$with_default_system_dir; DEFAULT_SYS_DIR=$withval
+else
+  DEFAULT_SYS_DIR="/etc/lvm"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_SYS_DIR "$DEFAULT_SYS_DIR"
+_ACEOF
+
+
+
+# Check whether --with-default-archive-subdir was given.
+if test "${with_default_archive_subdir+set}" = set; then
+  withval=$with_default_archive_subdir; DEFAULT_ARCHIVE_SUBDIR=$withval
+else
+  DEFAULT_ARCHIVE_SUBDIR=archive
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_ARCHIVE_SUBDIR "$DEFAULT_ARCHIVE_SUBDIR"
+_ACEOF
+
+
+
+# Check whether --with-default-backup-subdir was given.
+if test "${with_default_backup_subdir+set}" = set; then
+  withval=$with_default_backup_subdir; DEFAULT_BACKUP_SUBDIR=$withval
+else
+  DEFAULT_BACKUP_SUBDIR=backup
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_BACKUP_SUBDIR "$DEFAULT_BACKUP_SUBDIR"
+_ACEOF
+
+
+
+# Check whether --with-default-cache-subdir was given.
+if test "${with_default_cache_subdir+set}" = set; then
+  withval=$with_default_cache_subdir; DEFAULT_CACHE_SUBDIR=$withval
+else
+  DEFAULT_CACHE_SUBDIR=cache
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_CACHE_SUBDIR "$DEFAULT_CACHE_SUBDIR"
+_ACEOF
+
+
+
+# Check whether --with-default-locking-dir was given.
+if test "${with_default_locking_dir+set}" = set; then
+  withval=$with_default_locking_dir; DEFAULT_LOCK_DIR=$withval
+else
+  DEFAULT_LOCK_DIR="/var/lock/lvm"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_LOCK_DIR "$DEFAULT_LOCK_DIR"
+_ACEOF
+
+
+################################################################################
+
+# Check whether --with-default-data-alignment was given.
+if test "${with_default_data_alignment+set}" = set; then
+  withval=$with_default_data_alignment; DEFAULT_DATA_ALIGNMENT=$withval
+else
+  DEFAULT_DATA_ALIGNMENT=1
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_DATA_ALIGNMENT $DEFAULT_DATA_ALIGNMENT
+_ACEOF
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for kernel interface choice" >&5
+$as_echo_n "checking for kernel interface choice... " >&6; }
+
+# Check whether --with-interface was given.
+if test "${with_interface+set}" = set; then
+  withval=$with_interface; interface=$withval
+else
+  interface=ioctl
+fi
+
+if [ "x$interface" != xioctl ];
+then
+  { { $as_echo "$as_me:$LINENO: error: --with-interface=ioctl required. fs no longer supported." >&5
+$as_echo "$as_me: error: --with-interface=ioctl required. fs no longer supported." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+{ $as_echo "$as_me:$LINENO: result: $interface" >&5
+$as_echo "$interface" >&6; }
+
+################################################################################
+DM_LIB_VERSION="\"`cat "$srcdir"/VERSION_DM 2>/dev/null || echo Unknown`\""
+
+cat >>confdefs.h <<_ACEOF
+#define DM_LIB_VERSION $DM_LIB_VERSION
+_ACEOF
+
+
+DM_LIB_PATCHLEVEL=`cat "$srcdir"/VERSION_DM | $AWK -F '[-. ]' '{printf "%s.%s.%s",$1,$2,$3}'`
+
+LVM_VERSION="\"`cat "$srcdir"/VERSION 2>/dev/null || echo Unknown`\""
+
+VER=`cat "$srcdir"/VERSION`
+LVM_RELEASE_DATE="\"`echo $VER | $SED 's/.* (//;s/).*//'`\""
+VER=`echo "$VER" | $AWK '{print $1}'`
+LVM_RELEASE="\"`echo "$VER" | $AWK -F '-' '{print $2}'`\""
+VER=`echo "$VER" | $AWK -F '-' '{print $1}'`
+LVM_MAJOR=`echo "$VER" | $AWK -F '.' '{print $1}'`
+LVM_MINOR=`echo "$VER" | $AWK -F '.' '{print $2}'`
+LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[(.]' '{print $3}'`
+LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'`
+
+################################################################################
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+################################################################################
+ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile doc/Makefile doc/example.conf include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/snapshot/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_monitoring_init_red_hat scripts/Makefile test/Makefile test/api/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) $as_unset $ac_var ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    test "x$cache_file" != "x/dev/null" &&
+      { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+    cat confcache >$cache_file
+  else
+    { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in
+  *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+case $0 in
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line after each line using $LINENO; the second 'sed'
+  # does the real work.  The second script uses 'N' to pair each
+  # line-number line with the line containing $LINENO, and appends
+  # trailing '-' during substitution so that $LINENO is not a special
+  # case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # scripts with optimization help from Paolo Bonzini.  Blame Lee
+  # E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+  case `echo 'x\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  *)   ECHO_C='\c';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -p'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -p'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -p'
+  fi
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+  as_test_x='test -x'
+else
+  if ls -dL / >/dev/null 2>&1; then
+    as_ls_L_option=L
+  else
+    as_ls_L_option=
+  fi
+  as_test_x='
+    eval sh -c '\''
+      if test -d "$1"; then
+       test -d "$1/.";
+      else
+       case $1 in
+       -*)set "./$1";;
+       esac;
+       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+       ???[sx]*):;;*)false;;esac;fi
+    '\'' sh
+  '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.63.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTION]... [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.63,
+  with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    CONFIG_FILES="$CONFIG_FILES '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    { $as_echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { $as_echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "lib/misc/configure.h") CONFIG_HEADERS="$CONFIG_HEADERS lib/misc/configure.h" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "make.tmpl") CONFIG_FILES="$CONFIG_FILES make.tmpl" ;;
+    "daemons/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/Makefile" ;;
+    "daemons/clvmd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/clvmd/Makefile" ;;
+    "daemons/cmirrord/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/cmirrord/Makefile" ;;
+    "daemons/dmeventd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/Makefile" ;;
+    "daemons/dmeventd/libdevmapper-event.pc") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/libdevmapper-event.pc" ;;
+    "daemons/dmeventd/plugins/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/Makefile" ;;
+    "daemons/dmeventd/plugins/lvm2/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/lvm2/Makefile" ;;
+    "daemons/dmeventd/plugins/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/mirror/Makefile" ;;
+    "daemons/dmeventd/plugins/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/snapshot/Makefile" ;;
+    "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+    "doc/example.conf") CONFIG_FILES="$CONFIG_FILES doc/example.conf" ;;
+    "include/.symlinks") CONFIG_FILES="$CONFIG_FILES include/.symlinks" ;;
+    "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
+    "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
+    "lib/format1/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format1/Makefile" ;;
+    "lib/format_pool/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format_pool/Makefile" ;;
+    "lib/locking/Makefile") CONFIG_FILES="$CONFIG_FILES lib/locking/Makefile" ;;
+    "lib/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES lib/mirror/Makefile" ;;
+    "lib/replicator/Makefile") CONFIG_FILES="$CONFIG_FILES lib/replicator/Makefile" ;;
+    "lib/misc/lvm-version.h") CONFIG_FILES="$CONFIG_FILES lib/misc/lvm-version.h" ;;
+    "lib/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES lib/snapshot/Makefile" ;;
+    "libdm/Makefile") CONFIG_FILES="$CONFIG_FILES libdm/Makefile" ;;
+    "libdm/libdevmapper.pc") CONFIG_FILES="$CONFIG_FILES libdm/libdevmapper.pc" ;;
+    "liblvm/Makefile") CONFIG_FILES="$CONFIG_FILES liblvm/Makefile" ;;
+    "liblvm/liblvm2app.pc") CONFIG_FILES="$CONFIG_FILES liblvm/liblvm2app.pc" ;;
+    "man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;;
+    "po/Makefile") CONFIG_FILES="$CONFIG_FILES po/Makefile" ;;
+    "scripts/clvmd_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/clvmd_init_red_hat" ;;
+    "scripts/cmirrord_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/cmirrord_init_red_hat" ;;
+    "scripts/lvm2_monitoring_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_init_red_hat" ;;
+    "scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
+    "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
+    "test/api/Makefile") CONFIG_FILES="$CONFIG_FILES test/api/Makefile" ;;
+    "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;;
+    "udev/Makefile") CONFIG_FILES="$CONFIG_FILES udev/Makefile" ;;
+    "unit-tests/datastruct/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/datastruct/Makefile" ;;
+    "unit-tests/regex/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/regex/Makefile" ;;
+    "unit-tests/mm/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/mm/Makefile" ;;
+
+  *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp=
+  trap 'exit_status=$?
+  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} ||
+{
+   $as_echo "$as_me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr='\r'
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\).*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\).*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+  || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5
+$as_echo "$as_me: error: could not setup config files machinery" >&2;}
+   { (exit 1); exit 1; }; }
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[    ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_t=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_t"; then
+    break
+  elif $ac_last_try; then
+    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5
+$as_echo "$as_me: error: could not setup config headers machinery" >&2;}
+   { (exit 1); exit 1; }; }
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5
+$as_echo "$as_me: error: invalid tag $ac_tag" >&2;}
+   { (exit 1); exit 1; }; };;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+   { (exit 1); exit 1; }; };;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      ac_file_inputs="$ac_file_inputs '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:$LINENO: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$tmp/stdin" \
+      || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; } ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  { as_dir="$ac_dir"
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+   { (exit 1); exit 1; }; }; }
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+    s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+  || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined." >&2;}
+
+  rm -f "$tmp/stdin"
+  case $ac_file in
+  -) cat "$tmp/out" && rm -f "$tmp/out";;
+  *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+  esac \
+  || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+    } >"$tmp/config.h" \
+      || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+    if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$tmp/config.h" "$ac_file" \
+       || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+      || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5
+$as_echo "$as_me: error: could not create -" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;}
+   { (exit 1); exit 1; }; }
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+if test x$ODIRECT != xyes; then
+  { $as_echo "$as_me:$LINENO: WARNING: Warning: O_DIRECT disabled: low-memory pvmove may lock up" >&5
+$as_echo "$as_me: WARNING: Warning: O_DIRECT disabled: low-memory pvmove may lock up" >&2;}
+fi
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..6cb5e8d
--- /dev/null
@@ -0,0 +1,1413 @@
+###############################################################################
+## Copyright (C) 2000-2004 Sistina Software, Inc. All rights reserved.
+## Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+##
+## This copyrighted material is made available to anyone wishing to use,
+## modify, copy, or redistribute it subject to the terms and conditions
+## of the GNU General Public License v.2.
+##
+## You should have received a copy of the GNU 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
+################################################################################
+
+AC_PREREQ(2.61)
+################################################################################
+dnl -- Process this file with autoconf to produce a configure script.
+AC_INIT
+AC_CONFIG_SRCDIR([lib/device/dev-cache.h])
+AC_CONFIG_HEADERS([lib/misc/configure.h])
+
+################################################################################
+dnl -- Setup the directory where autoconf has auxilary files
+AC_CONFIG_AUX_DIR(autoconf)
+
+################################################################################
+dnl -- Get system type
+AC_CANONICAL_TARGET([])
+
+case "$host_os" in
+       linux*)
+               CFLAGS="$CFLAGS"
+               COPTIMISE_FLAG="-O2"
+               CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
+               CLDWHOLEARCHIVE="-Wl,-whole-archive"
+               CLDNOWHOLEARCHIVE="-Wl,-no-whole-archive"
+               LDDEPS="$LDDEPS .export.sym"
+               LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+               LIB_SUFFIX=so
+               DEVMAPPER=yes
+               ODIRECT=yes
+               DM_IOCTLS=yes
+               SELINUX=yes
+               CLUSTER=internal
+               FSADM=yes
+               ;;
+       darwin*)
+               CFLAGS="$CFLAGS -no-cpp-precomp -fno-common"
+               COPTIMISE_FLAG="-O2"
+               CLDFLAGS="$CLDFLAGS"
+               CLDWHOLEARCHIVE="-all_load"
+               CLDNOWHOLEARCHIVE=
+               LIB_SUFFIX=dylib
+               DEVMAPPER=yes
+               ODIRECT=no
+               DM_IOCTLS=no
+               SELINUX=no
+               CLUSTER=none
+               FSADM=no
+               ;;
+esac
+
+################################################################################
+dnl -- Checks for programs.
+AC_PROG_SED
+AC_PROG_AWK
+AC_PROG_CC
+
+dnl probably no longer needed in 2008, but...
+AC_PROG_GCC_TRADITIONAL
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_MKDIR_P
+AC_PROG_RANLIB
+AC_PATH_PROG(CFLOW_CMD, cflow)
+AC_PATH_PROG(CSCOPE_CMD, cscope)
+
+################################################################################
+dnl -- Check for header files.
+AC_HEADER_DIRENT
+AC_HEADER_MAJOR
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_HEADER_TIME
+
+AC_CHECK_HEADERS([locale.h stddef.h syslog.h sys/file.h sys/time.h assert.h \
+  langinfo.h libgen.h signal.h sys/mman.h sys/resource.h sys/utsname.h \
+  sys/wait.h time.h], ,
+  [AC_MSG_ERROR(bailing out)])
+
+case "$host_os" in
+       linux*)
+               AC_CHECK_HEADERS(asm/byteorder.h linux/fs.h malloc.h,,AC_MSG_ERROR(bailing out)) ;;
+       darwin*)
+               AC_CHECK_HEADERS(machine/endian.h sys/disk.h,,AC_MSG_ERROR(bailing out)) ;;
+esac
+
+AC_CHECK_HEADERS([ctype.h dirent.h errno.h fcntl.h getopt.h inttypes.h limits.h \
+  stdarg.h stdio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/stat.h \
+  sys/types.h unistd.h], , [AC_MSG_ERROR(bailing out)])
+AC_CHECK_HEADERS(termios.h sys/statvfs.h)
+
+################################################################################
+dnl -- Check for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_CHECK_MEMBERS([struct stat.st_rdev])
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIGNAL
+AC_TYPE_SIZE_T
+AC_TYPE_MODE_T
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UID_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_CHECK_MEMBERS([struct stat.st_rdev])
+AC_STRUCT_TM
+
+################################################################################
+dnl -- Check for functions
+AC_CHECK_FUNCS([ftruncate gethostname getpagesize \
+  gettimeofday memset mkdir mkfifo rmdir munmap nl_langinfo setenv setlocale \
+  strcasecmp strchr strcspn strspn strdup strncasecmp strerror strrchr \
+  strstr strtol strtoul uname], , [AC_MSG_ERROR(bailing out)])
+AC_CHECK_FUNCS(siginterrupt)
+AC_FUNC_ALLOCA
+AC_FUNC_CLOSEDIR_VOID
+AC_FUNC_CHOWN
+AC_FUNC_FORK
+AC_FUNC_LSTAT
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_MMAP
+AC_FUNC_REALLOC
+AC_FUNC_STAT
+AC_FUNC_STRTOD
+AC_FUNC_VPRINTF
+
+################################################################################
+dnl -- Enables statically-linked tools
+AC_MSG_CHECKING(whether to use static linking)
+AC_ARG_ENABLE(static_link,
+             AC_HELP_STRING([--enable-static_link],
+                            [use this to link the tools to their libraries
+                             statically (default is dynamic linking]),
+             STATIC_LINK=$enableval, STATIC_LINK=no)
+AC_MSG_RESULT($STATIC_LINK)
+
+################################################################################
+dnl -- Prefix is /usr by default, the exec_prefix default is setup later
+AC_PREFIX_DEFAULT(/usr)
+
+################################################################################
+dnl -- Setup the ownership of the files
+AC_MSG_CHECKING(file owner)
+AC_ARG_WITH(user,
+           AC_HELP_STRING([--with-user=USER],
+                          [set the owner of installed files [[USER=]]]),
+           OWNER=$withval)
+AC_MSG_RESULT($OWNER)
+
+if test x$OWNER != x; then
+       INSTALL="$INSTALL -o $OWNER"
+fi
+
+################################################################################
+dnl -- Setup the group ownership of the files
+AC_MSG_CHECKING(group owner)
+AC_ARG_WITH(group,
+           AC_HELP_STRING([--with-group=GROUP],
+                          [set the group owner of installed files [[GROUP=]]]),
+           GROUP=$withval)
+AC_MSG_RESULT($GROUP)
+
+if test x$GROUP != x; then
+       INSTALL="$INSTALL -g $GROUP"
+fi
+
+################################################################################
+dnl -- Setup device node ownership
+AC_MSG_CHECKING(device node uid)
+
+AC_ARG_WITH(device-uid,
+           AC_HELP_STRING([--with-device-uid=UID],
+                          [set the owner used for new device nodes [[UID=0]]]),
+           DM_DEVICE_UID=$withval, DM_DEVICE_UID=0)
+AC_MSG_RESULT($DM_DEVICE_UID)
+
+################################################################################
+dnl -- Setup device group ownership
+AC_MSG_CHECKING(device node gid)
+
+AC_ARG_WITH(device-gid,
+           AC_HELP_STRING([--with-device-gid=GID],
+                          [set the group used for new device nodes [[GID=0]]]),
+           DM_DEVICE_GID=$withval, DM_DEVICE_GID=0)
+AC_MSG_RESULT($DM_DEVICE_GID)
+
+################################################################################
+dnl -- Setup device mode
+AC_MSG_CHECKING(device node mode)
+
+AC_ARG_WITH(device-mode,
+           AC_HELP_STRING([--with-device-mode=MODE],
+                          [set the mode used for new device nodes [[MODE=0600]]]),
+           DM_DEVICE_MODE=$withval, DM_DEVICE_MODE=0600)
+AC_MSG_RESULT($DM_DEVICE_MODE)
+
+################################################################################
+dnl -- LVM1 tool fallback option
+AC_MSG_CHECKING(whether to enable lvm1 fallback)
+AC_ARG_ENABLE(lvm1_fallback,
+             AC_HELP_STRING([--enable-lvm1_fallback],
+                            [use this to fall back and use LVM1 binaries if
+                             device-mapper is missing from the kernel]),
+             LVM1_FALLBACK=$enableval, LVM1_FALLBACK=no)
+AC_MSG_RESULT($LVM1_FALLBACK)
+
+if test x$LVM1_FALLBACK = xyes; then
+       AC_DEFINE([LVM1_FALLBACK], 1, [Define to 1 if 'lvm' should fall back to using LVM1 binaries if device-mapper is missing from the kernel])
+fi
+
+################################################################################
+dnl -- format1 inclusion type
+AC_MSG_CHECKING(whether to include support for lvm1 metadata)
+AC_ARG_WITH(lvm1,
+           AC_HELP_STRING([--with-lvm1=TYPE],
+                          [LVM1 metadata support: internal/shared/none
+                           [[TYPE=internal]]]),
+           LVM1=$withval, LVM1=internal)
+AC_MSG_RESULT($LVM1)
+
+if [[ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ]];
+ then  AC_MSG_ERROR(
+--with-lvm1 parameter invalid
+)
+fi;
+
+if test x$LVM1 = xinternal; then
+       AC_DEFINE([LVM1_INTERNAL], 1, [Define to 1 to include built-in support for LVM1 metadata.])
+fi
+
+################################################################################
+dnl -- format_pool inclusion type
+AC_MSG_CHECKING(whether to include support for GFS pool metadata)
+AC_ARG_WITH(pool,
+           AC_HELP_STRING([--with-pool=TYPE],
+                          [GFS pool read-only support: internal/shared/none
+                           [[TYPE=internal]]]),
+           POOL=$withval, POOL=internal)
+AC_MSG_RESULT($POOL)
+
+if [[ "x$POOL" != xnone -a "x$POOL" != xinternal -a "x$POOL" != xshared ]];
+ then  AC_MSG_ERROR(
+--with-pool parameter invalid
+)
+fi;
+
+if test x$POOL = xinternal; then
+       AC_DEFINE([POOL_INTERNAL], 1, [Define to 1 to include built-in support for GFS pool metadata.])
+fi
+
+################################################################################
+dnl -- cluster_locking inclusion type
+AC_MSG_CHECKING(whether to include support for cluster locking)
+AC_ARG_WITH(cluster,
+           AC_HELP_STRING([--with-cluster=TYPE],
+                          [cluster LVM locking support: internal/shared/none
+                           [[TYPE=internal]]]),
+           CLUSTER=$withval)
+AC_MSG_RESULT($CLUSTER)
+
+if [[ "x$CLUSTER" != xnone -a "x$CLUSTER" != xinternal -a "x$CLUSTER" != xshared ]];
+ then  AC_MSG_ERROR(
+--with-cluster parameter invalid
+)
+fi;
+
+if test x$CLUSTER = xinternal; then
+       AC_DEFINE([CLUSTER_LOCKING_INTERNAL], 1, [Define to 1 to include built-in support for clustered LVM locking.])
+fi
+
+################################################################################
+dnl -- snapshots inclusion type
+AC_MSG_CHECKING(whether to include snapshots)
+AC_ARG_WITH(snapshots,
+           AC_HELP_STRING([--with-snapshots=TYPE],
+                          [snapshot support: internal/shared/none
+                           [[TYPE=internal]]]),
+           SNAPSHOTS=$withval, SNAPSHOTS=internal)
+AC_MSG_RESULT($SNAPSHOTS)
+
+if [[ "x$SNAPSHOTS" != xnone -a "x$SNAPSHOTS" != xinternal -a "x$SNAPSHOTS" != xshared ]];
+ then  AC_MSG_ERROR(
+--with-snapshots parameter invalid
+)
+fi;
+
+if test x$SNAPSHOTS = xinternal; then
+       AC_DEFINE([SNAPSHOT_INTERNAL], 1, [Define to 1 to include built-in support for snapshots.])
+fi
+
+################################################################################
+dnl -- mirrors inclusion type
+AC_MSG_CHECKING(whether to include mirrors)
+AC_ARG_WITH(mirrors,
+           AC_HELP_STRING([--with-mirrors=TYPE],
+                          [mirror support: internal/shared/none
+                           [[TYPE=internal]]]),
+           MIRRORS=$withval, MIRRORS=internal)
+AC_MSG_RESULT($MIRRORS)
+
+if [[ "x$MIRRORS" != xnone -a "x$MIRRORS" != xinternal -a "x$MIRRORS" != xshared ]];
+ then  AC_MSG_ERROR(
+--with-mirrors parameter invalid
+)
+fi;
+
+if test x$MIRRORS = xinternal; then
+       AC_DEFINE([MIRRORED_INTERNAL], 1, [Define to 1 to include built-in support for mirrors.])
+fi
+
+################################################################################
+dnl -- asynchronous volume replicator inclusion type
+AC_MSG_CHECKING(whether to include replicators)
+AC_ARG_WITH(replicators,
+           AC_HELP_STRING([--with-replicators=TYPE],
+                          [replicator support: internal/shared/none
+                           [[TYPE=none]]]),
+           REPLICATORS=$withval, REPLICATORS=none)
+AC_MSG_RESULT($REPLICATORS)
+
+case "$REPLICATORS" in
+  none|shared) ;;
+  internal) AC_DEFINE([REPLICATOR_INTERNAL], 1,
+               [Define to 1 to include built-in support for replicators.]) ;;
+  *) AC_MSG_ERROR([--with-replicators parameter invalid ($REPLICATORS)]) ;;
+esac
+
+################################################################################
+dnl -- Disable readline
+AC_MSG_CHECKING(whether to enable readline)
+AC_ARG_ENABLE([readline],
+             AC_HELP_STRING([--disable-readline], [disable readline support]),
+             READLINE=$enableval, READLINE=maybe)
+AC_MSG_RESULT($READLINE)
+
+################################################################################
+dnl -- Disable realtime clock support
+AC_MSG_CHECKING(whether to enable realtime support)
+AC_ARG_ENABLE(realtime,
+             AC_HELP_STRING([--enable-realtime], [enable realtime clock support]),
+             REALTIME=$enableval)
+AC_MSG_RESULT($REALTIME)
+
+################################################################################
+dnl -- disable OCF resource agents
+AC_MSG_CHECKING(whether to enable OCF resource agents)
+AC_ARG_ENABLE(ocf,
+             AC_HELP_STRING([--enable-ocf],
+                            [enable Open Cluster Framework (OCF) compliant resource agents]),
+             OCF=$enableval, OCF=no)
+AC_MSG_RESULT($OCF)
+
+################################################################################
+dnl -- Init pkg-config with dummy invokation:
+dnl -- this is required because PKG_CHECK_MODULES macro is expanded
+dnl -- to initialize the pkg-config environment only at the first invokation,
+dnl -- that would be conditional in this configure.in.
+pkg_config_init() {
+       PKG_CHECK_MODULES(PKGCONFIGINIT, pkgconfiginit, [],
+               [AC_MSG_RESULT([pkg-config initialized])])
+       PKGCONFIG_INIT=1
+}
+
+################################################################################
+dnl -- Build cluster LVM daemon
+AC_MSG_CHECKING(whether to build cluster LVM daemon)
+AC_ARG_WITH(clvmd,
+  [  --with-clvmd=TYPE       build cluster LVM Daemon
+                          The following cluster manager combinations are valid:
+                           * cman,gulm             (RHEL4 or equivalent)
+                           * cman                  (RHEL5 or equivalent)
+                           * cman,corosync,openais (or selection of them)
+                           * singlenode            (localhost only)
+                           * all                   (autodetect)
+                           * none                  (disable build)
+                          [[TYPE=none]]],
+           CLVMD=$withval, CLVMD=none)
+if test x$CLVMD = xyes; then
+       CLVMD=all
+fi
+AC_MSG_RESULT($CLVMD)
+
+dnl -- If clvmd enabled without cluster locking, automagically include it
+if  test x$CLVMD != xnone && test x$CLUSTER = xnone; then
+       CLUSTER=internal
+fi
+
+dnl -- init pkgconfig if required
+if test x$CLVMD != xnone && test x$PKGCONFIG_INIT != x1; then
+       pkg_config_init
+fi
+
+dnl -- Express clvmd init script Required-Start / Required-Stop
+CLVMD_CMANAGERS=""
+dnl -- On RHEL4/RHEL5, qdiskd is started from a separate init script.
+dnl -- Enable if we are build for either cman or gulm.
+CLVMD_NEEDS_QDISKD=no
+
+dnl -- define build types
+if [[ `expr x"$CLVMD" : '.*gulm.*'` != 0 ]]; then
+       BUILDGULM=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+       CLVMD_NEEDS_QDISKD=yes
+fi
+if [[ `expr x"$CLVMD" : '.*cman.*'` != 0 ]]; then
+       BUILDCMAN=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+       CLVMD_NEEDS_QDISKD=yes
+fi
+if [[ `expr x"$CLVMD" : '.*corosync.*'` != 0 ]]; then
+       BUILDCOROSYNC=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+fi
+if [[ `expr x"$CLVMD" : '.*openais.*'` != 0 ]]; then
+       BUILDOPENAIS=yes
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+fi
+if test x$CLVMD_NEEDS_QDISKD != xno; then
+       CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+fi
+
+dnl -- sanity check around user selection
+if test x$BUILDGULM = xyes; then
+       if test x$BUILDCOROSYNC = xyes || \
+          test x$BUILDOPENAIS = xyes; then
+               AC_MSG_ERROR([requested clvmd configuration is not valid])
+       fi
+fi
+
+dnl -- define a soft bailout if we are autodetecting
+soft_bailout() {
+       NOTFOUND=1
+}
+
+hard_bailout() {
+       AC_MSG_ERROR([bailing out])
+}
+
+dnl -- if clvmd=all then set soft_bailout (we don't want to error)
+dnl -- and set all builds to yes. We need to do this here
+dnl -- to skip the gulm + openais|corosync sanity check above.
+if test x$CLVMD = xall; then
+       bailout=soft_bailout
+       BUILDGULM=yes
+       BUILDCMAN=yes
+       BUILDCOROSYNC=yes
+       BUILDOPENAIS=yes
+else
+       bailout=hard_bailout
+fi
+
+dnl -- helper macro to check libs without adding them to LIBS
+check_lib_no_libs() {
+       lib_no_libs_arg1=$1
+       shift
+       lib_no_libs_arg2=$1
+       shift
+       lib_no_libs_args=$@
+       AC_CHECK_LIB([$lib_no_libs_arg1],
+                    [$lib_no_libs_arg2],,
+                    [$bailout],
+                    [$lib_no_libs_args])
+       LIBS=$ac_check_lib_save_LIBS
+}
+
+dnl -- Look for gulm libraries if required.
+if test x$BUILDGULM = xyes; then
+       PKG_CHECK_MODULES(CCS, libccs, [HAVE_CCS=yes],
+               [NOTFOUND=0
+               AC_CHECK_HEADERS(ccs.h,,$bailout)
+               check_lib_no_libs ccs ccs_connect
+               if test $NOTFOUND = 0; then
+                       AC_MSG_RESULT([no pkg for libccs, using -lccs])
+                       CCS_LIBS="-lccs"
+                       HAVE_CCS=yes
+               fi])
+       PKG_CHECK_MODULES(GULM, libgulm, [HAVE_GULM=yes],
+               [NOTFOUND=0
+               AC_CHECK_HEADERS(libgulm.h,,$bailout)
+               check_lib_no_libs gulm lg_core_login
+               if test $NOTFOUND = 0; then
+                       AC_MSG_RESULT([no pkg for libgulm, using -lgulm])
+                       GULM_LIBS="-lgulm"
+                       HAVE_GULM=yes
+               fi])
+fi
+
+dnl -- Look for cman libraries if required.
+if test x$BUILDCMAN = xyes; then
+       PKG_CHECK_MODULES(CMAN, libcman, [HAVE_CMAN=yes],
+               [NOTFOUND=0
+               AC_CHECK_HEADERS(libcman.h,,$bailout)
+               check_lib_no_libs cman cman_init
+               if test $NOTFOUND = 0; then
+                       AC_MSG_RESULT([no pkg for libcman, using -lcman])
+                       CMAN_LIBS="-lcman"
+                       HAVE_CMAN=yes
+               fi])
+       CHECKCONFDB=yes
+       CHECKDLM=yes
+fi
+
+dnl -- Look for corosync that's required also for openais build
+dnl -- only enough recent version of corosync ship pkg-config files.
+dnl -- We can safely rely on that to detect the correct bits.
+if test x$BUILDCOROSYNC = xyes || \
+   test x$BUILDOPENAIS = xyes; then
+       PKG_CHECK_MODULES(COROSYNC, corosync, [HAVE_COROSYNC=yes], $bailout)
+       CHECKCONFDB=yes
+fi
+
+dnl -- Look for corosync libraries if required.
+if test x$BUILDCOROSYNC = xyes; then
+       PKG_CHECK_MODULES(QUORUM, libquorum, [HAVE_QUORUM=yes], $bailout)
+       CHECKCPG=yes
+       CHECKDLM=yes
+fi
+
+dnl -- Look for openais libraries if required.
+if test x$BUILDOPENAIS = xyes; then
+       PKG_CHECK_MODULES(SALCK, libSaLck, [HAVE_SALCK=yes], $bailout)
+       CHECKCPG=yes
+fi
+
+dnl -- Below are checks for libraries common to more than one build.
+
+dnl -- Check confdb library.
+dnl -- mandatory for corosync build.
+dnl -- optional for openais/cman build.
+
+if test x$CHECKCONFDB = xyes; then
+       PKG_CHECK_MODULES(CONFDB, libconfdb,
+                         [HAVE_CONFDB=yes],
+                         [HAVE_CONFDB=no])
+
+       AC_CHECK_HEADERS(corosync/confdb.h,
+               [HAVE_CONFDB_H=yes],
+               [HAVE_CONFDB_H=no])
+
+       if test x$HAVE_CONFDB != xyes && \
+          test x$HAVE_CONFDB_H = xyes; then
+               check_lib_no_libs confdb confdb_initialize
+               AC_MSG_RESULT([no pkg for confdb, using -lconfdb])
+               CONFDB_LIBS="-lconfdb"
+               HAVE_CONFDB=yes
+       fi
+
+       if test x$BUILDCOROSYNC = xyes && \
+          test x$HAVE_CONFDB != xyes &&
+          test x$CLVMD != xall; then
+               AC_MSG_ERROR([bailing out... confdb library is required])
+       fi
+fi
+
+dnl -- Check cpg library.
+if test x$CHECKCPG = xyes; then
+       PKG_CHECK_MODULES(CPG, libcpg, [HAVE_CPG=yes], $bailout)
+fi
+
+dnl -- Check dlm library.
+if test x$CHECKDLM = xyes; then
+       PKG_CHECK_MODULES(DLM, libdlm, [HAVE_DLM=yes],
+               [NOTFOUND=0
+               AC_CHECK_HEADERS(libdlm.h,,$bailout)
+               check_lib_no_libs dlm dlm_lock -lpthread
+               if test $NOTFOUND = 0; then
+                       AC_MSG_RESULT([no pkg for libdlm, using -ldlm])
+                       DLM_LIBS="-ldlm -lpthread"
+                       HAVE_DLM=yes
+               fi])
+fi
+
+dnl -- If we are autodetecting, we need to re-create
+dnl -- the depedencies checks and set a proper CLVMD,
+dnl -- together with init script Required-Start/Stop entries.
+if test x$CLVMD = xall; then
+       CLVMD=none
+       CLVMD_CMANAGERS=""
+       CLVMD_NEEDS_QDISKD=no
+       if test x$HAVE_CCS = xyes && \
+          test x$HAVE_GULM = xyes; then
+               AC_MSG_RESULT([Enabling clvmd gulm cluster manager])
+               CLVMD="$CLVMD,gulm"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+               CLVMD_NEEDS_QDISKD=yes
+       fi
+       if test x$HAVE_CMAN = xyes && \
+          test x$HAVE_DLM = xyes; then
+               AC_MSG_RESULT([Enabling clvmd cman cluster manager])
+               CLVMD="$CLVMD,cman"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+               CLVMD_NEEDS_QDISKD=yes
+       fi
+       if test x$HAVE_COROSYNC = xyes && \
+          test x$HAVE_QUORUM = xyes && \
+          test x$HAVE_CPG = xyes && \
+          test x$HAVE_DLM = xyes && \
+          test x$HAVE_CONFDB = xyes; then
+               AC_MSG_RESULT([Enabling clvmd corosync cluster manager])
+               CLVMD="$CLVMD,corosync"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+       fi
+       if test x$HAVE_COROSYNC = xyes && \
+          test x$HAVE_CPG = xyes && \
+          test x$HAVE_SALCK = xyes; then
+               AC_MSG_RESULT([Enabling clvmd openais cluster manager])
+               CLVMD="$CLVMD,openais"
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+       fi
+       if test x$CLVMD_NEEDS_QDISKD != xno; then
+               CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+       fi
+       if test x$CLVMD = xnone; then
+               AC_MSG_RESULT([Disabling clvmd build. No cluster manager detected.])
+       fi
+fi
+
+################################################################################
+dnl -- clvmd pidfile
+if test "x$CLVMD" != xnone; then
+       AC_ARG_WITH(clvmd-pidfile,
+                   AC_HELP_STRING([--with-clvmd-pidfile=PATH],
+                                  [clvmd pidfile [[/var/run/clvmd.pid]]]),
+                   CLVMD_PIDFILE=$withval,
+                   CLVMD_PIDFILE="/var/run/clvmd.pid")
+       AC_DEFINE_UNQUOTED(CLVMD_PIDFILE, ["$CLVMD_PIDFILE"],
+                          [Path to clvmd pidfile.])
+fi
+
+################################################################################
+dnl -- Build cluster mirror log daemon
+AC_MSG_CHECKING(whether to build cluster mirror log daemon)
+AC_ARG_ENABLE(cmirrord,
+             AC_HELP_STRING([--enable-cmirrord],
+                            [enable the cluster mirror log daemon]),
+             CMIRRORD=$enableval, CMIRRORD=no)
+AC_MSG_RESULT($CMIRRORD)
+
+BUILD_CMIRRORD=$CMIRRORD
+
+################################################################################
+dnl -- cmirrord pidfile
+if test "x$BUILD_CMIRRORD" = xyes; then
+       AC_ARG_WITH(cmirrord-pidfile,
+                   AC_HELP_STRING([--with-cmirrord-pidfile=PATH],
+                                  [cmirrord pidfile [[/var/run/cmirrord.pid]]]),
+                   CMIRRORD_PIDFILE=$withval,
+                   CMIRRORD_PIDFILE="/var/run/cmirrord.pid")
+       AC_DEFINE_UNQUOTED(CMIRRORD_PIDFILE, ["$CMIRRORD_PIDFILE"],
+                          [Path to cmirrord pidfile.])
+fi
+
+################################################################################
+dnl -- Look for corosync libraries if required.
+if [[ "x$BUILD_CMIRRORD" = xyes ]]; then
+       dnl -- init pkgconfig if required
+       if  test x$PKGCONFIG_INIT != x1; then
+               pkg_config_init
+       fi
+       PKG_CHECK_MODULES(SACKPT, libSaCkpt)
+       if test x$HAVE_CPG != xyes; then
+               PKG_CHECK_MODULES(CPG, libcpg)
+       fi
+fi
+
+################################################################################
+dnl -- Enable debugging
+AC_MSG_CHECKING(whether to enable debugging)
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable debugging]),
+             DEBUG=$enableval, DEBUG=no)
+AC_MSG_RESULT($DEBUG)
+
+dnl -- Normally turn off optimisation for debug builds
+if test x$DEBUG = xyes; then
+       COPTIMISE_FLAG=
+else
+       CSCOPE_CMD=
+fi
+
+################################################################################
+dnl -- Override optimisation
+AC_MSG_CHECKING(for C optimisation flag)
+AC_ARG_WITH(optimisation,
+           AC_HELP_STRING([--with-optimisation=OPT],
+                          [C optimisation flag [[OPT=-O2]]]),
+           COPTIMISE_FLAG=$withval)
+AC_MSG_RESULT($COPTIMISE_FLAG)
+
+################################################################################
+dnl -- Enable profiling
+AC_MSG_CHECKING(whether to gather gcov profiling data)
+AC_ARG_ENABLE(profiling,
+             AC_HELP_STRING(--enable-profiling, [gather gcov profiling data]),
+             PROFILING=$enableval, PROFILING=no)
+AC_MSG_RESULT($PROFILING)
+
+if test "x$PROFILING" = xyes; then
+  COPTIMISE_FLAG="$COPTIMISE_FLAG -fprofile-arcs -ftest-coverage"
+  AC_PATH_PROG(LCOV, lcov)
+  AC_PATH_PROG(GENHTML, genhtml)
+  if test -z "$LCOV" -o -z "$GENHTML"; then
+    AC_MSG_ERROR([lcov and genhtml are required for profiling])
+  fi
+  AC_PATH_PROG(GENPNG, genpng)
+  if test -n "$GENPNG"; then
+    AC_MSG_CHECKING([whether $GENPNG has all required modules])
+    if $GENPNG --help > /dev/null 2>&1 ; then
+      AC_MSG_RESULT(ok)
+      GENHTML="$GENHTML --frames"
+    else
+      AC_MSG_RESULT(not supported)
+      AC_MSG_WARN([GD.pm perl module is not installed])
+      GENPNG=
+    fi
+  fi
+fi
+
+################################################################################
+dnl -- Enable testing
+AC_MSG_CHECKING(whether to enable unit testing)
+AC_ARG_ENABLE(testing,
+              AC_HELP_STRING(--enable-testing, [enable testing targets in the makefile]),
+              TESTING=$enableval, TESTING=no)
+AC_MSG_RESULT($TESTING)
+
+if test "$TESTING" = yes; then
+   AC_PATH_PROG(RUBY19, ruby1.9)
+   AC_PATH_PROG(VALGRIND, valgrind)
+   if test -z "$RUBY19" -o -z "$VALGRIND"; then
+       AC_MSG_ERROR([ruby1.9 and valgrind are required for testing])
+   fi
+fi
+
+################################################################################
+dnl -- Enable valgrind awareness of memory pools
+AC_MSG_CHECKING(whether to enable valgrind awareness of pools)
+AC_ARG_ENABLE(valgrind_pool,
+              AC_HELP_STRING(--enable-valgrind-pool, [enable valgrind awareness of pools]),
+             VALGRIND_POOL=$enableval, VALGRIND_POOL=no)
+AC_MSG_RESULT($VALGRIND_POOL)
+
+if test "$VALGRIND_POOL" = yes; then
+    AC_CHECK_HEADERS([valgrind/memcheck.h], , [AC_MSG_ERROR(bailing out)])
+    AC_DEFINE([VALGRIND_POOL], 1, [Enable a valgrind aware build of pool])
+fi
+
+################################################################################
+dnl -- Disable devmapper
+AC_MSG_CHECKING(whether to use device-mapper)
+AC_ARG_ENABLE(devmapper,
+             AC_HELP_STRING([--disable-devmapper],
+                            [disable LVM2 device-mapper interaction]),
+             DEVMAPPER=$enableval)
+AC_MSG_RESULT($DEVMAPPER)
+
+if test x$DEVMAPPER = xyes; then
+       AC_DEFINE([DEVMAPPER_SUPPORT], 1, [Define to 1 to enable LVM2 device-mapper interaction.])
+fi
+
+################################################################################
+dnl -- Enable udev synchronisation
+AC_MSG_CHECKING(whether to enable synchronisation with udev processing)
+AC_ARG_ENABLE(udev_sync,
+             AC_HELP_STRING([--enable-udev_sync], 
+                            [enable synchronisation with udev processing]),
+             UDEV_SYNC=$enableval, UDEV_SYNC=no)
+AC_MSG_RESULT($UDEV_SYNC)
+
+if test x$UDEV_SYNC = xyes; then
+       AC_CHECK_LIB(udev, udev_queue_get_udev_is_active,
+                    [UDEV_PC="libudev"; UDEV_LIBS="-ludev"],
+                    [AC_MSG_ERROR([bailing out... libudev library is required])])
+       AC_DEFINE([UDEV_SYNC_SUPPORT], 1, [Define to 1 to enable synchronisation with udev processing.])
+fi
+
+dnl -- Enable udev rules
+AC_MSG_CHECKING(whether to enable installation of udev rules required for synchronisation)
+AC_ARG_ENABLE(udev_rules,
+             AC_HELP_STRING([--enable-udev_rules],
+                            [install rule files needed for udev synchronisation]),
+             UDEV_RULES=$enableval, UDEV_RULES=$UDEV_SYNC)
+AC_MSG_RESULT($UDEV_RULES)
+
+################################################################################
+dnl -- Compatibility mode
+AC_ARG_ENABLE(compat,
+             AC_HELP_STRING([--enable-compat],
+                            [enable support for old device-mapper versions]),
+             DM_COMPAT=$enableval, DM_COMPAT=no)
+
+################################################################################
+dnl -- Compatible units suffix mode
+AC_ARG_ENABLE(units-compat,
+             AC_HELP_STRING([--enable-units-compat],
+                            [enable output compatibility with old versions that
+                             that do not use KiB-style unit suffixes]),
+             UNITS_COMPAT=$enableval, UNITS_COMPAT=no)
+
+if test x$UNITS_COMPAT = xyes; then
+       AC_DEFINE([DEFAULT_SI_UNIT_CONSISTENCY], 0, [Define to 0 to reinstate the pre-2.02.54 handling of unit suffixes.])
+fi
+
+################################################################################
+dnl -- Disable ioctl
+AC_ARG_ENABLE(ioctl,
+             AC_HELP_STRING([--disable-driver],
+                            [disable calls to device-mapper in the kernel]),
+             DM_IOCTLS=$enableval)
+
+################################################################################
+dnl -- Disable O_DIRECT
+AC_MSG_CHECKING(whether to enable O_DIRECT)
+AC_ARG_ENABLE(o_direct,
+             AC_HELP_STRING([--disable-o_direct], [disable O_DIRECT]),
+             ODIRECT=$enableval)
+AC_MSG_RESULT($ODIRECT)
+
+if test x$ODIRECT = xyes; then
+       AC_DEFINE([O_DIRECT_SUPPORT], 1, [Define to 1 to enable O_DIRECT support.])
+fi
+
+################################################################################
+dnl -- Enable liblvm2app.so
+AC_MSG_CHECKING(whether to build liblvm2app.so application library)
+AC_ARG_ENABLE(applib,
+             AC_HELP_STRING([--enable-applib], [build application library]),
+             APPLIB=$enableval, APPLIB=no)
+AC_MSG_RESULT($APPLIB)
+AC_SUBST([LVM2APP_LIB])
+test x$APPLIB = xyes \
+  && LVM2APP_LIB=-llvm2app \
+  || LVM2APP_LIB=
+
+################################################################################
+dnl -- Enable cmdlib
+AC_MSG_CHECKING(whether to compile liblvm2cmd.so)
+AC_ARG_ENABLE(cmdlib,
+             AC_HELP_STRING([--enable-cmdlib], [build shared command library]),
+             CMDLIB=$enableval, CMDLIB=no)
+AC_MSG_RESULT($CMDLIB)
+AC_SUBST([LVM2CMD_LIB])
+test x$CMDLIB = xyes \
+  && LVM2CMD_LIB=-llvm2cmd \
+  || LVM2CMD_LIB=
+
+################################################################################
+dnl -- Enable pkg-config
+AC_ARG_ENABLE(pkgconfig,
+             AC_HELP_STRING([--enable-pkgconfig], [install pkgconfig support]),
+             PKGCONFIG=$enableval, PKGCONFIG=no)
+
+################################################################################
+dnl -- Enable installation of writable files by user
+AC_ARG_ENABLE(write_install,
+             AC_HELP_STRING([--enable-write_install],
+                            [install user writable files]),
+             WRITE_INSTALL=$enableval, WRITE_INSTALL=no)
+
+################################################################################
+dnl -- Enable fsadm
+AC_MSG_CHECKING(whether to install fsadm)
+AC_ARG_ENABLE(fsadm, AC_HELP_STRING([--disable-fsadm], [disable fsadm]),
+             FSADM=$enableval)
+AC_MSG_RESULT($FSADM)
+
+################################################################################
+dnl -- enable dmeventd handling
+AC_MSG_CHECKING(whether to use dmeventd)
+AC_ARG_ENABLE(dmeventd, AC_HELP_STRING([--enable-dmeventd],
+                                      [enable the device-mapper event daemon]),
+             DMEVENTD=$enableval)
+AC_MSG_RESULT($DMEVENTD)
+
+BUILD_DMEVENTD=$DMEVENTD
+
+dnl -- dmeventd currently requires internal mirror support
+if test x$DMEVENTD = xyes; then
+   if test x$MIRRORS != xinternal; then
+      AC_MSG_ERROR(
+       --enable-dmeventd currently requires --with-mirrors=internal
+      )
+   fi
+   if test x$CMDLIB = xno; then
+      AC_MSG_ERROR(
+        --enable-dmeventd requires --enable-cmdlib to be used as well
+      )
+   fi
+fi
+
+if test x$DMEVENTD = xyes; then
+       AC_DEFINE([DMEVENTD], 1, [Define to 1 to enable the device-mapper event daemon.])
+fi
+
+################################################################################
+dnl -- getline included in recent libc
+
+AC_CHECK_LIB(c, getline, AC_DEFINE([HAVE_GETLINE], 1,
+  [Define to 1 if getline is available.]))
+
+################################################################################
+dnl -- canonicalize_file_name included in recent libc
+
+AC_CHECK_LIB(c, canonicalize_file_name,
+  AC_DEFINE([HAVE_CANONICALIZE_FILE_NAME], 1,
+    [Define to 1 if canonicalize_file_name is available.]))
+
+################################################################################
+dnl -- Clear default exec_prefix - install into /sbin rather than /usr/sbin
+if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]];
+ then  exec_prefix="";
+fi;
+
+################################################################################
+dnl -- Check for dlopen
+AC_CHECK_LIB(dl, dlopen, [
+       AC_DEFINE([HAVE_LIBDL], 1, [Define to 1 if dynamic libraries are available.])
+       DL_LIBS="-ldl"
+       HAVE_LIBDL=yes ], [
+       DL_LIBS=
+       HAVE_LIBDL=no ])
+
+################################################################################
+dnl -- Check for shared/static conflicts
+if [[ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \
+      -o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \
+      \) -a "x$STATIC_LINK" = xyes ]];
+ then  AC_MSG_ERROR(
+Features cannot be 'shared' when building statically
+)
+fi
+
+################################################################################
+if [[ "$DMEVENTD" = yes -o "$CLVMD" != none ]] ; then
+       AC_CHECK_LIB([pthread], [pthread_mutex_lock],
+               [PTHREAD_LIBS="-lpthread"], hard_bailout)
+fi
+
+################################################################################
+dnl -- Disable selinux
+AC_MSG_CHECKING(whether to enable selinux support)
+AC_ARG_ENABLE(selinux,
+             AC_HELP_STRING([--disable-selinux], [disable selinux support]),
+             SELINUX=$enableval)
+AC_MSG_RESULT($SELINUX)
+
+################################################################################
+dnl -- Check for selinux
+if test x$SELINUX = xyes; then
+       AC_CHECK_LIB([sepol], [sepol_check_context], [
+               AC_DEFINE([HAVE_SEPOL], 1, [Define to 1 if sepol_check_context is available.])
+               SELINUX_LIBS="-lsepol"])
+
+       AC_CHECK_LIB([selinux], [is_selinux_enabled], [
+               AC_CHECK_HEADERS([selinux/selinux.h],, hard_bailout)
+               AC_CHECK_HEADERS([selinux/label.h])
+               AC_DEFINE([HAVE_SELINUX], 1, [Define to 1 to include support for selinux.])
+               SELINUX_LIBS="-lselinux $SELINUX_LIBS"
+               SELINUX_PC="libselinux"
+               HAVE_SELINUX=yes ], [
+               AC_MSG_WARN(Disabling selinux)
+               SELINUX_LIBS=
+               SELINUX_PC=
+               HAVE_SELINUX=no ])
+fi
+
+################################################################################
+dnl -- Check for realtime clock support
+if test x$REALTIME = xyes; then
+       AC_CHECK_LIB(rt, clock_gettime, HAVE_REALTIME=yes, HAVE_REALTIME=no)
+
+       if test x$HAVE_REALTIME = xyes; then
+               AC_DEFINE([HAVE_REALTIME], 1, [Define to 1 to include support for realtime clock.])
+               LIBS="-lrt $LIBS"
+       else
+               AC_MSG_WARN(Disabling realtime clock)
+       fi
+fi
+
+################################################################################
+dnl -- Check for getopt
+AC_CHECK_HEADERS(getopt.h, AC_DEFINE([HAVE_GETOPTLONG], 1, [Define to 1 if getopt_long is available.]))
+
+################################################################################
+dnl -- Check for readline (Shamelessly copied from parted 1.4.17)
+if test x$READLINE != xno; then
+       lvm_saved_libs=$LIBS
+       AC_SEARCH_LIBS([tgetent], [tinfo ncurses curses termcap termlib],
+               READLINE_LIBS=$ac_cv_search_tgetent, [
+               if test "$READLINE" = yes; then
+                       AC_MSG_ERROR(
+[termcap could not be found which is required for the
+--enable-readline option (which is enabled by default).  Either disable readline
+support with --disable-readline or download and install termcap from:
+       ftp.gnu.org/gnu/termcap
+Note: if you are using precompiled packages you will also need the development
+  package as well (which may be called termcap-devel or something similar).
+Note: (n)curses also seems to work as a substitute for termcap.  This was
+  not found either - but you could try installing that as well.])
+               fi])
+       dnl -- Old systems may need extra termcap dependency explicitly in LIBS
+       AC_CHECK_LIB([readline], [readline], [
+               AC_DEFINE([READLINE_SUPPORT], 1,
+                       [Define to 1 to include the LVM readline shell.])
+               dnl -- Try only with -lreadline and check for different symbol
+               LIBS=$lvm_saved_libs
+               AC_CHECK_LIB([readline], [rl_line_buffer],
+                       [ READLINE_LIBS="-lreadline" ], [
+                         AC_MSG_RESULT([linking -lreadline with $READLINE_LIBS needed])
+                         READLINE_LIBS="-lreadline $READLINE_LIBS"
+                       ]) ], [
+               READLINE_LIBS=
+               if test "$READLINE" = yes; then
+                       AC_MSG_ERROR(
+[GNU Readline could not be found which is required for the
+--enable-readline option (which is enabled by default).  Either disable readline
+support with --disable-readline or download and install readline from:
+       ftp.gnu.org/gnu/readline
+Note: if you are using precompiled packages you will also need the development
+package as well (which may be called readline-devel or something similar).])
+               fi ])
+       LIBS="$READLINE_LIBS $lvm_saved_libs"
+       AC_CHECK_FUNCS([rl_completion_matches])
+       LIBS=$lvm_saved_libs
+fi
+
+################################################################################
+dnl -- Internationalisation stuff
+AC_MSG_CHECKING(whether to enable internationalisation)
+AC_ARG_ENABLE(nls,
+             AC_HELP_STRING([--enable-nls], [enable Native Language Support]),
+             INTL=$enableval, INTL=no)
+AC_MSG_RESULT($INTL)
+
+if test x$INTL = xyes; then
+# FIXME - Move this - can be device-mapper too
+       INTL_PACKAGE="lvm2"
+       AC_PATH_PROG(MSGFMT, msgfmt)
+       if [[ "x$MSGFMT" == x ]];
+               then  AC_MSG_ERROR(
+               msgfmt not found in path $PATH
+               )
+       fi;
+
+       AC_ARG_WITH(localedir,
+                   AC_HELP_STRING([--with-localedir=DIR],
+                                  [translation files in DIR
+                                   [[PREFIX/share/locale]]]),
+                   LOCALEDIR=$withval, LOCALEDIR='${prefix}/share/locale')
+fi
+
+################################################################################
+AC_ARG_WITH(confdir,
+           AC_HELP_STRING([--with-confdir=DIR],
+                          [configuration files in DIR [[/etc]]]),
+           CONFDIR=$withval, CONFDIR="/etc")
+
+AC_ARG_WITH(staticdir,
+           AC_HELP_STRING([--with-staticdir=DIR],
+                          [static binaries in DIR [[EPREFIX/sbin]]]),
+           STATICDIR=$withval, STATICDIR='${exec_prefix}/sbin')
+
+AC_ARG_WITH(usrlibdir,
+           AC_HELP_STRING([--with-usrlibdir=DIR],
+                          [usrlib in DIR [[PREFIX/lib]]]),
+           usrlibdir=$withval, usrlibdir='${prefix}/lib')
+
+AC_ARG_WITH(usrsbindir,
+           AC_HELP_STRING([--with-usrsbindir=DIR],
+                          [usrsbin executables in DIR [[PREFIX/sbin]]]),
+           usrsbindir=$withval, usrsbindir='${prefix}/sbin')
+
+################################################################################
+AC_ARG_WITH(udev_prefix,
+           AC_HELP_STRING([--with-udev-prefix=UPREFIX],
+                          [install udev rule files in UPREFIX [[EPREFIX]]]),
+           udev_prefix=$withval, udev_prefix='${exec_prefix}')
+
+AC_ARG_WITH(udevdir,
+           AC_HELP_STRING([--with-udevdir=DIR],
+                          [udev rules in DIR [[UPREFIX/lib/udev/rules.d]]]),
+           udevdir=$withval, udevdir='${udev_prefix}/lib/udev/rules.d')
+
+################################################################################
+dnl -- Ensure additional headers required
+if test x$READLINE = xyes; then
+       AC_CHECK_HEADERS(readline/readline.h readline/history.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$CLVMD != xnone; then
+       AC_CHECK_HEADERS(mntent.h netdb.h netinet/in.h pthread.h search.h sys/mount.h sys/socket.h sys/uio.h sys/un.h utmpx.h,,AC_MSG_ERROR(bailing out))
+       AC_CHECK_FUNCS(dup2 getmntent memmove select socket,,AC_MSG_ERROR(bailing out))
+       AC_FUNC_GETMNTENT
+       AC_FUNC_SELECT_ARGTYPES
+fi
+
+if test x$CLUSTER != xnone; then
+       AC_CHECK_HEADERS(sys/socket.h sys/un.h,,AC_MSG_ERROR(bailing out))
+       AC_CHECK_FUNCS(socket,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$DMEVENTD = xyes; then
+       AC_CHECK_HEADERS(arpa/inet.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$HAVE_LIBDL = xyes; then
+       AC_CHECK_HEADERS(dlfcn.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$INTL = xyes; then
+       AC_CHECK_HEADERS(libintl.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$UDEV_SYNC = xyes; then
+       AC_CHECK_HEADERS(sys/ipc.h sys/sem.h,,AC_MSG_ERROR(bailing out))
+fi
+
+################################################################################
+AC_PATH_PROG(MODPROBE_CMD, modprobe)
+
+if test x$MODPROBE_CMD != x; then
+       AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.])
+fi
+
+
+lvm_exec_prefix=$exec_prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$ac_default_prefix
+AC_DEFINE_UNQUOTED(LVM_PATH, ["$lvm_exec_prefix/sbin/lvm"], [Path to lvm binary.])
+
+if  test "$CLVMD" != none; then
+        clvmd_prefix=$ac_default_prefix
+        test "$prefix" != NONE && clvmd_prefix=$prefix
+       AC_DEFINE_UNQUOTED(CLVMD_PATH, ["$clvmd_prefix/sbin/clvmd"],
+                          [Path to clvmd binary.])
+fi
+
+################################################################################
+dnl -- dmeventd pidfile and executable path
+if test "$BUILD_DMEVENTD" = yes; then
+       AC_ARG_WITH(dmeventd-pidfile,
+                   AC_HELP_STRING([--with-dmeventd-pidfile=PATH],
+                                  [dmeventd pidfile [[/var/run/dmeventd.pid]]]),
+                   DMEVENTD_PIDFILE=$withval,
+                   DMEVENTD_PIDFILE="/var/run/dmeventd.pid")
+       AC_DEFINE_UNQUOTED(DMEVENTD_PIDFILE, ["$DMEVENTD_PIDFILE"],
+                          [Path to dmeventd pidfile.])
+fi
+
+if test "$BUILD_DMEVENTD" = yes; then
+       AC_ARG_WITH(dmeventd-path,
+                   AC_HELP_STRING([--with-dmeventd-path=PATH],
+                                  [dmeventd path [[EPREFIX/sbin/dmeventd]]]),
+                   DMEVENTD_PATH=$withval,
+                   DMEVENTD_PATH="$lvm_exec_prefix/sbin/dmeventd")
+       AC_DEFINE_UNQUOTED(DMEVENTD_PATH, ["$DMEVENTD_PATH"],
+                          [Path to dmeventd binary.])
+fi
+
+AH_TEMPLATE(DEFAULT_RUN_DIR, [Name of default run directory.])
+AC_ARG_WITH(default-run-dir,
+           [  --with-default-run-dir=DIR       Default run directory [[/var/run/lvm]] ],
+           [ DEFAULT_RUN_DIR="$withval" ],
+           [ DEFAULT_RUN_DIR="/var/run/lvm" ])
+AC_DEFINE_UNQUOTED(DEFAULT_RUN_DIR,["$DEFAULT_RUN_DIR"] )
+
+################################################################################
+dnl -- various defaults
+AC_ARG_WITH(default-system-dir,
+           AC_HELP_STRING([--with-default-system-dir=DIR],
+                          [default LVM system directory [[/etc/lvm]]]),
+           DEFAULT_SYS_DIR=$withval, DEFAULT_SYS_DIR="/etc/lvm")
+AC_DEFINE_UNQUOTED(DEFAULT_SYS_DIR, ["$DEFAULT_SYS_DIR"],
+                  [Path to LVM system directory.])
+
+AC_ARG_WITH(default-archive-subdir,
+           AC_HELP_STRING([--with-default-archive-subdir=SUBDIR],
+                          [default metadata archive subdir [[archive]]]),
+           DEFAULT_ARCHIVE_SUBDIR=$withval, DEFAULT_ARCHIVE_SUBDIR=archive)
+AC_DEFINE_UNQUOTED(DEFAULT_ARCHIVE_SUBDIR, ["$DEFAULT_ARCHIVE_SUBDIR"],
+                  [Name of default metadata archive subdirectory.])
+
+AC_ARG_WITH(default-backup-subdir,
+           AC_HELP_STRING([--with-default-backup-subdir=SUBDIR],
+                          [default metadata backup subdir [[backup]]]),
+           DEFAULT_BACKUP_SUBDIR=$withval, DEFAULT_BACKUP_SUBDIR=backup)
+AC_DEFINE_UNQUOTED(DEFAULT_BACKUP_SUBDIR, ["$DEFAULT_BACKUP_SUBDIR"],
+                   [Name of default metadata backup subdirectory.])
+
+AC_ARG_WITH(default-cache-subdir,
+           AC_HELP_STRING([--with-default-cache-subdir=SUBDIR],
+                          [default metadata cache subdir [[cache]]]),
+           DEFAULT_CACHE_SUBDIR=$withval, DEFAULT_CACHE_SUBDIR=cache)
+AC_DEFINE_UNQUOTED(DEFAULT_CACHE_SUBDIR, ["$DEFAULT_CACHE_SUBDIR"],
+                  [Name of default metadata cache subdirectory.])
+
+AC_ARG_WITH(default-locking-dir,
+           AC_HELP_STRING([--with-default-locking-dir=DIR],
+                          [default locking directory [[/var/lock/lvm]]]),
+           DEFAULT_LOCK_DIR=$withval, DEFAULT_LOCK_DIR="/var/lock/lvm")
+AC_DEFINE_UNQUOTED(DEFAULT_LOCK_DIR, ["$DEFAULT_LOCK_DIR"],
+                  [Name of default locking directory.])
+
+################################################################################
+dnl -- Setup default data alignment
+AC_ARG_WITH(default-data-alignment,
+           AC_HELP_STRING([--with-default-data-alignment=NUM],
+                          [set the default data alignment in MiB [[1]]]),
+           DEFAULT_DATA_ALIGNMENT=$withval, DEFAULT_DATA_ALIGNMENT=1)
+AC_DEFINE_UNQUOTED(DEFAULT_DATA_ALIGNMENT, [$DEFAULT_DATA_ALIGNMENT],
+                  [Default data alignment.])
+
+################################################################################
+dnl -- which kernel interface to use (ioctl only)
+AC_MSG_CHECKING(for kernel interface choice)
+AC_ARG_WITH(interface,
+           AC_HELP_STRING([--with-interface=IFACE],
+                          [choose kernel interface (ioctl) [[ioctl]]]),
+           interface=$withval, interface=ioctl)
+if [[ "x$interface" != xioctl ]];
+then
+  AC_MSG_ERROR(--with-interface=ioctl required. fs no longer supported.)
+fi
+AC_MSG_RESULT($interface)
+
+################################################################################
+DM_LIB_VERSION="\"`cat "$srcdir"/VERSION_DM 2>/dev/null || echo Unknown`\""
+AC_DEFINE_UNQUOTED(DM_LIB_VERSION, $DM_LIB_VERSION, [Library version])
+
+DM_LIB_PATCHLEVEL=`cat "$srcdir"/VERSION_DM | $AWK -F '[[-. ]]' '{printf "%s.%s.%s",$1,$2,$3}'`
+
+LVM_VERSION="\"`cat "$srcdir"/VERSION 2>/dev/null || echo Unknown`\""
+
+VER=`cat "$srcdir"/VERSION`
+LVM_RELEASE_DATE="\"`echo $VER | $SED 's/.* (//;s/).*//'`\""
+VER=`echo "$VER" | $AWK '{print $1}'`
+LVM_RELEASE="\"`echo "$VER" | $AWK -F '-' '{print $2}'`\""
+VER=`echo "$VER" | $AWK -F '-' '{print $1}'`
+LVM_MAJOR=`echo "$VER" | $AWK -F '.' '{print $1}'`
+LVM_MINOR=`echo "$VER" | $AWK -F '.' '{print $2}'`
+LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[[(.]]' '{print $3}'`
+LVM_LIBAPI=`echo "$VER" | $AWK -F '[[()]]' '{print $2}'`
+
+################################################################################
+AC_SUBST(APPLIB)
+AC_SUBST(AWK)
+AC_SUBST(BUILD_CMIRRORD)
+AC_SUBST(BUILD_DMEVENTD)
+AC_SUBST(CCS_CFLAGS)
+AC_SUBST(CCS_LIBS)
+AC_SUBST(CFLAGS)
+AC_SUBST(CFLOW_CMD)
+AC_SUBST(CLDFLAGS)
+AC_SUBST(CLDNOWHOLEARCHIVE)
+AC_SUBST(CLDWHOLEARCHIVE)
+AC_SUBST(CLUSTER)
+AC_SUBST(CLVMD)
+AC_SUBST(CLVMD_CMANAGERS)
+AC_SUBST(CMAN_CFLAGS)
+AC_SUBST(CMAN_LIBS)
+AC_SUBST(CMDLIB)
+AC_SUBST(CONFDB_CFLAGS)
+AC_SUBST(CONFDB_LIBS)
+AC_SUBST(CONFDIR)
+AC_SUBST(COPTIMISE_FLAG)
+AC_SUBST(CPG_CFLAGS)
+AC_SUBST(CPG_LIBS)
+AC_SUBST(CSCOPE_CMD)
+AC_SUBST(DEBUG)
+AC_SUBST(DEFAULT_SYS_DIR)
+AC_SUBST(DEFAULT_ARCHIVE_SUBDIR)
+AC_SUBST(DEFAULT_BACKUP_SUBDIR)
+AC_SUBST(DEFAULT_CACHE_SUBDIR)
+AC_SUBST(DEFAULT_DATA_ALIGNMENT)
+AC_SUBST(DEFAULT_LOCK_DIR)
+AC_SUBST(DEFAULT_RUN_DIR)
+AC_SUBST(DEVMAPPER)
+AC_SUBST(DLM_CFLAGS)
+AC_SUBST(DLM_LIBS)
+AC_SUBST(DL_LIBS)
+AC_SUBST(DMEVENTD)
+AC_SUBST(DMEVENTD_PATH)
+AC_SUBST(DM_COMPAT)
+AC_SUBST(DM_DEVICE_GID)
+AC_SUBST(DM_DEVICE_MODE)
+AC_SUBST(DM_DEVICE_UID)
+AC_SUBST(DM_IOCTLS)
+AC_SUBST(DM_LIB_VERSION)
+AC_SUBST(DM_LIB_PATCHLEVEL)
+AC_SUBST(FSADM)
+AC_SUBST(GULM_CFLAGS)
+AC_SUBST(GULM_LIBS)
+AC_SUBST(HAVE_LIBDL)
+AC_SUBST(HAVE_REALTIME)
+AC_SUBST(INTL)
+AC_SUBST(INTL_PACKAGE)
+AC_SUBST(JOBS)
+AC_SUBST(LDDEPS)
+AC_SUBST(LIBS)
+AC_SUBST(LIB_SUFFIX)
+AC_SUBST(LOCALEDIR)
+AC_SUBST(LVM1)
+AC_SUBST(LVM1_FALLBACK)
+AC_SUBST(LVM_VERSION)
+AC_SUBST(LVM_LIBAPI)
+AC_SUBST(LVM_MAJOR)
+AC_SUBST(LVM_MINOR)
+AC_SUBST(LVM_PATCHLEVEL)
+AC_SUBST(LVM_RELEASE)
+AC_SUBST(LVM_RELEASE_DATE)
+AC_SUBST(MIRRORS)
+AC_SUBST(OCF)
+AC_SUBST(REPLICATORS)
+AC_SUBST(MSGFMT)
+AC_SUBST(PKGCONFIG)
+AC_SUBST(POOL)
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(QUORUM_CFLAGS)
+AC_SUBST(QUORUM_LIBS)
+AC_SUBST(READLINE_LIBS)
+AC_SUBST(SACKPT_CFLAGS)
+AC_SUBST(SACKPT_LIBS)
+AC_SUBST(SALCK_CFLAGS)
+AC_SUBST(SALCK_LIBS)
+AC_SUBST(SELINUX_LIBS)
+AC_SUBST(SELINUX_PC)
+AC_SUBST(SNAPSHOTS)
+AC_SUBST(STATICDIR)
+AC_SUBST(STATIC_LINK)
+AC_SUBST(TESTING)
+AC_SUBST(UDEV_LIBS)
+AC_SUBST(UDEV_PC)
+AC_SUBST(UDEV_RULES)
+AC_SUBST(UDEV_SYNC)
+AC_SUBST(WRITE_INSTALL)
+AC_SUBST(interface)
+AC_SUBST(kerneldir)
+AC_SUBST(missingkernel)
+AC_SUBST(kernelvsn)
+AC_SUBST(tmpdir)
+AC_SUBST(udev_prefix)
+AC_SUBST(udevdir)
+AC_SUBST(usrlibdir)
+AC_SUBST(usrsbindir)
+
+################################################################################
+dnl -- First and last lines should not contain files to generate in order to
+dnl -- keep utility scripts running properly
+AC_CONFIG_FILES([
+Makefile
+make.tmpl
+daemons/Makefile
+daemons/clvmd/Makefile
+daemons/cmirrord/Makefile
+daemons/dmeventd/Makefile
+daemons/dmeventd/libdevmapper-event.pc
+daemons/dmeventd/plugins/Makefile
+daemons/dmeventd/plugins/lvm2/Makefile
+daemons/dmeventd/plugins/mirror/Makefile
+daemons/dmeventd/plugins/snapshot/Makefile
+doc/Makefile
+doc/example.conf
+include/.symlinks
+include/Makefile
+lib/Makefile
+lib/format1/Makefile
+lib/format_pool/Makefile
+lib/locking/Makefile
+lib/mirror/Makefile
+lib/replicator/Makefile
+lib/misc/lvm-version.h
+lib/snapshot/Makefile
+libdm/Makefile
+libdm/libdevmapper.pc
+liblvm/Makefile
+liblvm/liblvm2app.pc
+man/Makefile
+po/Makefile
+scripts/clvmd_init_red_hat
+scripts/cmirrord_init_red_hat
+scripts/lvm2_monitoring_init_red_hat
+scripts/Makefile
+test/Makefile
+test/api/Makefile
+tools/Makefile
+udev/Makefile
+unit-tests/datastruct/Makefile
+unit-tests/regex/Makefile
+unit-tests/mm/Makefile
+])
+AC_OUTPUT
+
+if test x$ODIRECT != xyes; then
+  AC_MSG_WARN(Warning: O_DIRECT disabled: low-memory pvmove may lock up)
+fi
diff --git a/daemons/Makefile.in b/daemons/Makefile.in
new file mode 100644 (file)
index 0000000..ce400d7
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+.PHONY: dmeventd clvmd cmirrord
+
+ifneq ("@CLVMD@", "none")
+  SUBDIRS = clvmd
+endif
+
+ifeq ("@BUILD_CMIRRORD@", "yes")
+  SUBDIRS += cmirrord
+endif
+
+ifeq ("@BUILD_DMEVENTD@", "yes")
+  SUBDIRS += dmeventd
+ifneq ("$(CFLOW_CMD)", "")
+daemons.cflow: dmeventd.cflow
+endif
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+  SUBDIRS = clvmd cmirrord dmeventd
+endif
+
+include $(top_builddir)/make.tmpl
+
+ifeq ("@BUILD_DMEVENTD@", "yes")
+device-mapper: dmeventd.device-mapper
+endif
diff --git a/daemons/clvmd/Makefile.in b/daemons/clvmd/Makefile.in
new file mode 100644 (file)
index 0000000..ee19e6c
--- /dev/null
@@ -0,0 +1,117 @@
+#
+# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CCS_LIBS = @CCS_LIBS@
+CCS_CFLAGS = @CCS_CFLAGS@
+CMAN_LIBS = @CMAN_LIBS@
+CMAN_CFLAGS = @CMAN_CFLAGS@
+CONFDB_LIBS = @CONFDB_LIBS@
+CONFDB_CFLAGS = @CONFDB_CFLAGS@
+CPG_LIBS = @CPG_LIBS@
+CPG_CFLAGS = @CPG_CFLAGS@
+DLM_LIBS = @DLM_LIBS@
+DLM_CFLAGS = @DLM_CFLAGS@
+GULM_LIBS = @GULM_LIBS@
+GULM_CFLAGS = @GULM_CFLAGS@
+QUORUM_LIBS = @QUORUM_LIBS@
+QUORUM_CFLAGS = @QUORUM_CFLAGS@
+SALCK_LIBS = @SALCK_LIBS@
+SALCK_CFLAGS = @SALCK_CFLAGS@
+
+SOURCES = \
+       clvmd-command.c  \
+       clvmd.c          \
+       lvm-functions.c  \
+       refresh_clvmd.c
+
+ifeq ("@DEBUG@", "yes")
+       DEFS += -DDEBUG
+endif
+
+ifneq (,$(findstring gulm,, "@CLVMD@,"))
+       SOURCES += clvmd-gulm.c tcp-comms.c
+       LMLIBS += $(CCS_LIBS) $(GULM_LIBS)
+       CFLAGS += $(CCS_CFLAGS) $(GULM_CFLAGS)
+       DEFS += -DUSE_GULM
+endif
+
+ifneq (,$(findstring cman,, "@CLVMD@,"))
+       SOURCES += clvmd-cman.c
+       LMLIBS += $(CMAN_LIBS) $(CONFDB_LIBS) $(DLM_LIBS)
+       CFLAGS += $(CMAN_CFLAGS) $(CONFDB_CFLAGS) $(DLM_CFLAGS)
+       DEFS += -DUSE_CMAN
+endif
+
+ifneq (,$(findstring openais,, "@CLVMD@,"))
+       SOURCES += clvmd-openais.c
+       LMLIBS += $(CONFDB_LIBS) $(CPG_LIBS) $(SALCK_LIBS)
+       CFLAGS += $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(SALCK_CFLAGS)
+       DEFS += -DUSE_OPENAIS
+endif
+
+ifneq (,$(findstring corosync,, "@CLVMD@,"))
+       SOURCES += clvmd-corosync.c
+       LMLIBS += $(CONFDB_LIBS) $(CPG_LIBS) $(DLM_LIBS) $(QUORUM_LIBS)
+       CFLAGS += $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(DLM_CFLAGS) $(QUORUM_CFLAGS)
+       DEFS += -DUSE_COROSYNC
+endif
+
+ifneq (,$(findstring singlenode,, &quot;@CLVMD@,&quot;))
+       SOURCES += clvmd-singlenode.c
+       DEFS += -DUSE_SINGLENODE
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+       SOURCES += clvmd-gulm.c tcp-comms.c
+       SOURCES += clvmd-cman.c
+       SOURCES += clvmd-openais.c
+       SOURCES += clvmd-corosync.c
+       SOURCES += clvmd-singlenode.c
+endif
+
+TARGETS = \
+       clvmd
+
+LVMLIBS = $(LVMINTERNAL_LIBS)
+
+ifeq ("@DMEVENTD@", "yes")
+       LVMLIBS += -ldevmapper-event
+endif
+include $(top_builddir)/make.tmpl
+
+LVMLIBS += -ldevmapper
+LIBS += $(PTHREAD_LIBS)
+
+DEFS += -D_REENTRANT
+CFLAGS += -fno-strict-aliasing
+
+INSTALL_TARGETS = \
+       install_clvmd
+
+clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -o clvmd $(OBJECTS) \
+               $(LVMLIBS) $(LMLIBS) $(LIBS)
+
+.PHONY: install_clvmd
+
+install_clvmd: $(TARGETS)
+       $(INSTALL_PROGRAM) -D clvmd $(usrsbindir)/clvmd
+
+install: $(INSTALL_TARGETS)
+
+install_cluster: $(INSTALL_TARGETS)
diff --git a/daemons/clvmd/clvm.h b/daemons/clvmd/clvm.h
new file mode 100644 (file)
index 0000000..c9ea10c
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/* Definitions for CLVMD server and clients */
+
+/*
+ * The protocol spoken over the cluster and across the local socket.
+ */
+
+#ifndef _CLVM_H
+#define _CLVM_H
+
+#include "configure.h"
+
+struct clvm_header {
+       uint8_t  cmd;           /* See below */
+       uint8_t  flags;         /* See below */
+       uint16_t xid;           /* Transaction ID */
+       uint32_t clientid;      /* Only used in Daemon->Daemon comms */
+       int32_t  status;        /* For replies, whether request succeeded */
+       uint32_t arglen;        /* Length of argument below. 
+                                  If >1500 then it will be passed 
+                                  around the cluster in the system LV */
+       char node[1];           /* Actually a NUL-terminated string, node name.
+                                  If this is empty then the command is 
+                                  forwarded to all cluster nodes unless 
+                                  FLAG_LOCAL is also set. */
+       char args[1];           /* Arguments for the command follow the 
+                                  node name, This member is only
+                                  valid if the node name is empty */
+} __attribute__ ((packed));
+
+/* Flags */
+#define CLVMD_FLAG_LOCAL        1      /* Only do this on the local node */
+#define CLVMD_FLAG_SYSTEMLV     2      /* Data in system LV under my node name */
+#define CLVMD_FLAG_NODEERRS     4       /* Reply has errors in node-specific portion */
+
+/* Name of the local socket to communicate between lvm and clvmd */
+static const char CLVMD_SOCKNAME[]= DEFAULT_RUN_DIR "/clvmd.sock";
+
+/* Internal commands & replies */
+#define CLVMD_CMD_REPLY    1
+#define CLVMD_CMD_VERSION  2   /* Send version around cluster when we start */
+#define CLVMD_CMD_GOAWAY   3   /* Die if received this - we are running 
+                                  an incompatible version */
+#define CLVMD_CMD_TEST     4   /* Just for mucking about */
+
+#define CLVMD_CMD_LOCK              30
+#define CLVMD_CMD_UNLOCK            31
+
+/* Lock/Unlock commands */
+#define CLVMD_CMD_LOCK_LV           50
+#define CLVMD_CMD_LOCK_VG           51
+#define CLVMD_CMD_LOCK_QUERY       52
+
+/* Misc functions */
+#define CLVMD_CMD_REFRESH          40
+#define CLVMD_CMD_GET_CLUSTERNAME   41
+#define CLVMD_CMD_SET_DEBUG        42
+#define CLVMD_CMD_VG_BACKUP        43
+#define CLVMD_CMD_RESTART          44
+#endif
diff --git a/daemons/clvmd/clvmd-cman.c b/daemons/clvmd/clvmd-cman.c
new file mode 100644 (file)
index 0000000..52da2ac
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/*
+ * CMAN communication layer for clvmd.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvmd-comms.h"
+#include "clvm.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+#include <libdlm.h>
+
+#include <syslog.h>
+
+#define LOCKSPACE_NAME "clvmd"
+
+struct clvmd_node
+{
+       struct cman_node *node;
+       int clvmd_up;
+};
+
+static int num_nodes;
+static struct cman_node *nodes = NULL;
+static struct cman_node this_node;
+static int count_nodes; /* size of allocated nodes array */
+static struct dm_hash_table *node_updown_hash;
+static dlm_lshandle_t *lockspace;
+static cman_handle_t c_handle;
+
+static void count_clvmds_running(void);
+static void get_members(void);
+static int nodeid_from_csid(const char *csid);
+static int name_from_nodeid(int nodeid, char *name);
+static void event_callback(cman_handle_t handle, void *private, int reason, int arg);
+static void data_callback(cman_handle_t handle, void *private,
+                         char *buf, int len, uint8_t port, int nodeid);
+
+struct lock_wait {
+       pthread_cond_t cond;
+       pthread_mutex_t mutex;
+       struct dlm_lksb lksb;
+};
+
+static int _init_cluster(void)
+{
+       node_updown_hash = dm_hash_create(100);
+
+       /* Open the cluster communication socket */
+       c_handle = cman_init(NULL);
+       if (!c_handle) {
+               syslog(LOG_ERR, "Can't open cluster manager socket: %m");
+               return -1;
+       }
+       DEBUGLOG("Connected to CMAN\n");
+
+       if (cman_start_recv_data(c_handle, data_callback, CLUSTER_PORT_CLVMD)) {
+               syslog(LOG_ERR, "Can't bind cluster socket: %m");
+               return -1;
+       }
+
+       if (cman_start_notification(c_handle, event_callback)) {
+               syslog(LOG_ERR, "Can't start cluster event listening");
+               return -1;
+       }
+
+       /* Get the cluster members list */
+       get_members();
+       count_clvmds_running();
+
+       DEBUGLOG("CMAN initialisation complete\n");
+
+       /* Create a lockspace for LV & VG locks to live in */
+       lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
+       if (!lockspace) {
+               if (errno == EEXIST) {
+                       lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
+               }
+               if (!lockspace) {
+                       syslog(LOG_ERR, "Unable to create lockspace for CLVM: %m");
+                       return -1;
+               }
+       }
+       dlm_ls_pthread_init(lockspace);
+       DEBUGLOG("DLM initialisation complete\n");
+       return 0;
+}
+
+static void _cluster_init_completed(void)
+{
+       clvmd_cluster_init_completed();
+}
+
+static int _get_main_cluster_fd()
+{
+       return cman_get_fd(c_handle);
+}
+
+static int _get_num_nodes()
+{
+       int i;
+       int nnodes = 0;
+
+       /* return number of ACTIVE nodes */
+       for (i=0; i<num_nodes; i++) {
+               if (nodes[i].cn_member && nodes[i].cn_nodeid)
+                       nnodes++;
+       }
+       return nnodes;
+}
+
+/* send_message with the fd check removed */
+static int _cluster_send_message(const void *buf, int msglen, const char *csid,
+                                const char *errtext)
+{
+       int nodeid = 0;
+
+       if (csid)
+               memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
+
+       if (cman_send_data(c_handle, buf, msglen, 0, CLUSTER_PORT_CLVMD, nodeid) <= 0)
+       {
+               log_error("%s", errtext);
+       }
+       return msglen;
+}
+
+static void _get_our_csid(char *csid)
+{
+       if (this_node.cn_nodeid == 0) {
+               cman_get_node(c_handle, 0, &this_node);
+       }
+       memcpy(csid, &this_node.cn_nodeid, CMAN_MAX_CSID_LEN);
+}
+
+/* Call a callback routine for each node is that known (down means not running a clvmd) */
+static int _cluster_do_node_callback(struct local_client *client,
+                                    void (*callback) (struct local_client *,
+                                                      const char *,
+                                                      int))
+{
+       int i;
+       int somedown = 0;
+
+       for (i = 0; i < _get_num_nodes(); i++) {
+               if (nodes[i].cn_member && nodes[i].cn_nodeid) {
+                       int up = (int)(long)dm_hash_lookup_binary(node_updown_hash, (char *)&nodes[i].cn_nodeid, sizeof(int));
+
+                       callback(client, (char *)&nodes[i].cn_nodeid, up);
+                       if (!up)
+                               somedown = -1;
+               }
+       }
+       return somedown;
+}
+
+/* Process OOB messages from the cluster socket */
+static void event_callback(cman_handle_t handle, void *private, int reason, int arg)
+{
+       char namebuf[MAX_CLUSTER_MEMBER_NAME_LEN];
+
+       switch (reason) {
+        case CMAN_REASON_PORTCLOSED:
+               name_from_nodeid(arg, namebuf);
+               log_notice("clvmd on node %s has died\n", namebuf);
+               DEBUGLOG("Got port closed message, removing node %s\n", namebuf);
+
+               dm_hash_insert_binary(node_updown_hash, (char *)&arg, sizeof(int), (void *)0);
+               break;
+
+       case CMAN_REASON_STATECHANGE:
+               DEBUGLOG("Got state change message, re-reading members list\n");
+               get_members();
+               break;
+
+#if defined(LIBCMAN_VERSION) && LIBCMAN_VERSION >= 2
+       case CMAN_REASON_PORTOPENED:
+               /* Ignore this, wait for startup message from clvmd itself */
+               break;
+
+       case CMAN_REASON_TRY_SHUTDOWN:
+               DEBUGLOG("Got try shutdown, sending OK\n");
+               cman_replyto_shutdown(c_handle, 1);
+               break;
+#endif
+       default:
+               /* ERROR */
+               DEBUGLOG("Got unknown event callback message: %d\n", reason);
+               break;
+       }
+}
+
+static struct local_client *cman_client;
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+                               const char *csid,
+                               struct local_client **new_client)
+{
+
+       /* Save this for data_callback */
+       cman_client = fd;
+
+       /* We never return a new client */
+       *new_client = NULL;
+
+       return cman_dispatch(c_handle, 0);
+}
+
+
+static void data_callback(cman_handle_t handle, void *private,
+                         char *buf, int len, uint8_t port, int nodeid)
+{
+       /* Ignore looped back messages */
+       if (nodeid == this_node.cn_nodeid)
+               return;
+       process_message(cman_client, buf, len, (char *)&nodeid);
+}
+
+static void _add_up_node(const char *csid)
+{
+       /* It's up ! */
+       int nodeid = nodeid_from_csid(csid);
+
+       dm_hash_insert_binary(node_updown_hash, (char *)&nodeid, sizeof(int), (void *)1);
+       DEBUGLOG("Added new node %d to updown list\n", nodeid);
+}
+
+static void _cluster_closedown()
+{
+       destroy_lvhash();
+       dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+       cman_finish(c_handle);
+}
+
+static int is_listening(int nodeid)
+{
+       int status;
+
+       do {
+               status = cman_is_listening(c_handle, nodeid, CLUSTER_PORT_CLVMD);
+               if (status < 0 && errno == EBUSY) {     /* Don't busywait */
+                       sleep(1);
+                       errno = EBUSY;  /* In case sleep trashes it */
+               }
+       }
+       while (status < 0 && errno == EBUSY);
+
+       return status;
+}
+
+/* Populate the list of CLVMDs running.
+   called only at startup time */
+static void count_clvmds_running(void)
+{
+       int i;
+
+       for (i = 0; i < num_nodes; i++) {
+               int nodeid = nodes[i].cn_nodeid;
+
+               if (is_listening(nodeid) == 1)
+                       dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)1);
+               else
+                       dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)0);
+       }
+}
+
+/* Get a list of active cluster members */
+static void get_members()
+{
+       int retnodes;
+       int status;
+       int i;
+       int high_nodeid = 0;
+
+       num_nodes = cman_get_node_count(c_handle);
+       if (num_nodes == -1) {
+               log_error("Unable to get node count");
+               return;
+       }
+
+       /* Not enough room for new nodes list ? */
+       if (num_nodes > count_nodes && nodes) {
+               free(nodes);
+               nodes = NULL;
+       }
+
+       if (nodes == NULL) {
+               count_nodes = num_nodes + 10; /* Overallocate a little */
+               nodes = malloc(count_nodes * sizeof(struct cman_node));
+               if (!nodes) {
+                       log_error("Unable to allocate nodes array\n");
+                       exit(5);
+               }
+       }
+
+       status = cman_get_nodes(c_handle, count_nodes, &retnodes, nodes);
+       if (status < 0) {
+               log_error("Unable to get node details");
+               exit(6);
+       }
+
+       /* Get the highest nodeid */
+       for (i=0; i<retnodes; i++) {
+               if (nodes[i].cn_nodeid > high_nodeid)
+                       high_nodeid = nodes[i].cn_nodeid;
+       }
+}
+
+
+/* Convert a node name to a CSID */
+static int _csid_from_name(char *csid, const char *name)
+{
+       int i;
+
+       for (i = 0; i < num_nodes; i++) {
+               if (strcmp(name, nodes[i].cn_name) == 0) {
+                       memcpy(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+/* Convert a CSID to a node name */
+static int _name_from_csid(const char *csid, char *name)
+{
+       int i;
+
+       for (i = 0; i < num_nodes; i++) {
+               if (memcmp(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN) == 0) {
+                       strcpy(name, nodes[i].cn_name);
+                       return 0;
+               }
+       }
+       /* Who?? */
+       strcpy(name, "Unknown");
+       return -1;
+}
+
+/* Convert a node ID to a node name */
+static int name_from_nodeid(int nodeid, char *name)
+{
+       int i;
+
+       for (i = 0; i < num_nodes; i++) {
+               if (nodeid == nodes[i].cn_nodeid) {
+                       strcpy(name, nodes[i].cn_name);
+                       return 0;
+               }
+       }
+       /* Who?? */
+       strcpy(name, "Unknown");
+       return -1;
+}
+
+/* Convert a CSID to a node ID */
+static int nodeid_from_csid(const char *csid)
+{
+        int nodeid;
+
+       memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
+
+       return nodeid;
+}
+
+static int _is_quorate()
+{
+       return cman_is_quorate(c_handle);
+}
+
+static void sync_ast_routine(void *arg)
+{
+       struct lock_wait *lwait = arg;
+
+       pthread_mutex_lock(&lwait->mutex);
+       pthread_cond_signal(&lwait->cond);
+       pthread_mutex_unlock(&lwait->mutex);
+}
+
+static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+       int status;
+       struct lock_wait lwait;
+
+       if (!lockid) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       DEBUGLOG("sync_lock: '%s' mode:%d flags=%d\n", resource,mode,flags);
+       /* Conversions need the lockid in the LKSB */
+       if (flags & LKF_CONVERT)
+               lwait.lksb.sb_lkid = *lockid;
+
+       pthread_cond_init(&lwait.cond, NULL);
+       pthread_mutex_init(&lwait.mutex, NULL);
+       pthread_mutex_lock(&lwait.mutex);
+
+       status = dlm_ls_lock(lockspace,
+                            mode,
+                            &lwait.lksb,
+                            flags,
+                            resource,
+                            strlen(resource),
+                            0, sync_ast_routine, &lwait, NULL, NULL);
+       if (status)
+               return status;
+
+       /* Wait for it to complete */
+       pthread_cond_wait(&lwait.cond, &lwait.mutex);
+       pthread_mutex_unlock(&lwait.mutex);
+
+       *lockid = lwait.lksb.sb_lkid;
+
+       errno = lwait.lksb.sb_status;
+       DEBUGLOG("sync_lock: returning lkid %x\n", *lockid);
+       if (lwait.lksb.sb_status)
+               return -1;
+       else
+               return 0;
+}
+
+static int _sync_unlock(const char *resource /* UNUSED */, int lockid)
+{
+       int status;
+       struct lock_wait lwait;
+
+       DEBUGLOG("sync_unlock: '%s' lkid:%x\n", resource, lockid);
+
+       pthread_cond_init(&lwait.cond, NULL);
+       pthread_mutex_init(&lwait.mutex, NULL);
+       pthread_mutex_lock(&lwait.mutex);
+
+       status = dlm_ls_unlock(lockspace, lockid, 0, &lwait.lksb, &lwait);
+
+       if (status)
+               return status;
+
+       /* Wait for it to complete */
+       pthread_cond_wait(&lwait.cond, &lwait.mutex);
+       pthread_mutex_unlock(&lwait.mutex);
+
+       errno = lwait.lksb.sb_status;
+       if (lwait.lksb.sb_status != EUNLOCK)
+               return -1;
+       else
+               return 0;
+
+}
+
+static int _get_cluster_name(char *buf, int buflen)
+{
+       cman_cluster_t cluster_info;
+       int status;
+
+       status = cman_get_cluster(c_handle, &cluster_info);
+       if (!status) {
+               strncpy(buf, cluster_info.ci_name, buflen);
+       }
+       return status;
+}
+
+static struct cluster_ops _cluster_cman_ops = {
+       .cluster_init_completed   = _cluster_init_completed,
+       .cluster_send_message     = _cluster_send_message,
+       .name_from_csid           = _name_from_csid,
+       .csid_from_name           = _csid_from_name,
+       .get_num_nodes            = _get_num_nodes,
+       .cluster_fd_callback      = _cluster_fd_callback,
+       .get_main_cluster_fd      = _get_main_cluster_fd,
+       .cluster_do_node_callback = _cluster_do_node_callback,
+       .is_quorate               = _is_quorate,
+       .get_our_csid             = _get_our_csid,
+       .add_up_node              = _add_up_node,
+       .cluster_closedown        = _cluster_closedown,
+       .get_cluster_name         = _get_cluster_name,
+       .sync_lock                = _sync_lock,
+       .sync_unlock              = _sync_unlock,
+};
+
+struct cluster_ops *init_cman_cluster(void)
+{
+       if (!_init_cluster())
+               return &_cluster_cman_ops;
+       else
+               return NULL;
+}
diff --git a/daemons/clvmd/clvmd-command.c b/daemons/clvmd/clvmd-command.c
new file mode 100644 (file)
index 0000000..49f1197
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/*
+
+  CLVMD Cluster LVM daemon command processor.
+
+  To add commands to the daemon simply add a processor in do_command and return
+  and messages back in buf and the length in *retlen. The initial value of
+  buflen is the maximum size of the buffer. if buf is not large enough then it
+  may be reallocated by the functions in here to a suitable size bearing in
+  mind that anything larger than the passed-in size will have to be returned
+  using the system LV and so performance will suffer.
+
+  The status return will be negated and passed back to the originating node.
+
+  pre- and post- command routines are called only on the local node. The
+  purpose is primarily to get and release locks, though the pre- routine should
+  also do any other local setups required by the command (if any) and can
+  return a failure code that prevents the command from being distributed around
+  the cluster
+
+  The pre- and post- routines are run in their own thread so can block as long
+  they like, do_command is run in the main clvmd thread so should not block for
+  too long. If the pre-command returns an error code (!=0) then the command
+  will not be propogated around the cluster but the post-command WILL be called
+
+  Also note that the pre and post routine are *always* called on the local
+  node, even if the command to be executed was only requested to run on a
+  remote node. It may peek inside the client structure to check the status of
+  the command.
+
+  The clients of the daemon must, naturally, understand the return messages and
+  codes.
+
+  Routines in here may only READ the values in the client structure passed in
+  apart from client->private which they are free to do what they like with.
+
+*/
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvmd-comms.h"
+#include "clvm.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+#include "locking.h"
+
+#include <sys/utsname.h>
+
+extern debug_t debug;
+extern struct cluster_ops *clops;
+static int restart_clvmd(void);
+
+/* This is where all the real work happens:
+   NOTE: client will be NULL when this is executed on a remote node */
+int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
+              char **buf, int buflen, int *retlen)
+{
+       char *args = msg->node + strlen(msg->node) + 1;
+       int arglen = msglen - sizeof(struct clvm_header) - strlen(msg->node);
+       int status = 0;
+       char *lockname;
+       const char *locktype;
+       struct utsname nodeinfo;
+       unsigned char lock_cmd;
+       unsigned char lock_flags;
+
+       /* Do the command */
+       switch (msg->cmd) {
+               /* Just a test message */
+       case CLVMD_CMD_TEST:
+               if (arglen > buflen) {
+                       char *new_buf;
+                       buflen = arglen + 200;
+                       new_buf = realloc(*buf, buflen);
+                       if (new_buf == NULL) {
+                               status = errno;
+                               free (*buf);
+                       }
+                       *buf = new_buf;
+               }
+               if (*buf) {
+                       uname(&nodeinfo);
+                       *retlen = 1 + dm_snprintf(*buf, buflen,
+                                                 "TEST from %s: %s v%s",
+                                                 nodeinfo.nodename, args,
+                                                 nodeinfo.release);
+               }
+               break;
+
+       case CLVMD_CMD_LOCK_VG:
+               lock_cmd = args[0];
+               lock_flags = args[1];
+               lockname = &args[2];
+               /* Check to see if the VG is in use by LVM1 */
+               status = do_check_lvm1(lockname);
+               do_lock_vg(lock_cmd, lock_flags, lockname);
+               break;
+
+       case CLVMD_CMD_LOCK_LV:
+               /* This is the biggie */
+               lock_cmd = args[0] & (LCK_NONBLOCK | LCK_HOLD | LCK_SCOPE_MASK | LCK_TYPE_MASK);
+               lock_flags = args[1];
+               lockname = &args[2];
+               status = do_lock_lv(lock_cmd, lock_flags, lockname);
+               /* Replace EIO with something less scary */
+               if (status == EIO) {
+                       *retlen = 1 + dm_snprintf(*buf, buflen, "%s",
+                                                 get_last_lvm_error());
+                       return EIO;
+               }
+               break;
+
+       case CLVMD_CMD_LOCK_QUERY:
+               lockname = &args[2];
+               if (buflen < 3)
+                       return EIO;
+               if ((locktype = do_lock_query(lockname)))
+                       *retlen = 1 + dm_snprintf(*buf, buflen, "%s", locktype);
+               break;
+
+       case CLVMD_CMD_REFRESH:
+               do_refresh_cache();
+               break;
+
+       case CLVMD_CMD_SET_DEBUG:
+               debug = args[0];
+               break;
+
+       case CLVMD_CMD_RESTART:
+               restart_clvmd();
+               break;
+
+       case CLVMD_CMD_GET_CLUSTERNAME:
+               status = clops->get_cluster_name(*buf, buflen);
+               if (!status)
+                       *retlen = strlen(*buf)+1;
+               break;
+
+       case CLVMD_CMD_VG_BACKUP:
+               /*
+                * Do not run backup on local node, caller should do that.
+                */
+               if (!client)
+                       lvm_do_backup(&args[2]);
+               break;
+
+       default:
+               /* Won't get here because command is validated in pre_command */
+               break;
+       }
+
+       /* Check the status of the command and return the error text */
+       if (status) {
+               *retlen = 1 + ((*buf) ? dm_snprintf(*buf, buflen, "%s",
+                                                   strerror(status)) : -1);
+       }
+
+       return status;
+
+}
+
+static int lock_vg(struct local_client *client)
+{
+    struct dm_hash_table *lock_hash;
+    struct clvm_header *header =
+       (struct clvm_header *) client->bits.localsock.cmd;
+    unsigned char lock_cmd;
+    unsigned char lock_flags;
+    int lock_mode;
+    char *args = header->node + strlen(header->node) + 1;
+    int lkid;
+    int status = 0;
+    char *lockname;
+
+    /* Keep a track of VG locks in our own hash table. In current
+       practice there should only ever be more than two VGs locked
+       if a user tries to merge lots of them at once */
+    if (client->bits.localsock.private) {
+       lock_hash = (struct dm_hash_table *)client->bits.localsock.private;
+    }
+    else {
+       lock_hash = dm_hash_create(3);
+       if (!lock_hash)
+           return ENOMEM;
+       client->bits.localsock.private = (void *)lock_hash;
+    }
+
+    lock_cmd = args[0] & (LCK_NONBLOCK | LCK_HOLD | LCK_SCOPE_MASK | LCK_TYPE_MASK);
+    lock_mode = ((int)lock_cmd & LCK_TYPE_MASK);
+    lock_flags = args[1];
+    lockname = &args[2];
+    DEBUGLOG("doing PRE command LOCK_VG '%s' at %x (client=%p)\n", lockname, lock_cmd, client);
+
+    if (lock_mode == LCK_UNLOCK) {
+
+       lkid = (int)(long)dm_hash_lookup(lock_hash, lockname);
+       if (lkid == 0)
+           return EINVAL;
+
+       status = sync_unlock(lockname, lkid);
+       if (status)
+           status = errno;
+       else
+           dm_hash_remove(lock_hash, lockname);
+    }
+    else {
+       /* Read locks need to be PR; other modes get passed through */
+       if (lock_mode == LCK_READ)
+           lock_mode = LCK_PREAD;
+       status = sync_lock(lockname, lock_mode, (lock_cmd & LCK_NONBLOCK) ? LCKF_NOQUEUE : 0, &lkid);
+       if (status)
+           status = errno;
+       else
+           dm_hash_insert(lock_hash, lockname, (void *)(long)lkid);
+    }
+
+    return status;
+}
+
+
+/* Pre-command is a good place to get locks that are needed only for the duration
+   of the commands around the cluster (don't forget to free them in post-command),
+   and to sanity check the command arguments */
+int do_pre_command(struct local_client *client)
+{
+       struct clvm_header *header =
+           (struct clvm_header *) client->bits.localsock.cmd;
+       unsigned char lock_cmd;
+       unsigned char lock_flags;
+       char *args = header->node + strlen(header->node) + 1;
+       int lockid;
+       int status = 0;
+       char *lockname;
+
+       switch (header->cmd) {
+       case CLVMD_CMD_TEST:
+               status = sync_lock("CLVMD_TEST", LCK_EXCL, 0, &lockid);
+               client->bits.localsock.private = (void *)(long)lockid;
+               break;
+
+       case CLVMD_CMD_LOCK_VG:
+               lockname = &args[2];
+               /* We take out a real lock unless LCK_CACHE was set */
+               if (!strncmp(lockname, "V_", 2) ||
+                   !strncmp(lockname, "P_#", 3))
+                       status = lock_vg(client);
+               break;
+
+       case CLVMD_CMD_LOCK_LV:
+               lock_cmd = args[0];
+               lock_flags = args[1];
+               lockname = &args[2];
+               status = pre_lock_lv(lock_cmd, lock_flags, lockname);
+               break;
+
+       case CLVMD_CMD_REFRESH:
+       case CLVMD_CMD_GET_CLUSTERNAME:
+       case CLVMD_CMD_SET_DEBUG:
+       case CLVMD_CMD_VG_BACKUP:
+       case CLVMD_CMD_LOCK_QUERY:
+       case CLVMD_CMD_RESTART:
+               break;
+
+       default:
+               log_error("Unknown command %d received\n", header->cmd);
+               status = EINVAL;
+       }
+       return status;
+}
+
+/* Note that the post-command routine is called even if the pre-command or the real command
+   failed */
+int do_post_command(struct local_client *client)
+{
+       struct clvm_header *header =
+           (struct clvm_header *) client->bits.localsock.cmd;
+       int status = 0;
+       unsigned char lock_cmd;
+       unsigned char lock_flags;
+       char *args = header->node + strlen(header->node) + 1;
+       char *lockname;
+
+       switch (header->cmd) {
+       case CLVMD_CMD_TEST:
+               status =
+                   sync_unlock("CLVMD_TEST", (int) (long) client->bits.localsock.private);
+               client->bits.localsock.private = 0;
+               break;
+
+       case CLVMD_CMD_LOCK_VG:
+       case CLVMD_CMD_VG_BACKUP:
+       case CLVMD_CMD_LOCK_QUERY:
+               /* Nothing to do here */
+               break;
+
+       case CLVMD_CMD_LOCK_LV:
+               lock_cmd = args[0];
+               lock_flags = args[1];
+               lockname = &args[2];
+               status = post_lock_lv(lock_cmd, lock_flags, lockname);
+               break;
+       }
+       return status;
+}
+
+
+/* Called when the client is about to be deleted */
+void cmd_client_cleanup(struct local_client *client)
+{
+    if (client->bits.localsock.private) {
+
+       struct dm_hash_node *v;
+       struct dm_hash_table *lock_hash =
+           (struct dm_hash_table *)client->bits.localsock.private;
+
+       dm_hash_iterate(v, lock_hash) {
+               int lkid = (int)(long)dm_hash_get_data(lock_hash, v);
+               char *lockname = dm_hash_get_key(lock_hash, v);
+
+               DEBUGLOG("cleanup: Unlocking lock %s %x\n", lockname, lkid);
+               sync_unlock(lockname, lkid);
+       }
+
+       dm_hash_destroy(lock_hash);
+       client->bits.localsock.private = 0;
+    }
+}
+
+
+static int restart_clvmd(void)
+{
+       char **argv = NULL;
+       char *debug_arg = NULL, *lv_name;
+       int i, argc = 0, max_locks = 0;
+       struct dm_hash_node *hn = NULL;
+
+       DEBUGLOG("clvmd restart requested\n");
+
+       /* Count exclusively-open LVs */
+       hn = NULL;
+       do {
+               hn = get_next_excl_lock(hn, &lv_name);
+               if (lv_name)
+                       max_locks++;
+       } while (hn && *lv_name);
+
+       /* clvmd + locks (-E uuid) + debug (-d X) + NULL */
+       argv = malloc((max_locks * 2 + 4) * sizeof(*argv));
+       if (!argv)
+               goto_out;
+
+       /*
+        * Build the command-line
+        */
+       argv[argc++] = strdup("clvmd");
+       if (!argv[0])
+               goto_out;
+
+       /* Propogate debug options */
+       if (debug) {
+               if (!(debug_arg = malloc(16)) ||
+                   dm_snprintf(debug_arg, 16, "-d%d", (int)debug) < 0)
+                       goto_out;
+               argv[argc++] = debug_arg;
+       }
+
+       /* Now add the exclusively-open LVs */
+       do {
+               hn = get_next_excl_lock(hn, &lv_name);
+               if (lv_name) {
+                       argv[argc] = strdup("-E");
+                       if (!argv[argc++])
+                               goto_out;
+                       argv[argc] = strdup(lv_name);
+                       if (!argv[argc++])
+                               goto_out;
+
+                       DEBUGLOG("excl lock: %s\n", lv_name);
+                       hn = get_next_excl_lock(hn, &lv_name);
+               }
+       } while (hn && *lv_name);
+       argv[argc++] = NULL;
+
+       /* Exec new clvmd */
+       /* NOTE: This will fail when downgrading! */
+       execve(CLVMD_PATH, argv, NULL);
+out:
+       /* We failed */
+       DEBUGLOG("Restart of clvmd failed.\n");
+
+       for (i = 0; i < argc && argv[i]; i++)
+               free(argv[i]);
+       free(argv);
+
+       return 0;
+}
diff --git a/daemons/clvmd/clvmd-common.h b/daemons/clvmd/clvmd-common.h
new file mode 100644 (file)
index 0000000..a94edd4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This file must be included first by every clvmd source file.
+ */
+#ifndef _LVM_CLVMD_COMMON_H
+#define _LVM_CLVMD_COMMON_H
+
+#include "configure.h"
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "libdevmapper.h"
+
+#include "lvm-logging.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#endif
diff --git a/daemons/clvmd/clvmd-comms.h b/daemons/clvmd/clvmd-comms.h
new file mode 100644 (file)
index 0000000..fbcfe8b
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/*
+ * Abstraction layer for clvmd cluster communications
+ */
+
+#ifndef _CLVMD_COMMS_H
+#define _CLVMD_COMMS_H
+
+struct local_client;
+
+struct cluster_ops {
+       void (*cluster_init_completed) (void);
+
+       int (*cluster_send_message) (const void *buf, int msglen,
+                                    const char *csid,
+                                    const char *errtext);
+       int (*name_from_csid) (const char *csid, char *name);
+       int (*csid_from_name) (char *csid, const char *name);
+       int (*get_num_nodes) (void);
+       int (*cluster_fd_callback) (struct local_client *fd, char *buf, int len,
+                                   const char *csid,
+                                   struct local_client **new_client);
+       int (*get_main_cluster_fd) (void);      /* gets accept FD or cman cluster socket */
+       int (*cluster_do_node_callback) (struct local_client *client,
+                                        void (*callback) (struct local_client *,
+                                                          const char *csid,
+                                                          int node_up));
+       int (*is_quorate) (void);
+
+       void (*get_our_csid) (char *csid);
+       void (*add_up_node) (const char *csid);
+       void (*reread_config) (void);
+       void (*cluster_closedown) (void);
+
+       int (*get_cluster_name)(char *buf, int buflen);
+
+       int (*sync_lock) (const char *resource, int mode,
+                         int flags, int *lockid);
+       int (*sync_unlock) (const char *resource, int lockid);
+
+};
+
+#ifdef USE_GULM
+#  include "tcp-comms.h"
+struct cluster_ops *init_gulm_cluster(void);
+#define MAX_CSID_LEN                   GULM_MAX_CSID_LEN
+#define MAX_CLUSTER_MEMBER_NAME_LEN    GULM_MAX_CLUSTER_MEMBER_NAME_LEN
+#endif
+
+#ifdef USE_CMAN
+#  include <netinet/in.h>
+#  include "libcman.h"
+#  define CMAN_MAX_CSID_LEN 4
+#  ifndef MAX_CSID_LEN
+#    define MAX_CSID_LEN CMAN_MAX_CSID_LEN
+#  endif
+#  undef MAX_CLUSTER_MEMBER_NAME_LEN
+#  define MAX_CLUSTER_MEMBER_NAME_LEN   CMAN_MAX_NODENAME_LEN
+#  define CMAN_MAX_CLUSTER_MESSAGE 1500
+#  define CLUSTER_PORT_CLVMD 11
+struct cluster_ops *init_cman_cluster(void);
+#endif
+
+#ifdef USE_OPENAIS
+#  include <openais/saAis.h>
+#  include <corosync/totem/totem.h>
+#  define OPENAIS_CSID_LEN (sizeof(int))
+#  define OPENAIS_MAX_CLUSTER_MESSAGE         MESSAGE_SIZE_MAX
+#  define OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH
+#  ifndef MAX_CLUSTER_MEMBER_NAME_LEN
+#    define MAX_CLUSTER_MEMBER_NAME_LEN       SA_MAX_NAME_LENGTH
+#  endif
+#  ifndef CMAN_MAX_CLUSTER_MESSAGE
+#    define CMAN_MAX_CLUSTER_MESSAGE          MESSAGE_SIZE_MAX
+#  endif
+#  ifndef MAX_CSID_LEN
+#    define MAX_CSID_LEN sizeof(int)
+#  endif
+struct cluster_ops *init_openais_cluster(void);
+#endif
+
+#ifdef USE_COROSYNC
+#  include <corosync/corotypes.h>
+#  define COROSYNC_CSID_LEN (sizeof(int))
+#  define COROSYNC_MAX_CLUSTER_MESSAGE         65535
+#  define COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH
+#  ifndef MAX_CLUSTER_MEMBER_NAME_LEN
+#    define MAX_CLUSTER_MEMBER_NAME_LEN       CS_MAX_NAME_LENGTH
+#  endif
+#  ifndef CMAN_MAX_CLUSTER_MESSAGE
+#    define CMAN_MAX_CLUSTER_MESSAGE          65535
+#  endif
+#  ifndef MAX_CSID_LEN
+#    define MAX_CSID_LEN sizeof(int)
+#  endif
+struct cluster_ops *init_corosync_cluster(void);
+#endif
+
+#ifdef USE_SINGLENODE
+#  define SINGLENODE_CSID_LEN (sizeof(int))
+#  ifndef MAX_CLUSTER_MEMBER_NAME_LEN
+#    define MAX_CLUSTER_MEMBER_NAME_LEN       64
+#  endif
+#  define SINGLENODE_MAX_CLUSTER_MESSAGE          65535
+#  ifndef MAX_CSID_LEN
+#    define MAX_CSID_LEN sizeof(int)
+#  endif
+struct cluster_ops *init_singlenode_cluster(void);
+#endif
+
+#endif
diff --git a/daemons/clvmd/clvmd-corosync.c b/daemons/clvmd/clvmd-corosync.c
new file mode 100644 (file)
index 0000000..cfe7150
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This provides the interface between clvmd and corosync/DLM as the cluster
+ * and lock manager.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+#include "locking.h"
+
+#include <corosync/cpg.h>
+#include <corosync/quorum.h>
+#include <corosync/confdb.h>
+#include <libdlm.h>
+
+#include <syslog.h>
+
+/* Timeout value for several corosync calls */
+#define LOCKSPACE_NAME "clvmd"
+
+static void corosync_cpg_deliver_callback (cpg_handle_t handle,
+                                 const struct cpg_name *groupName,
+                                 uint32_t nodeid,
+                                 uint32_t pid,
+                                 void *msg,
+                                 size_t msg_len);
+static void corosync_cpg_confchg_callback(cpg_handle_t handle,
+                                const struct cpg_name *groupName,
+                                const struct cpg_address *member_list, size_t member_list_entries,
+                                const struct cpg_address *left_list, size_t left_list_entries,
+                                const struct cpg_address *joined_list, size_t joined_list_entries);
+static void _cluster_closedown(void);
+
+/* Hash list of nodes in the cluster */
+static struct dm_hash_table *node_hash;
+
+/* Number of active nodes */
+static int num_nodes;
+static unsigned int our_nodeid;
+
+static struct local_client *cluster_client;
+
+/* Corosync handles */
+static cpg_handle_t cpg_handle;
+static quorum_handle_t quorum_handle;
+
+/* DLM Handle */
+static dlm_lshandle_t *lockspace;
+
+static struct cpg_name cpg_group_name;
+
+/* Corosync callback structs */
+cpg_callbacks_t corosync_cpg_callbacks = {
+       .cpg_deliver_fn =            corosync_cpg_deliver_callback,
+       .cpg_confchg_fn =            corosync_cpg_confchg_callback,
+};
+
+quorum_callbacks_t quorum_callbacks = {
+       .quorum_notify_fn = NULL,
+};
+
+struct node_info
+{
+       enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
+       int nodeid;
+};
+
+
+/* Set errno to something approximating the right value and return 0 or -1 */
+static int cs_to_errno(cs_error_t err)
+{
+       switch(err)
+       {
+       case CS_OK:
+               return 0;
+        case CS_ERR_LIBRARY:
+               errno = EINVAL;
+               break;
+        case CS_ERR_VERSION:
+               errno = EINVAL;
+               break;
+        case CS_ERR_INIT:
+               errno = EINVAL;
+               break;
+        case CS_ERR_TIMEOUT:
+               errno = ETIME;
+               break;
+        case CS_ERR_TRY_AGAIN:
+               errno = EAGAIN;
+               break;
+        case CS_ERR_INVALID_PARAM:
+               errno = EINVAL;
+               break;
+        case CS_ERR_NO_MEMORY:
+               errno = ENOMEM;
+               break;
+        case CS_ERR_BAD_HANDLE:
+               errno = EINVAL;
+               break;
+        case CS_ERR_BUSY:
+               errno = EBUSY;
+               break;
+        case CS_ERR_ACCESS:
+               errno = EPERM;
+               break;
+        case CS_ERR_NOT_EXIST:
+               errno = ENOENT;
+               break;
+        case CS_ERR_NAME_TOO_LONG:
+               errno = ENAMETOOLONG;
+               break;
+        case CS_ERR_EXIST:
+               errno = EEXIST;
+               break;
+        case CS_ERR_NO_SPACE:
+               errno = ENOSPC;
+               break;
+        case CS_ERR_INTERRUPT:
+               errno = EINTR;
+               break;
+       case CS_ERR_NAME_NOT_FOUND:
+               errno = ENOENT;
+               break;
+        case CS_ERR_NO_RESOURCES:
+               errno = ENOMEM;
+               break;
+        case CS_ERR_NOT_SUPPORTED:
+               errno = EOPNOTSUPP;
+               break;
+        case CS_ERR_BAD_OPERATION:
+               errno = EINVAL;
+               break;
+        case CS_ERR_FAILED_OPERATION:
+               errno = EIO;
+               break;
+        case CS_ERR_MESSAGE_ERROR:
+               errno = EIO;
+               break;
+        case CS_ERR_QUEUE_FULL:
+               errno = EXFULL;
+               break;
+        case CS_ERR_QUEUE_NOT_AVAILABLE:
+               errno = EINVAL;
+               break;
+        case CS_ERR_BAD_FLAGS:
+               errno = EINVAL;
+               break;
+        case CS_ERR_TOO_BIG:
+               errno = E2BIG;
+               break;
+        case CS_ERR_NO_SECTIONS:
+               errno = ENOMEM;
+               break;
+       default:
+               errno = EINVAL;
+               break;
+       }
+       return -1;
+}
+
+static char *print_corosync_csid(const char *csid)
+{
+       static char buf[128];
+       int id;
+
+       memcpy(&id, csid, sizeof(int));
+       sprintf(buf, "%d", id);
+       return buf;
+}
+
+static void corosync_cpg_deliver_callback (cpg_handle_t handle,
+                                 const struct cpg_name *groupName,
+                                 uint32_t nodeid,
+                                 uint32_t pid,
+                                 void *msg,
+                                 size_t msg_len)
+{
+       int target_nodeid;
+
+       memcpy(&target_nodeid, msg, COROSYNC_CSID_LEN);
+
+       DEBUGLOG("%u got message from nodeid %d for %d. len %zd\n",
+                our_nodeid, nodeid, target_nodeid, msg_len-4);
+
+       if (nodeid != our_nodeid)
+               if (target_nodeid == our_nodeid || target_nodeid == 0)
+                       process_message(cluster_client, (char *)msg+COROSYNC_CSID_LEN,
+                                       msg_len-COROSYNC_CSID_LEN, (char*)&nodeid);
+}
+
+static void corosync_cpg_confchg_callback(cpg_handle_t handle,
+                                const struct cpg_name *groupName,
+                                const struct cpg_address *member_list, size_t member_list_entries,
+                                const struct cpg_address *left_list, size_t left_list_entries,
+                                const struct cpg_address *joined_list, size_t joined_list_entries)
+{
+       int i;
+       struct node_info *ninfo;
+
+       DEBUGLOG("confchg callback. %zd joined, %zd left, %zd members\n",
+                joined_list_entries, left_list_entries, member_list_entries);
+
+       for (i=0; i<joined_list_entries; i++) {
+               ninfo = dm_hash_lookup_binary(node_hash,
+                                             (char *)&joined_list[i].nodeid,
+                                             COROSYNC_CSID_LEN);
+               if (!ninfo) {
+                       ninfo = malloc(sizeof(struct node_info));
+                       if (!ninfo) {
+                               break;
+                       }
+                       else {
+                               ninfo->nodeid = joined_list[i].nodeid;
+                               dm_hash_insert_binary(node_hash,
+                                                     (char *)&ninfo->nodeid,
+                                                     COROSYNC_CSID_LEN, ninfo);
+                       }
+               }
+               ninfo->state = NODE_CLVMD;
+       }
+
+       for (i=0; i<left_list_entries; i++) {
+               ninfo = dm_hash_lookup_binary(node_hash,
+                                             (char *)&left_list[i].nodeid,
+                                             COROSYNC_CSID_LEN);
+               if (ninfo)
+                       ninfo->state = NODE_DOWN;
+       }
+
+       for (i=0; i<member_list_entries; i++) {
+               if (member_list[i].nodeid == 0) continue;
+               ninfo = dm_hash_lookup_binary(node_hash,
+                               (char *)&member_list[i].nodeid,
+                               COROSYNC_CSID_LEN);
+               if (!ninfo) {
+                       ninfo = malloc(sizeof(struct node_info));
+                       if (!ninfo) {
+                               break;
+                       }
+                       else {
+                               ninfo->nodeid = member_list[i].nodeid;
+                               dm_hash_insert_binary(node_hash,
+                                               (char *)&ninfo->nodeid,
+                                               COROSYNC_CSID_LEN, ninfo);
+                       }
+               }
+               ninfo->state = NODE_CLVMD;
+       }
+
+       num_nodes = member_list_entries;
+}
+
+static int _init_cluster(void)
+{
+       cs_error_t err;
+
+       node_hash = dm_hash_create(100);
+
+       err = cpg_initialize(&cpg_handle,
+                            &corosync_cpg_callbacks);
+       if (err != CS_OK) {
+               syslog(LOG_ERR, "Cannot initialise Corosync CPG service: %d",
+                      err);
+               DEBUGLOG("Cannot initialise Corosync CPG service: %d", err);
+               return cs_to_errno(err);
+       }
+
+       err = quorum_initialize(&quorum_handle,
+                               &quorum_callbacks);
+       if (err != CS_OK) {
+               syslog(LOG_ERR, "Cannot initialise Corosync quorum service: %d",
+                      err);
+               DEBUGLOG("Cannot initialise Corosync quorum service: %d", err);
+               return cs_to_errno(err);
+       }
+
+
+       /* Create a lockspace for LV & VG locks to live in */
+       lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
+       if (!lockspace) {
+               if (errno == EEXIST) {
+                       lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
+               }
+               if (!lockspace) {
+                       syslog(LOG_ERR, "Unable to create lockspace for CLVM: %m");
+                       quorum_finalize(quorum_handle);
+                       return -1;
+               }
+       }
+       dlm_ls_pthread_init(lockspace);
+       DEBUGLOG("DLM initialisation complete\n");
+
+       /* Connect to the clvmd group */
+       strcpy((char *)cpg_group_name.value, "clvmd");
+       cpg_group_name.length = strlen((char *)cpg_group_name.value);
+       err = cpg_join(cpg_handle, &cpg_group_name);
+       if (err != CS_OK) {
+               cpg_finalize(cpg_handle);
+               quorum_finalize(quorum_handle);
+               dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+               syslog(LOG_ERR, "Cannot join clvmd process group");
+               DEBUGLOG("Cannot join clvmd process group: %d\n", err);
+               return cs_to_errno(err);
+       }
+
+       err = cpg_local_get(cpg_handle,
+                           &our_nodeid);
+       if (err != CS_OK) {
+               cpg_finalize(cpg_handle);
+               quorum_finalize(quorum_handle);
+               dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+               syslog(LOG_ERR, "Cannot get local node id\n");
+               return cs_to_errno(err);
+       }
+       DEBUGLOG("Our local node id is %d\n", our_nodeid);
+
+       DEBUGLOG("Connected to Corosync\n");
+
+       return 0;
+}
+
+static void _cluster_closedown(void)
+{
+       DEBUGLOG("cluster_closedown\n");
+       destroy_lvhash();
+
+       dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+       cpg_finalize(cpg_handle);
+       quorum_finalize(quorum_handle);
+}
+
+static void _get_our_csid(char *csid)
+{
+       memcpy(csid, &our_nodeid, sizeof(int));
+}
+
+/* Corosync doesn't really have nmode names so we
+   just use the node ID in hex instead */
+static int _csid_from_name(char *csid, const char *name)
+{
+       int nodeid;
+       struct node_info *ninfo;
+
+       if (sscanf(name, "%x", &nodeid) == 1) {
+               ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
+               if (ninfo)
+                       return nodeid;
+       }
+       return -1;
+}
+
+static int _name_from_csid(const char *csid, char *name)
+{
+       struct node_info *ninfo;
+
+       ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
+       if (!ninfo)
+       {
+               sprintf(name, "UNKNOWN %s", print_corosync_csid(csid));
+               return -1;
+       }
+
+       sprintf(name, "%x", ninfo->nodeid);
+       return 0;
+}
+
+static int _get_num_nodes()
+{
+       DEBUGLOG("num_nodes = %d\n", num_nodes);
+       return num_nodes;
+}
+
+/* Node is now known to be running a clvmd */
+static void _add_up_node(const char *csid)
+{
+       struct node_info *ninfo;
+
+       ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
+       if (!ninfo) {
+               DEBUGLOG("corosync_add_up_node no node_hash entry for csid %s\n",
+                        print_corosync_csid(csid));
+               return;
+       }
+
+       DEBUGLOG("corosync_add_up_node %d\n", ninfo->nodeid);
+
+       ninfo->state = NODE_CLVMD;
+
+       return;
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+                                    void (*callback)(struct local_client *,
+                                                     const char *csid, int node_up))
+{
+       struct dm_hash_node *hn;
+       struct node_info *ninfo;
+       int somedown = 0;
+
+       dm_hash_iterate(hn, node_hash)
+       {
+               char csid[COROSYNC_CSID_LEN];
+
+               ninfo = dm_hash_get_data(node_hash, hn);
+               memcpy(csid, dm_hash_get_key(node_hash, hn), COROSYNC_CSID_LEN);
+
+               DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
+                        ninfo->state);
+
+               if (ninfo->state != NODE_DOWN)
+                       callback(master_client, csid, ninfo->state == NODE_CLVMD);
+               if (ninfo->state != NODE_CLVMD)
+                       somedown = -1;
+       }
+       return somedown;
+}
+
+/* Real locking */
+static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
+{
+       struct dlm_lksb lksb;
+       int err;
+
+       DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
+
+       if (flags & LKF_CONVERT)
+               lksb.sb_lkid = *lockid;
+
+       err = dlm_ls_lock_wait(lockspace,
+                              mode,
+                              &lksb,
+                              flags,
+                              resource,
+                              strlen(resource),
+                              0,
+                              NULL, NULL, NULL);
+
+       if (err != 0)
+       {
+               DEBUGLOG("dlm_ls_lock returned %d\n", errno);
+               return err;
+       }
+       if (lksb.sb_status != 0)
+       {
+               DEBUGLOG("dlm_ls_lock returns lksb.sb_status %d\n", lksb.sb_status);
+               errno = lksb.sb_status;
+               return -1;
+       }
+
+       DEBUGLOG("lock_resource returning %d, lock_id=%x\n", err, lksb.sb_lkid);
+
+       *lockid = lksb.sb_lkid;
+
+       return 0;
+}
+
+
+static int _unlock_resource(const char *resource, int lockid)
+{
+       struct dlm_lksb lksb;
+       int err;
+
+       DEBUGLOG("unlock_resource: %s lockid: %x\n", resource, lockid);
+       lksb.sb_lkid = lockid;
+
+       err = dlm_ls_unlock_wait(lockspace,
+                                lockid,
+                                0,
+                                &lksb);
+       if (err != 0)
+       {
+               DEBUGLOG("Unlock returned %d\n", err);
+               return err;
+       }
+       if (lksb.sb_status != EUNLOCK)
+       {
+               DEBUGLOG("dlm_ls_unlock_wait returns lksb.sb_status: %d\n", lksb.sb_status);
+               errno = lksb.sb_status;
+               return -1;
+       }   
+
+
+       return 0;
+}
+
+static int _is_quorate()
+{
+       int quorate;
+       if (quorum_getquorate(quorum_handle, &quorate) == CS_OK)
+               return quorate;
+       else
+               return 0;
+}
+
+static int _get_main_cluster_fd(void)
+{
+       int select_fd;
+
+       cpg_fd_get(cpg_handle, &select_fd);
+       return select_fd;
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+                               const char *csid,
+                               struct local_client **new_client)
+{
+       cluster_client = fd;
+       *new_client = NULL;
+       cpg_dispatch(cpg_handle, CS_DISPATCH_ONE);
+       return 1;
+}
+
+static int _cluster_send_message(const void *buf, int msglen, const char *csid,
+                                const char *errtext)
+{
+       struct iovec iov[2];
+       cs_error_t err;
+       int target_node;
+
+       if (csid)
+               memcpy(&target_node, csid, COROSYNC_CSID_LEN);
+       else
+               target_node = 0;
+
+       iov[0].iov_base = &target_node;
+       iov[0].iov_len = sizeof(int);
+       iov[1].iov_base = (char *)buf;
+       iov[1].iov_len = msglen;
+
+       err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
+       return cs_to_errno(err);
+}
+
+/*
+ * We are not necessarily connected to a Red Hat Cluster system,
+ * but if we are, this returns the cluster name from cluster.conf.
+ * I've used confdb rather than ccs to reduce the inter-package
+ * dependancies as well as to allow people to set a cluster name
+ * for themselves even if they are not running on RH cluster.
+ */
+static int _get_cluster_name(char *buf, int buflen)
+{
+       confdb_handle_t handle;
+       int result;
+       size_t namelen = buflen;
+       hdb_handle_t cluster_handle;
+       confdb_callbacks_t callbacks = {
+               .confdb_key_change_notify_fn = NULL,
+               .confdb_object_create_change_notify_fn = NULL,
+               .confdb_object_delete_change_notify_fn = NULL
+       };
+
+       /* This is a default in case everything else fails */
+       strncpy(buf, "Corosync", buflen);
+
+       /* Look for a cluster name in confdb */
+       result = confdb_initialize (&handle, &callbacks);
+        if (result != CS_OK)
+               return 0;
+
+        result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
+       if (result != CS_OK)
+               goto out;
+
+        result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, (void *)"cluster", strlen("cluster"), &cluster_handle);
+        if (result != CS_OK)
+               goto out;
+
+        result = confdb_key_get(handle, cluster_handle, (void *)"name", strlen("name"), buf, &namelen);
+        if (result != CS_OK)
+               goto out;
+
+       buf[namelen] = '\0';
+
+out:
+       confdb_finalize(handle);
+       return 0;
+}
+
+static struct cluster_ops _cluster_corosync_ops = {
+       .cluster_init_completed   = NULL,
+       .cluster_send_message     = _cluster_send_message,
+       .name_from_csid           = _name_from_csid,
+       .csid_from_name           = _csid_from_name,
+       .get_num_nodes            = _get_num_nodes,
+       .cluster_fd_callback      = _cluster_fd_callback,
+       .get_main_cluster_fd      = _get_main_cluster_fd,
+       .cluster_do_node_callback = _cluster_do_node_callback,
+       .is_quorate               = _is_quorate,
+       .get_our_csid             = _get_our_csid,
+       .add_up_node              = _add_up_node,
+       .reread_config            = NULL,
+       .cluster_closedown        = _cluster_closedown,
+       .get_cluster_name         = _get_cluster_name,
+       .sync_lock                = _lock_resource,
+       .sync_unlock              = _unlock_resource,
+};
+
+struct cluster_ops *init_corosync_cluster(void)
+{
+       if (!_init_cluster())
+               return &_cluster_corosync_ops;
+       else
+               return NULL;
+}
diff --git a/daemons/clvmd/clvmd-gulm.c b/daemons/clvmd/clvmd-gulm.c
new file mode 100644 (file)
index 0000000..3561004
--- /dev/null
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2002-2003 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This provides the interface between clvmd and gulm as the cluster
+ * and lock manager.
+ *
+ * It also provides the "liblm" functions too as it's hard (and pointless)
+ * to seperate them out when using gulm.
+ *
+ * What it does /not/ provide is the communications between clvmd daemons
+ * on the cluster nodes. That is done in tcp-comms.c
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <utmpx.h>
+#include <syslog.h>
+#include <assert.h>
+#include <ccs.h>
+#include <libgulm.h>
+
+#include "locking.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "lvm-functions.h"
+#include "clvmd.h"
+#include "clvmd-gulm.h"
+
+/* Hash list of nodes in the cluster */
+static struct dm_hash_table *node_hash;
+
+/* hash list of outstanding lock requests */
+static struct dm_hash_table *lock_hash;
+
+/* Copy of the current quorate state */
+static uint8_t gulm_quorate = 0;
+static enum {INIT_NOTDONE, INIT_DONE, INIT_WAITQUORATE} init_state = INIT_NOTDONE;
+
+/* Number of active nodes */
+static int num_nodes;
+
+static char *cluster_name;
+static int in_shutdown = 0;
+
+static pthread_mutex_t lock_start_mutex;
+static volatile int lock_start_flag;
+
+struct node_info
+{
+    enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
+    char name[GULM_MAX_CLUSTER_MEMBER_NAME_LEN];
+};
+
+struct lock_wait
+{
+    pthread_cond_t cond;
+    pthread_mutex_t mutex;
+    int status;
+};
+
+/* Forward */
+static int read_from_core_sock(struct local_client *client, char *buf, int len, const char *csid,
+                              struct local_client **new_client);
+static int read_from_lock_sock(struct local_client *client, char *buf, int len, const char *csid,
+                              struct local_client **new_client);
+static int get_all_cluster_nodes(void);
+static int _csid_from_name(char *csid, const char *name);
+static void _cluster_closedown(void);
+
+/* In tcp-comms.c */
+extern struct dm_hash_table *sock_hash;
+
+static int add_internal_client(int fd, fd_callback_t callback)
+{
+    struct local_client *client;
+
+    DEBUGLOG("Add_internal_client, fd = %d\n", fd);
+
+    /* Add a GULM file descriptor it to the main loop */
+    client = malloc(sizeof(struct local_client));
+    if (!client)
+    {
+       DEBUGLOG("malloc failed\n");
+       return -1;
+    }
+
+    memset(client, 0, sizeof(struct local_client));
+    client->fd = fd;
+    client->type = CLUSTER_INTERNAL;
+    client->callback = callback;
+    add_client(client);
+
+    /* Set Close-on-exec */
+    fcntl(fd, F_SETFD, 1);
+
+    return 0;
+}
+
+/* Gulm library handle */
+static gulm_interface_p gulm_if;
+static lg_core_callbacks_t core_callbacks;
+static lg_lockspace_callbacks_t lock_callbacks;
+
+static void badsig_handler(int sig)
+{
+    DEBUGLOG("got sig %d\n", sig);
+    _cluster_closedown();
+    exit(0);
+}
+
+static void _reread_config(void)
+{
+        /* Re-read CCS node list */
+       DEBUGLOG("Re-reading CCS config\n");
+       get_all_cluster_nodes();
+}
+
+static int _init_cluster(void)
+{
+    int status;
+    int ccs_h;
+    int port = 0;
+    char *portstr;
+
+    /* Get cluster name from CCS */
+    ccs_h = ccs_force_connect(NULL, 0);
+    if (ccs_h < 0)
+    {
+       syslog(LOG_ERR, "Cannot login in to CCSD server\n");
+       return -1;
+    }
+
+    ccs_get(ccs_h, "//cluster/@name", &cluster_name);
+    DEBUGLOG("got cluster name %s\n", cluster_name);
+
+    if (!ccs_get(ccs_h, "//cluster/clvm/@port", &portstr))
+    {
+       port = atoi(portstr);
+       free(portstr);
+       DEBUGLOG("got port number %d\n", port);
+
+       if (port <= 0 && port >= 65536)
+           port = 0;
+    }
+
+    ccs_disconnect(ccs_h);
+
+    /* Block locking until we are logged in */
+    pthread_mutex_init(&lock_start_mutex, NULL);
+    pthread_mutex_lock(&lock_start_mutex);
+    lock_start_flag = 1;
+
+    node_hash = dm_hash_create(100);
+    lock_hash = dm_hash_create(10);
+
+    /* Get all nodes from CCS */
+    if (get_all_cluster_nodes())
+       return -1;
+
+    /* Initialise GULM library */
+    status = lg_initialize(&gulm_if, cluster_name, "clvmd");
+    if (status)
+    {
+       DEBUGLOG("lg_initialize failed: %d\n", status);
+       return status;
+    }
+
+    /* Connect to core - we are not "important" :-) */
+    status = lg_core_login(gulm_if, 0);
+    if (status)
+    {
+       DEBUGLOG("lg_core_login failed: %d\n", status);
+       return status;
+    }
+
+    /* Initialise the inter-node comms */
+    status = init_comms(port);
+    if (status)
+       return status;
+
+    /* Add core FD to the list */
+    status = add_internal_client(lg_core_selector(gulm_if), read_from_core_sock);
+    if (status)
+    {
+       DEBUGLOG("can't allocate client space\n");
+       return status;
+    }
+
+    /* Connect to the lock server */
+    if (lg_lock_login(gulm_if, "CLVM"))
+    {
+       syslog(LOG_ERR, "Cannot login in to LOCK server\n");
+       DEBUGLOG("Cannot login in to LOCK server\n");
+       exit(88);
+    }
+
+    /* Add lockspace FD to the list */
+    status = add_internal_client(lg_lock_selector(gulm_if), read_from_lock_sock);
+    if (status)
+    {
+       DEBUGLOG("can't allocate client space\n");
+       exit(status);
+    }
+
+    /* Request a list of nodes, we can't really do anything until
+       this comes back */
+    status = lg_core_nodelist(gulm_if);
+    if (status)
+    {
+       DEBUGLOG("lg_core_nodelist failed: %d\n", status);
+       return status;
+    }
+
+    /* So I can kill it without taking GULM down too */
+    signal(SIGINT, badsig_handler);
+    signal(SIGTERM, badsig_handler);
+
+    return 0;
+}
+
+static void _cluster_closedown(void)
+{
+    DEBUGLOG("cluster_closedown\n");
+    in_shutdown = 1;
+    destroy_lvhash();
+    lg_lock_logout(gulm_if);
+    lg_core_logout(gulm_if);
+    lg_release(gulm_if);
+}
+
+/* Expire locks for a named node, or us */
+#define GIO_KEY_SIZE 46
+static void drop_expired_locks(char *nodename)
+{
+    struct utsname nodeinfo;
+    uint8_t mask[GIO_KEY_SIZE];
+
+    DEBUGLOG("Dropping expired locks for %s\n", nodename?nodename:"(null)");
+    memset(mask, 0xff, GIO_KEY_SIZE);
+
+    if (!nodename)
+    {
+       uname(&nodeinfo);
+       nodename = nodeinfo.nodename;
+    }
+
+    if (lg_lock_drop_exp(gulm_if, nodename, mask, GIO_KEY_SIZE))
+    {
+       DEBUGLOG("Error calling lg_lock_drop_exp()\n");
+    }
+}
+
+
+static int read_from_core_sock(struct local_client *client, char *buf, int len, const char *csid,
+                              struct local_client **new_client)
+{
+    int status;
+
+    *new_client = NULL;
+    status = lg_core_handle_messages(gulm_if, &core_callbacks, NULL);
+    return status<0 ? status : 1;
+}
+
+static int read_from_lock_sock(struct local_client *client, char *buf, int len, const char *csid,
+                              struct local_client **new_client)
+{
+    int status;
+
+    *new_client = NULL;
+    status = lg_lock_handle_messages(gulm_if, &lock_callbacks, NULL);
+    return status<0 ? status : 1;
+}
+
+
+/* CORE callback routines */
+static int core_login_reply(void *misc, uint64_t gen, uint32_t error, uint32_t rank, uint8_t corestate)
+{
+   DEBUGLOG("CORE Got a Login reply.  gen:%lld err:%d rank:%d corestate:%d\n",
+         gen, error, rank, corestate);
+
+   if (error)
+       exit(error);
+
+   /* Get the current core state (for quorum) */
+   lg_core_corestate(gulm_if);
+
+   return 0;
+}
+
+static void set_node_state(struct node_info *ninfo, char *csid, uint8_t nodestate)
+{
+    if (nodestate == lg_core_Logged_in)
+    {
+       /* Don't clobber NODE_CLVMD state */
+       if (ninfo->state != NODE_CLVMD)
+       {
+           if (ninfo->state == NODE_UNKNOWN ||
+               ninfo->state == NODE_DOWN)
+               num_nodes++;
+
+           ninfo->state = NODE_UP;
+       }
+    }
+    else
+    {
+       if (nodestate == lg_core_Expired ||
+           nodestate == lg_core_Fenced ||
+           nodestate == lg_core_Logged_out)
+       {
+           if (ninfo->state != NODE_DOWN)
+               num_nodes--;
+           ninfo->state = NODE_DOWN;
+       }
+    }
+    /* Gulm doesn't always send node DOWN events, so even if this a a node UP we must
+     * assume (ahem) that it prevously went down at some time. So we close
+     * the sockets here to make sure that we don't have any dead connections
+     * to that node.
+     */
+    tcp_remove_client(csid);
+
+    DEBUGLOG("set_node_state, '%s' state = %d num_nodes=%d\n",
+            ninfo->name, ninfo->state, num_nodes);
+}
+
+static struct node_info *add_or_set_node(char *name, struct in6_addr *ip, uint8_t state)
+{
+    struct node_info *ninfo;
+
+    ninfo = dm_hash_lookup_binary(node_hash, (char *)ip, GULM_MAX_CSID_LEN);
+    if (!ninfo)
+    {
+       /* If we can't find that node then re-read the config file in case it
+          was added after we were started */
+       DEBUGLOG("Node %s not found, re-reading config file\n", name);
+       get_all_cluster_nodes();
+
+       /* Now try again */
+       ninfo = dm_hash_lookup_binary(node_hash, (char *)ip, GULM_MAX_CSID_LEN);
+       if (!ninfo)
+       {
+           DEBUGLOG("Ignoring node %s, not part of the SAN cluster\n", name);
+           return NULL;
+       }
+    }
+
+    set_node_state(ninfo, (char *)ip, state);
+
+    return ninfo;
+}
+
+static void _get_our_csid(char *csid)
+{
+       get_our_gulm_csid(csid);
+}
+
+static int core_nodelist(void *misc, lglcb_t type, char *name, struct in6_addr *ip, uint8_t state)
+{
+    DEBUGLOG("CORE nodelist\n");
+
+    if (type == lglcb_start)
+    {
+       DEBUGLOG("Got Nodelist, start\n");
+    }
+    else
+    {
+       if (type == lglcb_item)
+       {
+           DEBUGLOG("Got nodelist, item: %s, %#x\n", name, state);
+
+           add_or_set_node(name, ip, state);
+       }
+       else
+       {
+           if (type == lglcb_stop)
+           {
+               char ourcsid[GULM_MAX_CSID_LEN];
+
+               DEBUGLOG("Got Nodelist, stop\n");
+               if (gulm_quorate)
+               {
+                       clvmd_cluster_init_completed();
+                       init_state = INIT_DONE;
+               }
+               else
+               {
+                       if (init_state == INIT_NOTDONE)
+                               init_state = INIT_WAITQUORATE;
+               }
+
+               /* Mark ourself as up */
+               _get_our_csid(ourcsid);
+               gulm_add_up_node(ourcsid);
+           }
+           else
+           {
+               DEBUGLOG("Unknown lglcb_t %#x\n", type);
+           }
+       }
+    }
+
+    return 0;
+}
+
+static int core_statechange(void *misc, uint8_t corestate, uint8_t quorate, struct in6_addr *masterip, char *mastername)
+{
+    DEBUGLOG("CORE Got statechange. quorate:%d, corestate:%x mastername:%s\n",
+            quorate, corestate, mastername);
+
+    gulm_quorate = quorate;
+    if (quorate && init_state == INIT_WAITQUORATE)
+    {
+           clvmd_cluster_init_completed();
+           init_state = INIT_DONE;
+    }
+    return 0;
+}
+
+static int core_nodechange(void *misc, char *nodename, struct in6_addr *nodeip, uint8_t nodestate)
+{
+    struct node_info *ninfo;
+
+    DEBUGLOG("CORE node change, name=%s, state = %d\n", nodename, nodestate);
+
+    /* If we don't get nodeip here, try a lookup by name */
+    if (!nodeip)
+       _csid_from_name((char *)nodeip, nodename);
+    if (!nodeip)
+       return 0;
+
+    ninfo = add_or_set_node(nodename, nodeip, nodestate);
+    if (!ninfo)
+       return 0;
+
+    /* Check if we need to drop any expired locks */
+    if (ninfo->state == NODE_DOWN)
+    {
+       drop_expired_locks(nodename);
+    }
+
+    return 0;
+}
+static int core_error(void *misc, uint32_t err)
+{
+    DEBUGLOG("CORE error: %d\n", err);
+    // Not sure what happens here
+    return 0;
+}
+
+/* LOCK callback routines */
+static int lock_login_reply(void *misc, uint32_t error, uint8_t which)
+{
+    DEBUGLOG("LOCK Got a Login reply.  err:%d which:%d\n",
+            error, which);
+
+    if (error)
+       exit(error);
+
+    /* Drop any expired locks for us that might be hanging around */
+    drop_expired_locks(NULL);
+
+    /* Enable locking operations in other threads */
+    if (lock_start_flag)
+    {
+       lock_start_flag = 0;
+       pthread_mutex_unlock(&lock_start_mutex);
+    }
+
+    return 0;
+}
+
+static int lock_lock_state(void *misc, uint8_t *key, uint16_t keylen,
+                          uint64_t subid, uint64_t start, uint64_t stop,
+                          uint8_t state, uint32_t flags, uint32_t error,
+                          uint8_t *LVB, uint16_t LVBlen)
+{
+    struct lock_wait *lwait;
+
+    DEBUGLOG("LOCK lock state: %s, error = %d\n", key, error);
+
+    /* No waiting process to wake up when we are shutting down */
+    if (in_shutdown)
+           return 0;
+
+    lwait = dm_hash_lookup(lock_hash, key);
+    if (!lwait)
+    {
+       DEBUGLOG("Can't find hash entry for resource %s\n", key);
+       return 0;
+    }
+    lwait->status = error;
+    pthread_mutex_lock(&lwait->mutex);
+    pthread_cond_signal(&lwait->cond);
+    pthread_mutex_unlock(&lwait->mutex);
+
+    return 0;
+}
+static int lock_error(void *misc, uint32_t err)
+{
+    DEBUGLOG("LOCK error: %d\n", err);
+    // Not sure what happens here
+    return 0;
+}
+
+
+/* CORE callbacks */
+static lg_core_callbacks_t core_callbacks = {
+    .login_reply  = core_login_reply,
+    .nodelist     = core_nodelist,
+    .statechange  = core_statechange,
+    .nodechange   = core_nodechange,
+    .error        = core_error,
+};
+
+/* LOCK callbacks */
+static lg_lockspace_callbacks_t lock_callbacks = {
+    .login_reply   = lock_login_reply,
+    .lock_state    = lock_lock_state,
+    .error         = lock_error,
+};
+
+/* Allow tcp-comms to loop round the list of active nodes */
+int get_next_node_csid(void **context, char *csid)
+{
+    struct node_info *ninfo = NULL;
+
+    /* First node */
+    if (!*context)
+    {
+       *context = dm_hash_get_first(node_hash);
+    }
+    else
+    {
+       *context = dm_hash_get_next(node_hash, *context);
+    }
+    if (*context)
+       ninfo = dm_hash_get_data(node_hash, *context);
+
+    /* Find a node that is UP */
+    while (*context && ninfo->state == NODE_DOWN)
+    {
+       *context = dm_hash_get_next(node_hash, *context);
+       if (*context)
+       {
+           ninfo = dm_hash_get_data(node_hash, *context);
+       }
+    }
+
+    if (!*context || ninfo->state == NODE_DOWN)
+    {
+       return 0;
+    }
+
+    memcpy(csid, dm_hash_get_key(node_hash, *context), GULM_MAX_CSID_LEN);
+    return 1;
+}
+
+int gulm_name_from_csid(const char *csid, char *name)
+{
+    struct node_info *ninfo;
+
+    ninfo = dm_hash_lookup_binary(node_hash, csid, GULM_MAX_CSID_LEN);
+    if (!ninfo)
+    {
+        sprintf(name, "UNKNOWN %s", print_csid(csid));
+       return -1;
+    }
+
+    strcpy(name, ninfo->name);
+    return 0;
+}
+
+
+static int _csid_from_name(char *csid, const char *name)
+{
+    struct dm_hash_node *hn;
+    struct node_info *ninfo;
+
+    dm_hash_iterate(hn, node_hash)
+    {
+       ninfo = dm_hash_get_data(node_hash, hn);
+       if (strcmp(ninfo->name, name) == 0)
+       {
+           memcpy(csid, dm_hash_get_key(node_hash, hn), GULM_MAX_CSID_LEN);
+           return 0;
+       }
+    }
+    return -1;
+}
+
+static int _get_num_nodes()
+{
+    DEBUGLOG("num_nodes = %d\n", num_nodes);
+    return num_nodes;
+}
+
+/* Node is now known to be running a clvmd */
+void gulm_add_up_node(const char *csid)
+{
+    struct node_info *ninfo;
+
+    ninfo = dm_hash_lookup_binary(node_hash, csid, GULM_MAX_CSID_LEN);
+    if (!ninfo) {
+           DEBUGLOG("gulm_add_up_node no node_hash entry for csid %s\n", print_csid(csid));
+       return;
+    }
+
+    DEBUGLOG("gulm_add_up_node %s\n", ninfo->name);
+
+    if (ninfo->state == NODE_DOWN)
+           num_nodes++;
+    ninfo->state = NODE_CLVMD;
+
+    return;
+
+}
+/* Node is now known to be NOT running a clvmd */
+void add_down_node(char *csid)
+{
+    struct node_info *ninfo;
+
+    ninfo = dm_hash_lookup_binary(node_hash, csid, GULM_MAX_CSID_LEN);
+    if (!ninfo)
+       return;
+
+    /* Only set it to UP if it was previously known to be
+       running clvmd - gulm may set it DOWN quite soon */
+    if (ninfo->state == NODE_CLVMD)
+       ninfo->state = NODE_UP;
+    drop_expired_locks(ninfo->name);
+    return;
+
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+                                    void (*callback)(struct local_client *, const char *csid, int node_up))
+{
+    struct dm_hash_node *hn;
+    struct node_info *ninfo;
+    int somedown = 0;
+
+    dm_hash_iterate(hn, node_hash)
+    {
+       char csid[GULM_MAX_CSID_LEN];
+       struct local_client *client;
+
+       ninfo = dm_hash_get_data(node_hash, hn);
+       memcpy(csid, dm_hash_get_key(node_hash, hn), GULM_MAX_CSID_LEN);
+
+       DEBUGLOG("down_callback. node %s, state = %d\n", ninfo->name, ninfo->state);
+
+       client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+       if (!client)
+       {
+           /* If it's up but not connected, try to make contact */
+           if (ninfo->state == NODE_UP)
+                   gulm_connect_csid(csid, &client);
+
+           client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+
+       }
+       DEBUGLOG("down_callback2. node %s, state = %d\n", ninfo->name, ninfo->state);
+       if (ninfo->state != NODE_DOWN)
+               callback(master_client, csid, ninfo->state == NODE_CLVMD);
+
+       if (ninfo->state != NODE_CLVMD)
+               somedown = -1;
+    }
+    return somedown;
+}
+
+/* Convert gulm error codes to unix errno numbers */
+static int gulm_to_errno(int gulm_ret)
+{
+    switch (gulm_ret)
+    {
+    case lg_err_TryFailed:
+    case lg_err_AlreadyPend:
+           errno = EAGAIN;
+           break;
+
+       /* More?? */
+    default:
+           errno = EINVAL;
+    }
+
+    return gulm_ret ? -1 : 0;
+}
+
+/* Real locking */
+static int _lock_resource(char *resource, int mode, int flags, int *lockid)
+{
+    int status;
+    struct lock_wait lwait;
+
+    /* Wait until the lock module is ready */
+    if (lock_start_flag)
+    {
+       pthread_mutex_lock(&lock_start_mutex);
+       pthread_mutex_unlock(&lock_start_mutex);
+    }
+
+    pthread_cond_init(&lwait.cond, NULL);
+    pthread_mutex_init(&lwait.mutex, NULL);
+    pthread_mutex_lock(&lwait.mutex);
+
+    /* This needs to be converted from DLM/LVM2 value for GULM */
+    if (flags & LCKF_NOQUEUE) flags = lg_lock_flag_Try;
+
+    dm_hash_insert(lock_hash, resource, &lwait);
+    DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
+
+    status = lg_lock_state_req(gulm_if, resource, strlen(resource)+1,
+                              0, 0, 0,
+                              mode, flags, NULL, 0);
+    if (status)
+    {
+       DEBUGLOG("lg_lock_state returned %d\n", status);
+       return status;
+    }
+
+    /* Wait for it to complete */
+    pthread_cond_wait(&lwait.cond, &lwait.mutex);
+    pthread_mutex_unlock(&lwait.mutex);
+
+    dm_hash_remove(lock_hash, resource);
+    DEBUGLOG("lock-resource returning %d\n", lwait.status);
+
+    return gulm_to_errno(lwait.status);
+}
+
+
+static int _unlock_resource(char *resource, int lockid)
+{
+    int status;
+    struct lock_wait lwait;
+
+    pthread_cond_init(&lwait.cond, NULL);
+    pthread_mutex_init(&lwait.mutex, NULL);
+    pthread_mutex_lock(&lwait.mutex);
+
+    dm_hash_insert(lock_hash, resource, &lwait);
+
+    DEBUGLOG("unlock_resource %s\n", resource);
+    status = lg_lock_state_req(gulm_if, resource, strlen(resource)+1,
+                              0, 0, 0,
+                              lg_lock_state_Unlock, 0, NULL, 0);
+
+    if (status)
+    {
+       DEBUGLOG("lg_lock_state(unlock) returned %d\n", status);
+       return status;
+    }
+
+    /* When we are shutting down, don't wait for unlocks
+       to be acknowledged, just do it. */
+    if (in_shutdown)
+           return status;
+
+    /* Wait for it to complete */
+
+    pthread_cond_wait(&lwait.cond, &lwait.mutex);
+    pthread_mutex_unlock(&lwait.mutex);
+
+    dm_hash_remove(lock_hash, resource);
+
+    return gulm_to_errno(lwait.status);
+}
+
+
+/* These two locking functions MUST be called in a seperate thread from
+   the clvmd main loop because they expect to be woken up by it.
+
+   These are abstractions around the real locking functions (above)
+   as we need to emulate the DLM's EX/PW/CW interaction with GULM using
+   two locks.
+   To aid unlocking, we store the lock mode in the lockid (as GULM
+   doesn't use this).
+*/
+static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+    int status;
+    char lock1[strlen(resource)+3];
+    char lock2[strlen(resource)+3];
+
+    snprintf(lock1, sizeof(lock1), "%s-1", resource);
+    snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+    switch (mode)
+    {
+    case LCK_EXCL:
+       status = _lock_resource(lock1, lg_lock_state_Exclusive, flags, lockid);
+       if (status)
+           goto out;
+
+       /* If we can't get this lock too then bail out */
+       status = _lock_resource(lock2, lg_lock_state_Exclusive, LCK_NONBLOCK, lockid);
+        if (status == lg_err_TryFailed)
+        {
+           _unlock_resource(lock1, *lockid);
+           status = -1;
+           errno = EAGAIN;
+        }
+       break;
+
+    case LCK_PREAD:
+    case LCK_READ:
+       status = _lock_resource(lock1, lg_lock_state_Shared, flags, lockid);
+       if (status)
+               goto out;
+       status = _unlock_resource(lock2, *lockid);
+       break;
+
+    case LCK_WRITE:
+       status = _lock_resource(lock2, lg_lock_state_Exclusive, flags, lockid);
+       if (status)
+               goto out;
+       status = _unlock_resource(lock1, *lockid);
+       break;
+
+    default:
+       status = -1;
+       errno = EINVAL;
+       break;
+    }
+ out:
+    *lockid = mode;
+    return status;
+}
+
+static int _sync_unlock(const char *resource, int lockid)
+{
+    int status = 0;
+    char lock1[strlen(resource)+3];
+    char lock2[strlen(resource)+3];
+
+    snprintf(lock1, sizeof(lock1), "%s-1", resource);
+    snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+    /* The held lock mode is in the lock id */
+    assert(lockid == LCK_EXCL ||
+          lockid == LCK_READ ||
+          lockid == LCK_PREAD ||
+          lockid == LCK_WRITE);
+
+    status = _unlock_resource(lock1, lockid);
+    if (!status)
+           status = _unlock_resource(lock2, lockid);
+
+    return status;
+}
+
+static int _is_quorate()
+{
+       return gulm_quorate;
+}
+
+/* Get all the cluster node names & IPs from CCS and
+   add them to our node list so we know who to talk to.
+   Called when we start up and if we get sent SIGHUP.
+*/
+static int get_all_cluster_nodes()
+{
+    int ctree;
+    char *nodename;
+    int error;
+    int i;
+
+    /* Open the config file */
+    ctree = ccs_force_connect(NULL, 1);
+    if (ctree < 0)
+    {
+       log_error("Error connecting to CCS");
+       return -1;
+    }
+
+    for (i=1;;i++)
+    {
+       char nodekey[256];
+       char nodeip[GULM_MAX_CSID_LEN];
+       int  clvmflag = 1;
+       char *clvmflagstr;
+       char key[256];
+
+       sprintf(nodekey, "//cluster/clusternodes/clusternode[%d]/@name", i);
+       error = ccs_get(ctree, nodekey, &nodename);
+       if (error)
+           break;
+
+       sprintf(key, "//cluster/clusternodes/clusternode[@name=\"%s\"]/clvm", nodename);
+       if (!ccs_get(ctree, key, &clvmflagstr))
+       {
+           clvmflag = atoi(clvmflagstr);
+           free(clvmflagstr);
+       }
+
+       DEBUGLOG("Got node %s from ccs(clvmflag = %d)\n", nodename, clvmflag);
+       if ((get_ip_address(nodename, nodeip) == 0) && clvmflag)
+       {
+           struct node_info *ninfo;
+
+           /* If it's not in the list, then add it */
+           ninfo = dm_hash_lookup_binary(node_hash, nodeip, GULM_MAX_CSID_LEN);
+           if (!ninfo)
+           {
+               ninfo = malloc(sizeof(struct node_info));
+               if (!ninfo)
+               {
+                   syslog(LOG_ERR, "Cannot alloc memory for node info\n");
+                   ccs_disconnect(ctree);
+                   return -1;
+               }
+               strcpy(ninfo->name, nodename);
+
+               ninfo->state = NODE_DOWN;
+               dm_hash_insert_binary(node_hash, nodeip, GULM_MAX_CSID_LEN, ninfo);
+           }
+       }
+       else
+       {
+               if (!clvmflag) {
+                       DEBUGLOG("node %s has clvm disabled\n", nodename);
+               }
+               else {
+                       DEBUGLOG("Cannot resolve host name %s\n", nodename);
+                       log_error("Cannot resolve host name %s\n", nodename);
+               }
+       }
+       free(nodename);
+    }
+
+    /* Finished with config file */
+    ccs_disconnect(ctree);
+
+    return 0;
+}
+
+static int _get_main_cluster_fd(void)
+{
+       return get_main_gulm_cluster_fd();
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len, const char *csid, struct local_client **new_client)
+{
+       return cluster_fd_gulm_callback(fd, buf, len, csid, new_client);
+}
+
+static int _cluster_send_message(const void *buf, int msglen, const char *csid, const char *errtext)
+{
+       return gulm_cluster_send_message((char *)buf, msglen, csid, errtext);
+}
+
+static int _get_cluster_name(char *buf, int buflen)
+{
+       strncpy(buf, cluster_name, buflen);
+       return 0;
+}
+
+static struct cluster_ops _cluster_gulm_ops = {
+       .cluster_init_completed   = NULL,
+       .cluster_send_message     = _cluster_send_message,
+       .name_from_csid           = gulm_name_from_csid,
+       .csid_from_name           = _csid_from_name,
+       .get_num_nodes            = _get_num_nodes,
+       .cluster_fd_callback      = _cluster_fd_callback,
+       .get_main_cluster_fd      = _get_main_cluster_fd,
+       .cluster_do_node_callback = _cluster_do_node_callback,
+       .is_quorate               = _is_quorate,
+       .get_our_csid             = _get_our_csid,
+       .add_up_node              = gulm_add_up_node,
+       .reread_config            = _reread_config,
+       .cluster_closedown        = _cluster_closedown,
+       .get_cluster_name         = _get_cluster_name,
+       .sync_lock                = _sync_lock,
+       .sync_unlock              = _sync_unlock,
+};
+
+struct cluster_ops *init_gulm_cluster(void)
+{
+       if (!_init_cluster())
+               return &_cluster_gulm_ops;
+       else
+               return NULL;
+}
diff --git a/daemons/clvmd/clvmd-gulm.h b/daemons/clvmd/clvmd-gulm.h
new file mode 100644 (file)
index 0000000..9416f5c
--- /dev/null
@@ -0,0 +1,9 @@
+extern int get_next_node_csid(void **context, char *csid);
+extern void add_down_node(char *csid);
+extern int gulm_fd(void);
+extern int get_ip_address(const char *node, char *addr);
+extern void tcp_remove_client(const char *csid);
+extern int alloc_client(int fd, const char *csid, struct local_client **new_client);
+
+void gulm_add_up_node(const char *csid);
+int gulm_name_from_csid(const char *csid, char *name);
diff --git a/daemons/clvmd/clvmd-openais.c b/daemons/clvmd/clvmd-openais.c
new file mode 100644 (file)
index 0000000..fc98b50
--- /dev/null
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2007-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This provides the interface between clvmd and OpenAIS as the cluster
+ * and lock manager.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#include <openais/saAis.h>
+#include <openais/saLck.h>
+
+#include <corosync/corotypes.h>
+#include <corosync/cpg.h>
+
+#include "locking.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "lvm-functions.h"
+#include "clvmd.h"
+
+/* Timeout value for several openais calls */
+#define TIMEOUT 10
+
+static void openais_cpg_deliver_callback (cpg_handle_t handle,
+                                 const struct cpg_name *groupName,
+                                 uint32_t nodeid,
+                                 uint32_t pid,
+                                 void *msg,
+                                 size_t msg_len);
+static void openais_cpg_confchg_callback(cpg_handle_t handle,
+                                const struct cpg_name *groupName,
+                                const struct cpg_address *member_list, size_t member_list_entries,
+                                const struct cpg_address *left_list, size_t left_list_entries,
+                                const struct cpg_address *joined_list, size_t joined_list_entries);
+
+static void _cluster_closedown(void);
+
+/* Hash list of nodes in the cluster */
+static struct dm_hash_table *node_hash;
+
+/* For associating lock IDs & resource handles */
+static struct dm_hash_table *lock_hash;
+
+/* Number of active nodes */
+static int num_nodes;
+static unsigned int our_nodeid;
+
+static struct local_client *cluster_client;
+
+/* OpenAIS handles */
+static cpg_handle_t cpg_handle;
+static SaLckHandleT lck_handle;
+
+static struct cpg_name cpg_group_name;
+
+/* Openais callback structs */
+cpg_callbacks_t openais_cpg_callbacks = {
+       .cpg_deliver_fn =            openais_cpg_deliver_callback,
+       .cpg_confchg_fn =            openais_cpg_confchg_callback,
+};
+
+struct node_info
+{
+       enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
+       int nodeid;
+};
+
+struct lock_info
+{
+       SaLckResourceHandleT res_handle;
+       SaLckLockIdT         lock_id;
+       SaNameT              lock_name;
+};
+
+/* Set errno to something approximating the right value and return 0 or -1 */
+static int ais_to_errno(SaAisErrorT err)
+{
+       switch(err)
+       {
+       case SA_AIS_OK:
+               return 0;
+        case SA_AIS_ERR_LIBRARY:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_VERSION:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_INIT:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_TIMEOUT:
+               errno = ETIME;
+               break;
+        case SA_AIS_ERR_TRY_AGAIN:
+               errno = EAGAIN;
+               break;
+        case SA_AIS_ERR_INVALID_PARAM:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_NO_MEMORY:
+               errno = ENOMEM;
+               break;
+        case SA_AIS_ERR_BAD_HANDLE:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_BUSY:
+               errno = EBUSY;
+               break;
+        case SA_AIS_ERR_ACCESS:
+               errno = EPERM;
+               break;
+        case SA_AIS_ERR_NOT_EXIST:
+               errno = ENOENT;
+               break;
+        case SA_AIS_ERR_NAME_TOO_LONG:
+               errno = ENAMETOOLONG;
+               break;
+        case SA_AIS_ERR_EXIST:
+               errno = EEXIST;
+               break;
+        case SA_AIS_ERR_NO_SPACE:
+               errno = ENOSPC;
+               break;
+        case SA_AIS_ERR_INTERRUPT:
+               errno = EINTR;
+               break;
+       case SA_AIS_ERR_NAME_NOT_FOUND:
+               errno = ENOENT;
+               break;
+        case SA_AIS_ERR_NO_RESOURCES:
+               errno = ENOMEM;
+               break;
+        case SA_AIS_ERR_NOT_SUPPORTED:
+               errno = EOPNOTSUPP;
+               break;
+        case SA_AIS_ERR_BAD_OPERATION:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_FAILED_OPERATION:
+               errno = EIO;
+               break;
+        case SA_AIS_ERR_MESSAGE_ERROR:
+               errno = EIO;
+               break;
+        case SA_AIS_ERR_QUEUE_FULL:
+               errno = EXFULL;
+               break;
+        case SA_AIS_ERR_QUEUE_NOT_AVAILABLE:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_BAD_FLAGS:
+               errno = EINVAL;
+               break;
+        case SA_AIS_ERR_TOO_BIG:
+               errno = E2BIG;
+               break;
+        case SA_AIS_ERR_NO_SECTIONS:
+               errno = ENOMEM;
+               break;
+       default:
+               errno = EINVAL;
+               break;
+       }
+       return -1;
+}
+
+static char *print_openais_csid(const char *csid)
+{
+       static char buf[128];
+       int id;
+
+       memcpy(&id, csid, sizeof(int));
+       sprintf(buf, "%d", id);
+       return buf;
+}
+
+static int add_internal_client(int fd, fd_callback_t callback)
+{
+       struct local_client *client;
+
+       DEBUGLOG("Add_internal_client, fd = %d\n", fd);
+
+       client = malloc(sizeof(struct local_client));
+       if (!client)
+       {
+               DEBUGLOG("malloc failed\n");
+               return -1;
+       }
+
+       memset(client, 0, sizeof(struct local_client));
+       client->fd = fd;
+       client->type = CLUSTER_INTERNAL;
+       client->callback = callback;
+       add_client(client);
+
+       /* Set Close-on-exec */
+       fcntl(fd, F_SETFD, 1);
+
+       return 0;
+}
+
+static void openais_cpg_deliver_callback (cpg_handle_t handle,
+                                 const struct cpg_name *groupName,
+                                 uint32_t nodeid,
+                                 uint32_t pid,
+                                 void *msg,
+                                 size_t msg_len)
+{
+       int target_nodeid;
+
+       memcpy(&target_nodeid, msg, OPENAIS_CSID_LEN);
+
+       DEBUGLOG("%u got message from nodeid %d for %d. len %d\n",
+                our_nodeid, nodeid, target_nodeid, msg_len-4);
+
+       if (nodeid != our_nodeid)
+               if (target_nodeid == our_nodeid || target_nodeid == 0)
+                       process_message(cluster_client, (char *)msg+OPENAIS_CSID_LEN,
+                                       msg_len-OPENAIS_CSID_LEN, (char*)&nodeid);
+}
+
+static void openais_cpg_confchg_callback(cpg_handle_t handle,
+                                const struct cpg_name *groupName,
+                                const struct cpg_address *member_list, size_t member_list_entries,
+                                const struct cpg_address *left_list, size_t left_list_entries,
+                                const struct cpg_address *joined_list, size_t joined_list_entries)
+{
+       int i;
+       struct node_info *ninfo;
+
+       DEBUGLOG("confchg callback. %d joined, %d left, %d members\n",
+                joined_list_entries, left_list_entries, member_list_entries);
+
+       for (i=0; i<joined_list_entries; i++) {
+               ninfo = dm_hash_lookup_binary(node_hash,
+                                             (char *)&joined_list[i].nodeid,
+                                             OPENAIS_CSID_LEN);
+               if (!ninfo) {
+                       ninfo = malloc(sizeof(struct node_info));
+                       if (!ninfo) {
+                               break;
+                       }
+                       else {
+                               ninfo->nodeid = joined_list[i].nodeid;
+                               dm_hash_insert_binary(node_hash,
+                                                     (char *)&ninfo->nodeid,
+                                                     OPENAIS_CSID_LEN, ninfo);
+                       }
+               }
+               ninfo->state = NODE_CLVMD;
+       }
+
+       for (i=0; i<left_list_entries; i++) {
+               ninfo = dm_hash_lookup_binary(node_hash,
+                                             (char *)&left_list[i].nodeid,
+                                             OPENAIS_CSID_LEN);
+               if (ninfo)
+                       ninfo->state = NODE_DOWN;
+       }
+
+       for (i=0; i<member_list_entries; i++) {
+               if (member_list[i].nodeid == 0) continue;
+               ninfo = dm_hash_lookup_binary(node_hash,
+                               (char *)&member_list[i].nodeid,
+                               OPENAIS_CSID_LEN);
+               if (!ninfo) {
+                       ninfo = malloc(sizeof(struct node_info));
+                       if (!ninfo) {
+                               break;
+                       }
+                       else {
+                               ninfo->nodeid = member_list[i].nodeid;
+                               dm_hash_insert_binary(node_hash,
+                                               (char *)&ninfo->nodeid,
+                                               OPENAIS_CSID_LEN, ninfo);
+                       }
+               }
+               ninfo->state = NODE_CLVMD;
+       }
+
+       num_nodes = member_list_entries;
+}
+
+static int lck_dispatch(struct local_client *client, char *buf, int len,
+                       const char *csid, struct local_client **new_client)
+{
+       *new_client = NULL;
+       saLckDispatch(lck_handle, SA_DISPATCH_ONE);
+       return 1;
+}
+
+static int _init_cluster(void)
+{
+       SaAisErrorT err;
+       SaVersionT  ver = { 'B', 1, 1 };
+       int select_fd;
+
+       node_hash = dm_hash_create(100);
+       lock_hash = dm_hash_create(10);
+
+       err = cpg_initialize(&cpg_handle,
+                            &openais_cpg_callbacks);
+       if (err != SA_AIS_OK) {
+               syslog(LOG_ERR, "Cannot initialise OpenAIS CPG service: %d",
+                      err);
+               DEBUGLOG("Cannot initialise OpenAIS CPG service: %d", err);
+               return ais_to_errno(err);
+       }
+
+       err = saLckInitialize(&lck_handle,
+                                       NULL,
+                             &ver);
+       if (err != SA_AIS_OK) {
+               cpg_initialize(&cpg_handle, &openais_cpg_callbacks);
+               syslog(LOG_ERR, "Cannot initialise OpenAIS lock service: %d",
+                      err);
+               DEBUGLOG("Cannot initialise OpenAIS lock service: %d\n\n", err);
+               return ais_to_errno(err);
+       }
+
+       /* Connect to the clvmd group */
+       strcpy((char *)cpg_group_name.value, "clvmd");
+       cpg_group_name.length = strlen((char *)cpg_group_name.value);
+       err = cpg_join(cpg_handle, &cpg_group_name);
+       if (err != SA_AIS_OK) {
+               cpg_finalize(cpg_handle);
+               saLckFinalize(lck_handle);
+               syslog(LOG_ERR, "Cannot join clvmd process group");
+               DEBUGLOG("Cannot join clvmd process group: %d\n", err);
+               return ais_to_errno(err);
+       }
+
+       err = cpg_local_get(cpg_handle,
+                           &our_nodeid);
+       if (err != SA_AIS_OK) {
+               cpg_finalize(cpg_handle);
+               saLckFinalize(lck_handle);
+               syslog(LOG_ERR, "Cannot get local node id\n");
+               return ais_to_errno(err);
+       }
+       DEBUGLOG("Our local node id is %d\n", our_nodeid);
+
+       saLckSelectionObjectGet(lck_handle, (SaSelectionObjectT *)&select_fd);
+       add_internal_client(select_fd, lck_dispatch);
+
+       DEBUGLOG("Connected to OpenAIS\n");
+
+       return 0;
+}
+
+static void _cluster_closedown(void)
+{
+       DEBUGLOG("cluster_closedown\n");
+       destroy_lvhash();
+
+       saLckFinalize(lck_handle);
+       cpg_finalize(cpg_handle);
+}
+
+static void _get_our_csid(char *csid)
+{
+       memcpy(csid, &our_nodeid, sizeof(int));
+}
+
+/* OpenAIS doesn't really have nmode names so we
+   just use the node ID in hex instead */
+static int _csid_from_name(char *csid, const char *name)
+{
+       int nodeid;
+       struct node_info *ninfo;
+
+       if (sscanf(name, "%x", &nodeid) == 1) {
+               ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
+               if (ninfo)
+                       return nodeid;
+       }
+       return -1;
+}
+
+static int _name_from_csid(const char *csid, char *name)
+{
+       struct node_info *ninfo;
+
+       ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
+       if (!ninfo)
+       {
+               sprintf(name, "UNKNOWN %s", print_openais_csid(csid));
+               return -1;
+       }
+
+       sprintf(name, "%x", ninfo->nodeid);
+       return 0;
+}
+
+static int _get_num_nodes()
+{
+       DEBUGLOG("num_nodes = %d\n", num_nodes);
+       return num_nodes;
+}
+
+/* Node is now known to be running a clvmd */
+static void _add_up_node(const char *csid)
+{
+       struct node_info *ninfo;
+
+       ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
+       if (!ninfo) {
+               DEBUGLOG("openais_add_up_node no node_hash entry for csid %s\n",
+                        print_openais_csid(csid));
+               return;
+       }
+
+       DEBUGLOG("openais_add_up_node %d\n", ninfo->nodeid);
+
+       ninfo->state = NODE_CLVMD;
+
+       return;
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+                                    void (*callback)(struct local_client *,
+                                                     const char *csid, int node_up))
+{
+       struct dm_hash_node *hn;
+       struct node_info *ninfo;
+       int somedown = 0;
+
+       dm_hash_iterate(hn, node_hash)
+       {
+               char csid[OPENAIS_CSID_LEN];
+
+               ninfo = dm_hash_get_data(node_hash, hn);
+               memcpy(csid, dm_hash_get_key(node_hash, hn), OPENAIS_CSID_LEN);
+
+               DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
+                        ninfo->state);
+
+               if (ninfo->state != NODE_DOWN)
+                       callback(master_client, csid, ninfo->state == NODE_CLVMD);
+               if (ninfo->state != NODE_CLVMD)
+                       somedown = -1;
+       }
+       return somedown;
+}
+
+/* Real locking */
+static int _lock_resource(char *resource, int mode, int flags, int *lockid)
+{
+       struct lock_info *linfo;
+       SaLckResourceHandleT res_handle;
+       SaAisErrorT err;
+       SaLckLockIdT lock_id;
+       SaLckLockStatusT lockStatus;
+
+       /* This needs to be converted from DLM/LVM2 value for OpenAIS LCK */
+       if (flags & LCK_NONBLOCK) flags = SA_LCK_LOCK_NO_QUEUE;
+
+       linfo = malloc(sizeof(struct lock_info));
+       if (!linfo)
+               return -1;
+
+       DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
+
+       linfo->lock_name.length = strlen(resource)+1;
+       strcpy((char *)linfo->lock_name.value, resource);
+
+       err = saLckResourceOpen(lck_handle, &linfo->lock_name,
+                               SA_LCK_RESOURCE_CREATE, TIMEOUT, &res_handle);
+       if (err != SA_AIS_OK)
+       {
+               DEBUGLOG("ResourceOpen returned %d\n", err);
+               free(linfo);
+               return ais_to_errno(err);
+       }
+
+       err = saLckResourceLock(
+                       res_handle,
+                       &lock_id,
+                       mode,
+                       flags,
+                       0,
+                       SA_TIME_END,
+                       &lockStatus);
+       if (err != SA_AIS_OK && lockStatus != SA_LCK_LOCK_GRANTED)
+       {
+               free(linfo);
+               saLckResourceClose(res_handle);
+               return ais_to_errno(err);
+       }
+                       
+       /* Wait for it to complete */
+
+       DEBUGLOG("lock_resource returning %d, lock_id=%llx\n", err,
+                lock_id);
+
+       linfo->lock_id = lock_id;
+       linfo->res_handle = res_handle;
+
+       dm_hash_insert(lock_hash, resource, linfo);
+
+       return ais_to_errno(err);
+}
+
+
+static int _unlock_resource(char *resource, int lockid)
+{
+       SaAisErrorT err;
+       struct lock_info *linfo;
+
+       DEBUGLOG("unlock_resource %s\n", resource);
+       linfo = dm_hash_lookup(lock_hash, resource);
+       if (!linfo)
+               return 0;
+
+       DEBUGLOG("unlock_resource: lockid: %llx\n", linfo->lock_id);
+       err = saLckResourceUnlock(linfo->lock_id, SA_TIME_END);
+       if (err != SA_AIS_OK)
+       {
+               DEBUGLOG("Unlock returned %d\n", err);
+               return ais_to_errno(err);
+       }
+
+       /* Release the resource */
+       dm_hash_remove(lock_hash, resource);
+       saLckResourceClose(linfo->res_handle);
+       free(linfo);
+
+       return ais_to_errno(err);
+}
+
+static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+       int status;
+       char lock1[strlen(resource)+3];
+       char lock2[strlen(resource)+3];
+
+       snprintf(lock1, sizeof(lock1), "%s-1", resource);
+       snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+       switch (mode)
+       {
+       case LCK_EXCL:
+               status = _lock_resource(lock1, SA_LCK_EX_LOCK_MODE, flags, lockid);
+               if (status)
+                       goto out;
+
+               /* If we can't get this lock too then bail out */
+               status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, LCK_NONBLOCK,
+                                       lockid);
+               if (status == SA_LCK_LOCK_NOT_QUEUED)
+               {
+                       _unlock_resource(lock1, *lockid);
+                       status = -1;
+                       errno = EAGAIN;
+               }
+               break;
+
+       case LCK_PREAD:
+       case LCK_READ:
+               status = _lock_resource(lock1, SA_LCK_PR_LOCK_MODE, flags, lockid);
+               if (status)
+                       goto out;
+               _unlock_resource(lock2, *lockid);
+               break;
+
+       case LCK_WRITE:
+               status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, flags, lockid);
+               if (status)
+                       goto out;
+               _unlock_resource(lock1, *lockid);
+               break;
+
+       default:
+               status = -1;
+               errno = EINVAL;
+               break;
+       }
+out:
+       *lockid = mode;
+       return status;
+}
+
+static int _sync_unlock(const char *resource, int lockid)
+{
+       int status = 0;
+       char lock1[strlen(resource)+3];
+       char lock2[strlen(resource)+3];
+
+       snprintf(lock1, sizeof(lock1), "%s-1", resource);
+       snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+       _unlock_resource(lock1, lockid);
+       _unlock_resource(lock2, lockid);
+
+       return status;
+}
+
+/* We are always quorate ! */
+static int _is_quorate()
+{
+       return 1;
+}
+
+static int _get_main_cluster_fd(void)
+{
+       int select_fd;
+
+       cpg_fd_get(cpg_handle, &select_fd);
+       return select_fd;
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+                               const char *csid,
+                               struct local_client **new_client)
+{
+       cluster_client = fd;
+       *new_client = NULL;
+       cpg_dispatch(cpg_handle, SA_DISPATCH_ONE);
+       return 1;
+}
+
+static int _cluster_send_message(const void *buf, int msglen, const char *csid,
+                                const char *errtext)
+{
+       struct iovec iov[2];
+       SaAisErrorT err;
+       int target_node;
+
+       if (csid)
+               memcpy(&target_node, csid, OPENAIS_CSID_LEN);
+       else
+               target_node = 0;
+
+       iov[0].iov_base = &target_node;
+       iov[0].iov_len = sizeof(int);
+       iov[1].iov_base = (char *)buf;
+       iov[1].iov_len = msglen;
+
+       err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
+       return ais_to_errno(err);
+}
+
+/* We don't have a cluster name to report here */
+static int _get_cluster_name(char *buf, int buflen)
+{
+       strncpy(buf, "OpenAIS", buflen);
+       return 0;
+}
+
+static struct cluster_ops _cluster_openais_ops = {
+       .cluster_init_completed   = NULL,
+       .cluster_send_message     = _cluster_send_message,
+       .name_from_csid           = _name_from_csid,
+       .csid_from_name           = _csid_from_name,
+       .get_num_nodes            = _get_num_nodes,
+       .cluster_fd_callback      = _cluster_fd_callback,
+       .get_main_cluster_fd      = _get_main_cluster_fd,
+       .cluster_do_node_callback = _cluster_do_node_callback,
+       .is_quorate               = _is_quorate,
+       .get_our_csid             = _get_our_csid,
+       .add_up_node              = _add_up_node,
+       .reread_config            = NULL,
+       .cluster_closedown        = _cluster_closedown,
+       .get_cluster_name         = _get_cluster_name,
+       .sync_lock                = _sync_lock,
+       .sync_unlock              = _sync_unlock,
+};
+
+struct cluster_ops *init_openais_cluster(void)
+{
+       if (!_init_cluster())
+               return &_cluster_openais_ops;
+       else
+               return NULL;
+}
diff --git a/daemons/clvmd/clvmd-singlenode.c b/daemons/clvmd/clvmd-singlenode.c
new file mode 100644 (file)
index 0000000..335cb98
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "locking.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "lvm-functions.h"
+#include "clvmd.h"
+
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+static const char SINGLENODE_CLVMD_SOCKNAME[] = DEFAULT_RUN_DIR "/clvmd_singlenode.sock";
+static int listen_fd = -1;
+
+static void close_comms(void)
+{
+       if (listen_fd != -1 && close(listen_fd))
+               stack;
+       (void)unlink(SINGLENODE_CLVMD_SOCKNAME);
+       listen_fd = -1;
+}
+
+static int init_comms(void)
+{
+       struct sockaddr_un addr;
+       mode_t old_mask;
+
+       close_comms();
+
+       (void) dm_prepare_selinux_context(SINGLENODE_CLVMD_SOCKNAME, S_IFSOCK);
+       old_mask = umask(0077);
+
+       listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (listen_fd < 0) {
+               DEBUGLOG("Can't create local socket: %s\n", strerror(errno));
+               goto error;
+       }
+       /* Set Close-on-exec */
+       fcntl(listen_fd, F_SETFD, 1);
+
+       memset(&addr, 0, sizeof(addr));
+       memcpy(addr.sun_path, SINGLENODE_CLVMD_SOCKNAME,
+              sizeof(SINGLENODE_CLVMD_SOCKNAME));
+       addr.sun_family = AF_UNIX;
+
+       if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               DEBUGLOG("Can't bind local socket: %s\n", strerror(errno));
+               goto error;
+       }
+       if (listen(listen_fd, 10) < 0) {
+               DEBUGLOG("Can't listen local socket: %s\n", strerror(errno));
+               goto error;
+       }
+
+       umask(old_mask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+       return 0;
+error:
+       umask(old_mask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+       close_comms();
+       return -1;
+}
+
+static int _init_cluster(void)
+{
+       int r;
+
+       r = init_comms();
+       if (r)
+               return r;
+
+       DEBUGLOG("Single-node cluster initialised.\n");
+       return 0;
+}
+
+static void _cluster_closedown(void)
+{
+       close_comms();
+
+       DEBUGLOG("cluster_closedown\n");
+       destroy_lvhash();
+}
+
+static void _get_our_csid(char *csid)
+{
+       int nodeid = 1;
+       memcpy(csid, &nodeid, sizeof(int));
+}
+
+static int _csid_from_name(char *csid, const char *name)
+{
+       return 1;
+}
+
+static int _name_from_csid(const char *csid, char *name)
+{
+       sprintf(name, "SINGLENODE");
+       return 0;
+}
+
+static int _get_num_nodes(void)
+{
+       return 1;
+}
+
+/* Node is now known to be running a clvmd */
+static void _add_up_node(const char *csid)
+{
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+                                    void (*callback)(struct local_client *,
+                                    const char *csid, int node_up))
+{
+       return 0;
+}
+
+int _lock_file(const char *file, uint32_t flags);
+
+static int *_locks = NULL;
+static char **_resources = NULL;
+static int _lock_max = 1;
+static pthread_mutex_t _lock_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Real locking */
+static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
+{
+       int *_locks_1;
+       char **_resources_1;
+       int i, j;
+
+       DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n",
+                resource, flags, mode);
+
+ retry:
+       pthread_mutex_lock(&_lock_mutex);
+
+       /* look for an existing lock for this resource */
+       for (i = 1; i < _lock_max; ++i) {
+               if (!_resources[i])
+                       break;
+               if (!strcmp(_resources[i], resource)) {
+                       if ((_locks[i] & LCK_TYPE_MASK) == LCK_WRITE ||
+                           (_locks[i] & LCK_TYPE_MASK) == LCK_EXCL) {
+                               DEBUGLOG("%s already write/exclusively locked...\n", resource);
+                               goto maybe_retry;
+                       }
+                       if ((mode & LCK_TYPE_MASK) == LCK_WRITE ||
+                           (mode & LCK_TYPE_MASK) == LCK_EXCL) {
+                               DEBUGLOG("%s already locked and WRITE/EXCL lock requested...\n",
+                                        resource);
+                               goto maybe_retry;
+                       }
+               }
+       }
+
+       if (i == _lock_max) { /* out of lock slots, extend */
+               _locks_1 = dm_realloc(_locks, 2 * _lock_max * sizeof(int));
+               if (!_locks_1)
+                       return 1; /* fail */
+               _locks = _locks_1;
+               _resources_1 = dm_realloc(_resources, 2 * _lock_max * sizeof(char *));
+               if (!_resources_1) {
+                       /* _locks may get realloc'd twice, but that should be safe */
+                       return 1; /* fail */
+               }
+               _resources = _resources_1;
+               /* clear the new resource entries */
+               for (j = _lock_max; j < 2 * _lock_max; ++j)
+                       _resources[j] = NULL;
+               _lock_max = 2 * _lock_max;
+       }
+
+       /* resource is not currently locked, grab it */
+
+       *lockid = i;
+       _locks[i] = mode;
+       _resources[i] = dm_strdup(resource);
+
+       DEBUGLOG("%s locked -> %d\n", resource, i);
+
+       pthread_mutex_unlock(&_lock_mutex);
+       return 0;
+ maybe_retry:
+       pthread_mutex_unlock(&_lock_mutex);
+       if (!(flags & LCK_NONBLOCK)) {
+               usleep(10000);
+               goto retry;
+       }
+
+       return 1; /* fail */
+}
+
+static int _unlock_resource(const char *resource, int lockid)
+{
+       DEBUGLOG("unlock_resource: %s lockid: %x\n", resource, lockid);
+       if(!_resources[lockid]) {
+               DEBUGLOG("(%s) %d not locked\n", resource, lockid);
+               return 1;
+       }
+       if(strcmp(_resources[lockid], resource)) {
+               DEBUGLOG("%d has wrong resource (requested %s, got %s)\n",
+                        lockid, resource, _resources[lockid]);
+               return 1;
+       }
+
+       dm_free(_resources[lockid]);
+       _resources[lockid] = 0;
+       return 0;
+}
+
+static int _is_quorate(void)
+{
+       return 1;
+}
+
+static int _get_main_cluster_fd(void)
+{
+       return listen_fd;
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+                               const char *csid,
+                               struct local_client **new_client)
+{
+       return 1;
+}
+
+static int _cluster_send_message(const void *buf, int msglen,
+                                const char *csid,
+                                const char *errtext)
+{
+       return 0;
+}
+
+static int _get_cluster_name(char *buf, int buflen)
+{
+       strncpy(buf, "localcluster", buflen);
+       buf[buflen - 1] = 0;
+       return 0;
+}
+
+static struct cluster_ops _cluster_singlenode_ops = {
+       .cluster_init_completed   = NULL,
+       .cluster_send_message     = _cluster_send_message,
+       .name_from_csid           = _name_from_csid,
+       .csid_from_name           = _csid_from_name,
+       .get_num_nodes            = _get_num_nodes,
+       .cluster_fd_callback      = _cluster_fd_callback,
+       .get_main_cluster_fd      = _get_main_cluster_fd,
+       .cluster_do_node_callback = _cluster_do_node_callback,
+       .is_quorate               = _is_quorate,
+       .get_our_csid             = _get_our_csid,
+       .add_up_node              = _add_up_node,
+       .reread_config            = NULL,
+       .cluster_closedown        = _cluster_closedown,
+       .get_cluster_name         = _get_cluster_name,
+       .sync_lock                = _lock_resource,
+       .sync_unlock              = _unlock_resource,
+};
+
+struct cluster_ops *init_singlenode_cluster(void)
+{
+       if (!_init_cluster())
+               return &_cluster_singlenode_ops;
+       else
+               return NULL;
+}
diff --git a/daemons/clvmd/clvmd.c b/daemons/clvmd/clvmd.c
new file mode 100644 (file)
index 0000000..1a35c49
--- /dev/null
@@ -0,0 +1,2239 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/*
+ * CLVMD: Cluster LVM daemon
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvmd-comms.h"
+#include "clvm.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+#include "lvm-version.h"
+#include "refresh_clvmd.h"
+
+#ifdef HAVE_COROSYNC_CONFDB_H
+#include <corosync/confdb.h>
+#endif
+
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stddef.h>
+#include <syslog.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define MAX_RETRIES 4
+
+#define ISLOCAL_CSID(c) (memcmp(c, our_csid, max_csid_len) == 0)
+
+/* Head of the fd list. Also contains
+   the cluster_socket details */
+static struct local_client local_client_head;
+
+static unsigned short global_xid = 0;  /* Last transaction ID issued */
+
+struct cluster_ops *clops = NULL;
+
+static char our_csid[MAX_CSID_LEN];
+static unsigned max_csid_len;
+static unsigned max_cluster_message;
+static unsigned max_cluster_member_name_len;
+
+/* Structure of items on the LVM thread list */
+struct lvm_thread_cmd {
+       struct dm_list list;
+
+       struct local_client *client;
+       struct clvm_header *msg;
+       char csid[MAX_CSID_LEN];
+       int remote;             /* Flag */
+       int msglen;
+       unsigned short xid;
+};
+
+struct lvm_startup_params {
+       int using_gulm;
+       char **argv;
+};
+
+debug_t debug;
+static pthread_t lvm_thread;
+static pthread_mutex_t lvm_thread_mutex;
+static pthread_cond_t lvm_thread_cond;
+static pthread_mutex_t lvm_start_mutex;
+static struct dm_list lvm_cmd_head;
+static volatile sig_atomic_t quit = 0;
+static volatile sig_atomic_t reread_config = 0;
+static int child_pipe[2];
+
+/* Reasons the daemon failed initialisation */
+#define DFAIL_INIT       1
+#define DFAIL_LOCAL_SOCK 2
+#define DFAIL_CLUSTER_IF 3
+#define DFAIL_MALLOC     4
+#define DFAIL_TIMEOUT    5
+#define SUCCESS          0
+
+typedef enum {IF_AUTO, IF_CMAN, IF_GULM, IF_OPENAIS, IF_COROSYNC, IF_SINGLENODE} if_type_t;
+
+typedef void *(lvm_pthread_fn_t)(void*);
+
+/* Prototypes for code further down */
+static void sigusr2_handler(int sig);
+static void sighup_handler(int sig);
+static void sigterm_handler(int sig);
+static void send_local_reply(struct local_client *client, int status,
+                            int clientid);
+static void free_reply(struct local_client *client);
+static void send_version_message(void);
+static void *pre_and_post_thread(void *arg);
+static int send_message(void *buf, int msglen, const char *csid, int fd,
+                       const char *errtext);
+static int read_from_local_sock(struct local_client *thisfd);
+static int process_local_command(struct clvm_header *msg, int msglen,
+                                struct local_client *client,
+                                unsigned short xid);
+static void process_remote_command(struct clvm_header *msg, int msglen, int fd,
+                                  const char *csid);
+static int process_reply(const struct clvm_header *msg, int msglen,
+                        const char *csid);
+static int open_local_sock(void);
+static void close_local_sock(int local_socket);
+static int check_local_clvmd(void);
+static struct local_client *find_client(int clientid);
+static void main_loop(int local_sock, int cmd_timeout);
+static void be_daemon(int start_timeout);
+static int check_all_clvmds_running(struct local_client *client);
+static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
+                                    int len, const char *csid,
+                                    struct local_client **new_client);
+static void lvm_thread_fn(void *) __attribute__ ((noreturn));
+static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
+                          int msglen, const char *csid);
+static int distribute_command(struct local_client *thisfd);
+static void hton_clvm(struct clvm_header *hdr);
+static void ntoh_clvm(struct clvm_header *hdr);
+static void add_reply_to_list(struct local_client *client, int status,
+                             const char *csid, const char *buf, int len);
+static if_type_t parse_cluster_interface(char *ifname);
+static if_type_t get_cluster_type(void);
+
+static void usage(const char *prog, FILE *file)
+{
+       fprintf(file, "Usage:\n"
+               "%s [Vhd]\n\n"
+               "   -V       Show version of clvmd\n"
+               "   -h       Show this help information\n"
+               "   -d       Set debug level\n"
+               "            If starting clvmd then don't fork, run in the foreground\n"
+               "   -R       Tell all running clvmds in the cluster to reload their device cache\n"
+               "   -S       Restart clvmd, preserving exclusive locks\n"
+               "   -C       Sets debug level (from -d) on all clvmd instances clusterwide\n"
+               "   -t<secs> Command timeout (default 60 seconds)\n"
+               "   -T<secs> Startup timeout (default none)\n"
+               "   -I<cmgr> Cluster manager (default: auto)\n"
+               "            Available cluster managers: "
+#ifdef USE_COROSYNC
+               "corosync "
+#endif
+#ifdef USE_CMAN
+               "cman "
+#endif
+#ifdef USE_OPENAIS
+               "openais "
+#endif
+#ifdef USE_GULM
+               "gulm "
+#endif
+#ifdef USE_SINGLENODE
+               "singlenode "
+#endif
+               "\n", prog);
+}
+
+/* Called to signal the parent how well we got on during initialisation */
+static void child_init_signal(int status)
+{
+       if (child_pipe[1]) {
+               /* FIXME Use a proper wrapper around write */
+               if (write(child_pipe[1], &status, sizeof(status)) < 0)
+                       log_sys_error("write", "child_pipe");
+               if (close(child_pipe[1]))
+                       log_sys_error("close", "child_pipe");
+       }
+}
+
+static __attribute__((noreturn)) void child_init_signal_and_exit(int status) 
+{
+       child_init_signal(status);
+       exit(status);
+}
+
+static void safe_close(int *fd)
+{
+       if (*fd >= 0) {
+               int to_close = *fd;
+               *fd = -1;
+               close(to_close);
+       }
+}
+
+void debuglog(const char *fmt, ...)
+{
+       time_t P;
+       va_list ap;
+       static int syslog_init = 0;
+
+       if (debug == DEBUG_STDERR) {
+               va_start(ap,fmt);
+               time(&P);
+               fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime(&P)+4 );
+               vfprintf(stderr, fmt, ap);
+               va_end(ap);
+       }
+       if (debug == DEBUG_SYSLOG) {
+               if (!syslog_init) {
+                       openlog("clvmd", LOG_PID, LOG_DAEMON);
+                       syslog_init = 1;
+               }
+
+               va_start(ap,fmt);
+               vsyslog(LOG_DEBUG, fmt, ap);
+               va_end(ap);
+       }
+}
+
+static const char *decode_cmd(unsigned char cmdl)
+{
+       static char buf[128];
+       const char *command;
+
+       switch (cmdl) {
+       case CLVMD_CMD_TEST:
+               command = "TEST";
+               break;
+       case CLVMD_CMD_LOCK_VG:
+               command = "LOCK_VG";
+               break;
+       case CLVMD_CMD_LOCK_LV:
+               command = "LOCK_LV";
+               break;
+       case CLVMD_CMD_REFRESH:
+               command = "REFRESH";
+               break;
+       case CLVMD_CMD_SET_DEBUG:
+               command = "SET_DEBUG";
+               break;
+       case CLVMD_CMD_GET_CLUSTERNAME:
+               command = "GET_CLUSTERNAME";
+               break;
+       case CLVMD_CMD_VG_BACKUP:
+               command = "VG_BACKUP";
+               break;
+       case CLVMD_CMD_REPLY:
+               command = "REPLY";
+               break;
+       case CLVMD_CMD_VERSION:
+               command = "VERSION";
+               break;
+       case CLVMD_CMD_GOAWAY:
+               command = "GOAWAY";
+               break;
+       case CLVMD_CMD_LOCK:
+               command = "LOCK";
+               break;
+       case CLVMD_CMD_UNLOCK:
+               command = "UNLOCK";
+               break;
+       case CLVMD_CMD_LOCK_QUERY:
+               command = "LOCK_QUERY";
+               break;
+       case CLVMD_CMD_RESTART:
+               command = "RESTART";
+               break;
+       default:
+               command = "unknown";
+               break;
+       }
+
+       sprintf(buf, "%s (0x%x)", command, cmdl);
+
+       return buf;
+}
+
+static void remove_lockfile(void)
+{
+       if (unlink(CLVMD_PIDFILE))
+               log_sys_error("unlink", CLVMD_PIDFILE);
+}
+
+/*
+ * clvmd require dm-ioctl capability for operation
+ */
+static void check_permissions(void)
+{
+       if (getuid() || geteuid()) {
+               log_error("Cannot run as a non-root user.");
+
+                /*
+                 * Fail cleanly here if not run as root, instead of failing
+                 * later when attempting a root-only operation 
+                 * Preferred exit code from an initscript for this.
+                 */
+               exit(4);
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       int local_sock;
+       struct local_client *newfd;
+       struct utsname nodeinfo;
+       struct lvm_startup_params lvm_params;
+       signed char opt;
+       int cmd_timeout = DEFAULT_CMD_TIMEOUT;
+       int start_timeout = 0;
+       if_type_t cluster_iface = IF_AUTO;
+       sigset_t ss;
+       int using_gulm = 0;
+       int debug_opt = 0;
+       int clusterwide_opt = 0;
+       mode_t old_mask;
+
+       /* Deal with command-line arguments */
+       opterr = 0;
+       optind = 0;
+       while ((opt = getopt(argc, argv, "?vVhd::t:RST:CI:E:")) != EOF) {
+               switch (opt) {
+               case 'h':
+                       usage(argv[0], stdout);
+                       exit(0);
+
+               case '?':
+                       usage(argv[0], stderr);
+                       exit(0);
+
+               case 'R':
+                       check_permissions();
+                       return refresh_clvmd(1)==1?0:1;
+
+               case 'S':
+                       check_permissions();
+                       return restart_clvmd(clusterwide_opt)==1?0:1;
+
+               case 'C':
+                       clusterwide_opt = 1;
+                       break;
+
+               case 'd':
+                       debug_opt = 1;
+                       if (optarg)
+                               debug = atoi(optarg);
+                       else
+                               debug = DEBUG_STDERR;
+                       break;
+
+               case 't':
+                       cmd_timeout = atoi(optarg);
+                       if (!cmd_timeout) {
+                               fprintf(stderr, "command timeout is invalid\n");
+                               usage(argv[0], stderr);
+                               exit(1);
+                       }
+                       break;
+               case 'I':
+                       cluster_iface = parse_cluster_interface(optarg);
+                       break;
+               case 'T':
+                       start_timeout = atoi(optarg);
+                       if (start_timeout <= 0) {
+                               fprintf(stderr, "startup timeout is invalid\n");
+                               usage(argv[0], stderr);
+                               exit(1);
+                       }
+                       break;
+
+               case 'V':
+                       printf("Cluster LVM daemon version: %s\n", LVM_VERSION);
+                       printf("Protocol version:           %d.%d.%d\n",
+                              CLVMD_MAJOR_VERSION, CLVMD_MINOR_VERSION,
+                              CLVMD_PATCH_VERSION);
+                       exit(0);
+                       break;
+
+               }
+       }
+
+       check_permissions();
+
+       /* Setting debug options on an existing clvmd */
+       if (debug_opt && !check_local_clvmd()) {
+
+               /* Sending to stderr makes no sense for a detached daemon */
+               if (debug == DEBUG_STDERR)
+                       debug = DEBUG_SYSLOG;
+               return debug_clvmd(debug, clusterwide_opt)==1?0:1;
+       }
+
+       /*
+        * Switch to C locale to avoid reading large locale-archive file
+        * used by some glibc (on some distributions it takes over 100MB).
+        * Daemon currently needs to use mlockall().
+        */
+       if (setenv("LANG", "C", 1))
+               perror("Cannot set LANG to C");
+
+       /* Fork into the background (unless requested not to) */
+       if (debug != DEBUG_STDERR) {
+               be_daemon(start_timeout);
+       }
+
+        dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
+        old_mask = umask(0077);
+        if (dm_create_dir(DEFAULT_RUN_DIR) == 0) {
+                DEBUGLOG("clvmd: unable to create %s directory\n",
+                          DEFAULT_RUN_DIR);
+                umask(old_mask);
+                exit(1);
+        }
+        umask(old_mask);
+
+       /* Create pidfile */
+       (void) dm_prepare_selinux_context(CLVMD_PIDFILE, S_IFREG);
+       if (dm_create_lockfile(CLVMD_PIDFILE) == 0) {
+               DEBUGLOG("clvmd: unable to create lockfile\n");
+               exit(1);
+       }
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       atexit(remove_lockfile);
+
+       DEBUGLOG("CLVMD started\n");
+
+       /* Open the Unix socket we listen for commands on.
+          We do this before opening the cluster socket so that
+          potential clients will block rather than error if we are running
+          but the cluster is not ready yet */
+       local_sock = open_local_sock();
+       if (local_sock < 0) {
+               child_init_signal_and_exit(DFAIL_LOCAL_SOCK);
+               /* NOTREACHED */
+       }
+
+       /* Set up signal handlers, USR1 is for cluster change notifications (in cman)
+          USR2 causes child threads to exit.
+          HUP causes gulm version to re-read nodes list from CCS.
+          PIPE should be ignored */
+       signal(SIGUSR2, sigusr2_handler);
+       signal(SIGHUP,  sighup_handler);
+       signal(SIGPIPE, SIG_IGN);
+
+       /* Block SIGUSR2/SIGINT/SIGTERM in process */
+       sigemptyset(&ss);
+       sigaddset(&ss, SIGUSR2);
+       sigaddset(&ss, SIGINT);
+       sigaddset(&ss, SIGTERM);
+       sigprocmask(SIG_BLOCK, &ss, NULL);
+
+       /* Initialise the LVM thread variables */
+       dm_list_init(&lvm_cmd_head);
+       pthread_mutex_init(&lvm_thread_mutex, NULL);
+       pthread_cond_init(&lvm_thread_cond, NULL);
+       pthread_mutex_init(&lvm_start_mutex, NULL);
+       init_lvhash();
+
+       /* Start the cluster interface */
+       if (cluster_iface == IF_AUTO)
+               cluster_iface = get_cluster_type();
+
+#ifdef USE_CMAN
+       if ((cluster_iface == IF_AUTO || cluster_iface == IF_CMAN) && (clops = init_cman_cluster())) {
+               max_csid_len = CMAN_MAX_CSID_LEN;
+               max_cluster_message = CMAN_MAX_CLUSTER_MESSAGE;
+               max_cluster_member_name_len = CMAN_MAX_NODENAME_LEN;
+               syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to CMAN");
+       }
+#endif
+#ifdef USE_GULM
+       if (!clops)
+               if ((cluster_iface == IF_AUTO || cluster_iface == IF_GULM) && (clops = init_gulm_cluster())) {
+                       max_csid_len = GULM_MAX_CSID_LEN;
+                       max_cluster_message = GULM_MAX_CLUSTER_MESSAGE;
+                       max_cluster_member_name_len = GULM_MAX_CLUSTER_MEMBER_NAME_LEN;
+                       using_gulm = 1;
+                       syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to GULM");
+               }
+#endif
+#ifdef USE_COROSYNC
+       if (!clops)
+               if (((cluster_iface == IF_AUTO || cluster_iface == IF_COROSYNC) && (clops = init_corosync_cluster()))) {
+                       max_csid_len = COROSYNC_CSID_LEN;
+                       max_cluster_message = COROSYNC_MAX_CLUSTER_MESSAGE;
+                       max_cluster_member_name_len = COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN;
+                       syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to Corosync");
+               }
+#endif
+#ifdef USE_OPENAIS
+       if (!clops)
+               if ((cluster_iface == IF_AUTO || cluster_iface == IF_OPENAIS) && (clops = init_openais_cluster())) {
+                       max_csid_len = OPENAIS_CSID_LEN;
+                       max_cluster_message = OPENAIS_MAX_CLUSTER_MESSAGE;
+                       max_cluster_member_name_len = OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN;
+                       syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to OpenAIS");
+               }
+#endif
+#ifdef USE_SINGLENODE
+       if (!clops)
+               if (cluster_iface == IF_SINGLENODE && (clops = init_singlenode_cluster())) {
+                       max_csid_len = SINGLENODE_CSID_LEN;
+                       max_cluster_message = SINGLENODE_MAX_CLUSTER_MESSAGE;
+                       max_cluster_member_name_len = MAX_CLUSTER_MEMBER_NAME_LEN;
+                       syslog(LOG_NOTICE, "Cluster LVM daemon started - running in single-node mode");
+               }
+#endif
+
+       if (!clops) {
+               DEBUGLOG("Can't initialise cluster interface\n");
+               log_error("Can't initialise cluster interface\n");
+               child_init_signal_and_exit(DFAIL_CLUSTER_IF);
+               /* NOTREACHED */
+       }
+       DEBUGLOG("Cluster ready, doing some more initialisation\n");
+
+       /* Save our CSID */
+       uname(&nodeinfo);
+       clops->get_our_csid(our_csid);
+
+       /* Initialise the FD list head */
+       local_client_head.fd = clops->get_main_cluster_fd();
+       local_client_head.type = CLUSTER_MAIN_SOCK;
+       local_client_head.callback = clops->cluster_fd_callback;
+
+       /* Add the local socket to the list */
+       newfd = malloc(sizeof(struct local_client));
+       if (!newfd) {
+               child_init_signal_and_exit(DFAIL_MALLOC);
+               /* NOTREACHED */
+       }
+
+       newfd->fd = local_sock;
+       newfd->removeme = 0;
+       newfd->type = LOCAL_RENDEZVOUS;
+       newfd->callback = local_rendezvous_callback;
+       newfd->next = local_client_head.next;
+       local_client_head.next = newfd;
+
+       /* This needs to be started after cluster initialisation
+          as it may need to take out locks */
+       DEBUGLOG("starting LVM thread\n");
+
+       /* Don't let anyone else to do work until we are started */
+       pthread_mutex_lock(&lvm_start_mutex);
+       lvm_params.using_gulm = using_gulm;
+       lvm_params.argv = argv;
+       pthread_create(&lvm_thread, NULL, (lvm_pthread_fn_t*)lvm_thread_fn,
+                       (void *)&lvm_params);
+
+       /* Tell the rest of the cluster our version number */
+       /* CMAN can do this immediately, gulm needs to wait until
+          the core initialisation has finished and the node list
+          has been gathered */
+       if (clops->cluster_init_completed)
+               clops->cluster_init_completed();
+
+       DEBUGLOG("clvmd ready for work\n");
+       child_init_signal(SUCCESS);
+
+       /* Try to shutdown neatly */
+       signal(SIGTERM, sigterm_handler);
+       signal(SIGINT, sigterm_handler);
+
+       /* Do some work */
+       main_loop(local_sock, cmd_timeout);
+
+       close_local_sock(local_sock);
+       destroy_lvm();
+
+       return 0;
+}
+
+/* Called when the GuLM cluster layer has completed initialisation.
+   We send the version message */
+void clvmd_cluster_init_completed()
+{
+       send_version_message();
+}
+
+/* Data on a connected socket */
+static int local_sock_callback(struct local_client *thisfd, char *buf, int len,
+                              const char *csid,
+                              struct local_client **new_client)
+{
+       *new_client = NULL;
+       return read_from_local_sock(thisfd);
+}
+
+/* Data on a connected socket */
+static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
+                                    int len, const char *csid,
+                                    struct local_client **new_client)
+{
+       /* Someone connected to our local socket, accept it. */
+
+       struct sockaddr_un socka;
+       struct local_client *newfd;
+       socklen_t sl = sizeof(socka);
+       int client_fd = accept(thisfd->fd, (struct sockaddr *) &socka, &sl);
+
+       if (client_fd == -1 && errno == EINTR)
+               return 1;
+
+       if (client_fd >= 0) {
+               newfd = malloc(sizeof(struct local_client));
+               if (!newfd) {
+                       close(client_fd);
+                       return 1;
+               }
+
+               if (fcntl(client_fd, F_SETFD, 1))
+                       DEBUGLOG("setting CLOEXEC on client fd failed: %s\n", strerror(errno));
+
+               newfd->fd = client_fd;
+               newfd->type = LOCAL_SOCK;
+               newfd->xid = 0;
+               newfd->removeme = 0;
+               newfd->callback = local_sock_callback;
+               newfd->bits.localsock.replies = NULL;
+               newfd->bits.localsock.expected_replies = 0;
+               newfd->bits.localsock.cmd = NULL;
+               newfd->bits.localsock.in_progress = FALSE;
+               newfd->bits.localsock.sent_out = FALSE;
+               newfd->bits.localsock.threadid = 0;
+               newfd->bits.localsock.finished = 0;
+               newfd->bits.localsock.pipe_client = NULL;
+               newfd->bits.localsock.private = NULL;
+               newfd->bits.localsock.all_success = 1;
+               DEBUGLOG("Got new connection on fd %d\n", newfd->fd);
+               *new_client = newfd;
+       }
+       return 1;
+}
+
+static int local_pipe_callback(struct local_client *thisfd, char *buf,
+                              int maxlen, const char *csid,
+                              struct local_client **new_client)
+{
+       int len;
+       char buffer[PIPE_BUF];
+       struct local_client *sock_client = thisfd->bits.pipe.client;
+       int status = -1;        /* in error by default */
+
+       len = read(thisfd->fd, buffer, sizeof(int));
+       if (len == -1 && errno == EINTR)
+               return 1;
+
+       if (len == sizeof(int)) {
+               memcpy(&status, buffer, sizeof(int));
+       }
+
+       DEBUGLOG("read on PIPE %d: %d bytes: status: %d\n",
+                thisfd->fd, len, status);
+
+       /* EOF on pipe or an error, close it */
+       if (len <= 0) {
+               int jstat;
+               void *ret = &status;
+               close(thisfd->fd);
+
+               /* Clear out the cross-link */
+               if (thisfd->bits.pipe.client != NULL)
+                       thisfd->bits.pipe.client->bits.localsock.pipe_client =
+                           NULL;
+
+               /* Reap child thread */
+               if (thisfd->bits.pipe.threadid) {
+                       jstat = pthread_join(thisfd->bits.pipe.threadid, &ret);
+                       thisfd->bits.pipe.threadid = 0;
+                       if (thisfd->bits.pipe.client != NULL)
+                               thisfd->bits.pipe.client->bits.localsock.
+                                   threadid = 0;
+               }
+               return -1;
+       } else {
+               DEBUGLOG("background routine status was %d, sock_client=%p\n",
+                        status, sock_client);
+               /* But has the client gone away ?? */
+               if (sock_client == NULL) {
+                       DEBUGLOG
+                           ("Got PIPE response for dead client, ignoring it\n");
+               } else {
+                       /* If error then just return that code */
+                       if (status)
+                               send_local_reply(sock_client, status,
+                                                sock_client->fd);
+                       else {
+                               if (sock_client->bits.localsock.state ==
+                                   POST_COMMAND) {
+                                       send_local_reply(sock_client, 0,
+                                                        sock_client->fd);
+                               } else  // PRE_COMMAND finished.
+                               {
+                                       if (
+                                           (status =
+                                            distribute_command(sock_client)) !=
+                                           0) send_local_reply(sock_client,
+                                                               EFBIG,
+                                                               sock_client->
+                                                               fd);
+                               }
+                       }
+               }
+       }
+       return len;
+}
+
+/* If a noed is up, look for it in the reply array, if it's not there then
+   add one with "ETIMEDOUT".
+   NOTE: This won't race with real replies because they happen in the same thread.
+*/
+static void timedout_callback(struct local_client *client, const char *csid,
+                             int node_up)
+{
+       if (node_up) {
+               struct node_reply *reply;
+               char nodename[max_cluster_member_name_len];
+
+               clops->name_from_csid(csid, nodename);
+               DEBUGLOG("Checking for a reply from %s\n", nodename);
+               pthread_mutex_lock(&client->bits.localsock.reply_mutex);
+
+               reply = client->bits.localsock.replies;
+               while (reply && strcmp(reply->node, nodename) != 0) {
+                       reply = reply->next;
+               }
+
+               pthread_mutex_unlock(&client->bits.localsock.reply_mutex);
+
+               if (!reply) {
+                       DEBUGLOG("Node %s timed-out\n", nodename);
+                       add_reply_to_list(client, ETIMEDOUT, csid,
+                                         "Command timed out", 18);
+               }
+       }
+}
+
+/* Called when the request has timed out on at least one node. We fill in
+   the remaining node entries with ETIMEDOUT and return.
+
+   By the time we get here the node that caused
+   the timeout could have gone down, in which case we will never get the expected
+   number of replies that triggers the post command so we need to do it here
+*/
+static void request_timed_out(struct local_client *client)
+{
+       DEBUGLOG("Request timed-out. padding\n");
+       clops->cluster_do_node_callback(client, timedout_callback);
+
+       if (client->bits.localsock.num_replies !=
+           client->bits.localsock.expected_replies) {
+               /* Post-process the command */
+               if (client->bits.localsock.threadid) {
+                       pthread_mutex_lock(&client->bits.localsock.mutex);
+                       client->bits.localsock.state = POST_COMMAND;
+                       pthread_cond_signal(&client->bits.localsock.cond);
+                       pthread_mutex_unlock(&client->bits.localsock.mutex);
+               }
+       }
+}
+
+/* This is where the real work happens */
+static void main_loop(int local_sock, int cmd_timeout)
+{
+       DEBUGLOG("Using timeout of %d seconds\n", cmd_timeout);
+
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigaddset(&ss, SIGINT);
+       sigaddset(&ss, SIGTERM);
+       pthread_sigmask(SIG_UNBLOCK, &ss, NULL);
+       /* Main loop */
+       while (!quit) {
+               fd_set in;
+               int select_status;
+               struct local_client *thisfd;
+               struct timeval tv = { cmd_timeout, 0 };
+               int quorate = clops->is_quorate();
+
+               /* Wait on the cluster FD and all local sockets/pipes */
+               local_client_head.fd = clops->get_main_cluster_fd();
+               FD_ZERO(&in);
+               for (thisfd = &local_client_head; thisfd != NULL;
+                    thisfd = thisfd->next) {
+
+                       if (thisfd->removeme)
+                               continue;
+
+                       /* if the cluster is not quorate then don't listen for new requests */
+                       if ((thisfd->type != LOCAL_RENDEZVOUS &&
+                            thisfd->type != LOCAL_SOCK) || quorate)
+                               FD_SET(thisfd->fd, &in);
+               }
+
+               select_status = select(FD_SETSIZE, &in, NULL, NULL, &tv);
+
+               if (reread_config) {
+                       int saved_errno = errno;
+
+                       reread_config = 0;
+                       if (clops->reread_config)
+                               clops->reread_config();
+                       errno = saved_errno;
+               }
+
+               if (select_status > 0) {
+                       struct local_client *lastfd = NULL;
+                       char csid[MAX_CSID_LEN];
+                       char buf[max_cluster_message];
+
+                       for (thisfd = &local_client_head; thisfd != NULL;
+                            thisfd = thisfd->next) {
+
+                               if (thisfd->removeme) {
+                                       struct local_client *free_fd;
+                                       lastfd->next = thisfd->next;
+                                       free_fd = thisfd;
+                                       thisfd = lastfd;
+
+                                       DEBUGLOG("removeme set for fd %d\n", free_fd->fd);
+
+                                       /* Queue cleanup, this also frees the client struct */
+                                       add_to_lvmqueue(free_fd, NULL, 0, NULL);
+                                       break;
+                               }
+
+                               if (FD_ISSET(thisfd->fd, &in)) {
+                                       struct local_client *newfd = NULL;
+                                       int ret;
+
+                                       /* Do callback */
+                                       ret =
+                                           thisfd->callback(thisfd, buf,
+                                                            sizeof(buf), csid,
+                                                            &newfd);
+                                       /* Ignore EAGAIN */
+                                       if (ret < 0 && (errno == EAGAIN ||
+                                                       errno == EINTR)) continue;
+
+                                       /* Got error or EOF: Remove it from the list safely */
+                                       if (ret <= 0) {
+                                               struct local_client *free_fd;
+                                               int type = thisfd->type;
+
+                                               /* If the cluster socket shuts down, so do we */
+                                               if (type == CLUSTER_MAIN_SOCK ||
+                                                   type == CLUSTER_INTERNAL)
+                                                       goto closedown;
+
+                                               DEBUGLOG("ret == %d, errno = %d. removing client\n",
+                                                        ret, errno);
+                                               lastfd->next = thisfd->next;
+                                               free_fd = thisfd;
+                                               thisfd = lastfd;
+                                               safe_close(&(free_fd->fd));
+
+                                               /* Queue cleanup, this also frees the client struct */
+                                               add_to_lvmqueue(free_fd, NULL, 0, NULL);
+                                               break;
+                                       }
+
+                                       /* New client...simply add it to the list */
+                                       if (newfd) {
+                                               newfd->next = thisfd->next;
+                                               thisfd->next = newfd;
+                                               break;
+                                       }
+                               }
+                               lastfd = thisfd;
+                       }
+               }
+
+               /* Select timed out. Check for clients that have been waiting too long for a response */
+               if (select_status == 0) {
+                       time_t the_time = time(NULL);
+
+                       for (thisfd = &local_client_head; thisfd != NULL;
+                            thisfd = thisfd->next) {
+                               if (thisfd->type == LOCAL_SOCK
+                                   && thisfd->bits.localsock.sent_out
+                                   && thisfd->bits.localsock.sent_time +
+                                   cmd_timeout < the_time
+                                   && thisfd->bits.localsock.
+                                   expected_replies !=
+                                   thisfd->bits.localsock.num_replies) {
+                                       /* Send timed out message + replies we already have */
+                                       DEBUGLOG
+                                           ("Request timed-out (send: %ld, now: %ld)\n",
+                                            thisfd->bits.localsock.sent_time,
+                                            the_time);
+
+                                       thisfd->bits.localsock.all_success = 0;
+
+                                       request_timed_out(thisfd);
+                               }
+                       }
+               }
+               if (select_status < 0) {
+                       if (errno == EINTR)
+                               continue;
+
+#ifdef DEBUG
+                       perror("select error");
+                       exit(-1);
+#endif
+               }
+       }
+
+      closedown:
+       clops->cluster_closedown();
+}
+
+static __attribute__ ((noreturn)) void wait_for_child(int c_pipe, int timeout)
+{
+       int child_status;
+       int sstat;
+       fd_set fds;
+       struct timeval tv = {timeout, 0};
+
+       FD_ZERO(&fds);
+       FD_SET(c_pipe, &fds);
+
+       sstat = select(c_pipe+1, &fds, NULL, NULL, timeout? &tv: NULL);
+       if (sstat == 0) {
+               fprintf(stderr, "clvmd startup timed out\n");
+               exit(DFAIL_TIMEOUT);
+       }
+       if (sstat == 1) {
+               if (read(c_pipe, &child_status, sizeof(child_status)) !=
+                   sizeof(child_status)) {
+
+                       fprintf(stderr, "clvmd failed in initialisation\n");
+                       exit(DFAIL_INIT);
+               }
+               else {
+                       switch (child_status) {
+                       case SUCCESS:
+                               break;
+                       case DFAIL_INIT:
+                               fprintf(stderr, "clvmd failed in initialisation\n");
+                               break;
+                       case DFAIL_LOCAL_SOCK:
+                               fprintf(stderr, "clvmd could not create local socket\n");
+                               fprintf(stderr, "Another clvmd is probably already running\n");
+                               break;
+                       case DFAIL_CLUSTER_IF:
+                               fprintf(stderr, "clvmd could not connect to cluster manager\n");
+                               fprintf(stderr, "Consult syslog for more information\n");
+                               break;
+                       case DFAIL_MALLOC:
+                               fprintf(stderr, "clvmd failed, not enough memory\n");
+                               break;
+                       default:
+                               fprintf(stderr, "clvmd failed, error was %d\n", child_status);
+                               break;
+                       }
+                       exit(child_status);
+               }
+       }
+       fprintf(stderr, "clvmd startup, select failed: %s\n", strerror(errno));
+       exit(DFAIL_INIT);
+}
+
+/*
+ * Fork into the background and detach from our parent process.
+ * In the interests of user-friendliness we wait for the daemon
+ * to complete initialisation before returning its status
+ * the the user.
+ */
+static void be_daemon(int timeout)
+{
+       int devnull = open("/dev/null", O_RDWR);
+       if (devnull == -1) {
+               perror("Can't open /dev/null");
+               exit(3);
+       }
+
+       pipe(child_pipe);
+
+       switch (fork()) {
+       case -1:
+               perror("clvmd: can't fork");
+               exit(2);
+
+       case 0:         /* Child */
+               close(child_pipe[0]);
+               break;
+
+       default:       /* Parent */
+               close(child_pipe[1]);
+               wait_for_child(child_pipe[0], timeout);
+       }
+
+       /* Detach ourself from the calling environment */
+       if (close(0) || close(1) || close(2)) {
+               perror("Error closing terminal FDs");
+               exit(4);
+       }
+       setsid();
+
+       if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0
+           || dup2(devnull, 2) < 0) {
+               perror("Error setting terminal FDs to /dev/null");
+               log_error("Error setting terminal FDs to /dev/null: %m");
+               exit(5);
+       }
+       if (chdir("/")) {
+               log_error("Error setting current directory to /: %m");
+               exit(6);
+       }
+
+}
+
+/* Called when we have a read from the local socket.
+   was in the main loop but it's grown up and is a big girl now */
+static int read_from_local_sock(struct local_client *thisfd)
+{
+       int len;
+       int argslen;
+       int missing_len;
+       char buffer[PIPE_BUF];
+
+       len = read(thisfd->fd, buffer, sizeof(buffer));
+       if (len == -1 && errno == EINTR)
+               return 1;
+
+       DEBUGLOG("Read on local socket %d, len = %d\n", thisfd->fd, len);
+
+       /* EOF or error on socket */
+       if (len <= 0) {
+               int *status;
+               int jstat;
+
+               DEBUGLOG("EOF on local socket: inprogress=%d\n",
+                        thisfd->bits.localsock.in_progress);
+
+               thisfd->bits.localsock.finished = 1;
+
+               /* If the client went away in mid command then tidy up */
+               if (thisfd->bits.localsock.in_progress) {
+                       pthread_kill(thisfd->bits.localsock.threadid, SIGUSR2);
+                       pthread_mutex_lock(&thisfd->bits.localsock.mutex);
+                       thisfd->bits.localsock.state = POST_COMMAND;
+                       pthread_cond_signal(&thisfd->bits.localsock.cond);
+                       pthread_mutex_unlock(&thisfd->bits.localsock.mutex);
+
+                       /* Free any unsent buffers */
+                       free_reply(thisfd);
+               }
+
+               /* Kill the subthread & free resources */
+               if (thisfd->bits.localsock.threadid) {
+                       DEBUGLOG("Waiting for child thread\n");
+                       pthread_mutex_lock(&thisfd->bits.localsock.mutex);
+                       thisfd->bits.localsock.state = PRE_COMMAND;
+                       pthread_cond_signal(&thisfd->bits.localsock.cond);
+                       pthread_mutex_unlock(&thisfd->bits.localsock.mutex);
+
+                       jstat =
+                           pthread_join(thisfd->bits.localsock.threadid,
+                                        (void **) &status);
+                       DEBUGLOG("Joined child thread\n");
+
+                       thisfd->bits.localsock.threadid = 0;
+                       pthread_cond_destroy(&thisfd->bits.localsock.cond);
+                       pthread_mutex_destroy(&thisfd->bits.localsock.mutex);
+
+                       /* Remove the pipe client */
+                       if (thisfd->bits.localsock.pipe_client != NULL) {
+                               struct local_client *newfd;
+                               struct local_client *lastfd = NULL;
+                               struct local_client *free_fd = NULL;
+
+                               close(thisfd->bits.localsock.pipe_client->fd);  /* Close pipe */
+                               close(thisfd->bits.localsock.pipe);
+
+                               /* Remove pipe client */
+                               for (newfd = &local_client_head; newfd != NULL;
+                                    newfd = newfd->next) {
+                                       if (thisfd->bits.localsock.
+                                           pipe_client == newfd) {
+                                               thisfd->bits.localsock.
+                                                   pipe_client = NULL;
+
+                                               lastfd->next = newfd->next;
+                                               free_fd = newfd;
+                                               newfd->next = lastfd;
+                                               free(free_fd);
+                                               break;
+                                       }
+                                       lastfd = newfd;
+                               }
+                       }
+               }
+
+               /* Free the command buffer */
+               free(thisfd->bits.localsock.cmd);
+
+               /* Clear out the cross-link */
+               if (thisfd->bits.localsock.pipe_client != NULL)
+                       thisfd->bits.localsock.pipe_client->bits.pipe.client =
+                           NULL;
+
+               safe_close(&(thisfd->fd));
+               return 0;
+       } else {
+               int comms_pipe[2];
+               struct local_client *newfd;
+               char csid[MAX_CSID_LEN];
+               struct clvm_header *inheader;
+               int status;
+
+               inheader = (struct clvm_header *) buffer;
+
+               /* Fill in the client ID */
+               inheader->clientid = htonl(thisfd->fd);
+
+               /* If we are already busy then return an error */
+               if (thisfd->bits.localsock.in_progress) {
+                       struct clvm_header reply;
+                       reply.cmd = CLVMD_CMD_REPLY;
+                       reply.status = EBUSY;
+                       reply.arglen = 0;
+                       reply.flags = 0;
+                       send_message(&reply, sizeof(reply), our_csid,
+                                    thisfd->fd,
+                                    "Error sending EBUSY reply to local user");
+                       return len;
+               }
+
+               /* Free any old buffer space */
+               free(thisfd->bits.localsock.cmd);
+
+               /* See if we have the whole message */
+               argslen =
+                   len - strlen(inheader->node) - sizeof(struct clvm_header);
+               missing_len = inheader->arglen - argslen;
+
+               if (missing_len < 0)
+                       missing_len = 0;
+
+               /* Save the message */
+               thisfd->bits.localsock.cmd = malloc(len + missing_len);
+
+               if (!thisfd->bits.localsock.cmd) {
+                       struct clvm_header reply;
+                       reply.cmd = CLVMD_CMD_REPLY;
+                       reply.status = ENOMEM;
+                       reply.arglen = 0;
+                       reply.flags = 0;
+                       send_message(&reply, sizeof(reply), our_csid,
+                                    thisfd->fd,
+                                    "Error sending ENOMEM reply to local user");
+                       return 0;
+               }
+               memcpy(thisfd->bits.localsock.cmd, buffer, len);
+               thisfd->bits.localsock.cmd_len = len + missing_len;
+               inheader = (struct clvm_header *) thisfd->bits.localsock.cmd;
+
+               /* If we don't have the full message then read the rest now */
+               if (missing_len) {
+                       char *argptr =
+                           inheader->node + strlen(inheader->node) + 1;
+
+                       while (missing_len > 0 && len >= 0) {
+                               DEBUGLOG
+                                   ("got %d bytes, need another %d (total %d)\n",
+                                    argslen, missing_len, inheader->arglen);
+                               len = read(thisfd->fd, argptr + argslen,
+                                          missing_len);
+                               if (len >= 0) {
+                                       missing_len -= len;
+                                       argslen += len;
+                               }
+                       }
+               }
+
+               /* Initialise and lock the mutex so the subthread will wait after
+                  finishing the PRE routine */
+               if (!thisfd->bits.localsock.threadid) {
+                       pthread_mutex_init(&thisfd->bits.localsock.mutex, NULL);
+                       pthread_cond_init(&thisfd->bits.localsock.cond, NULL);
+                       pthread_mutex_init(&thisfd->bits.localsock.reply_mutex, NULL);
+               }
+
+               /* Only run the command if all the cluster nodes are running CLVMD */
+               if (((inheader->flags & CLVMD_FLAG_LOCAL) == 0) &&
+                   (check_all_clvmds_running(thisfd) == -1)) {
+                       thisfd->bits.localsock.expected_replies = 0;
+                       thisfd->bits.localsock.num_replies = 0;
+                       send_local_reply(thisfd, EHOSTDOWN, thisfd->fd);
+                       return len;
+               }
+
+               /* Check the node name for validity */
+               if (inheader->node[0] && clops->csid_from_name(csid, inheader->node)) {
+                       /* Error, node is not in the cluster */
+                       struct clvm_header reply;
+                       DEBUGLOG("Unknown node: '%s'\n", inheader->node);
+
+                       reply.cmd = CLVMD_CMD_REPLY;
+                       reply.status = ENOENT;
+                       reply.flags = 0;
+                       reply.arglen = 0;
+                       send_message(&reply, sizeof(reply), our_csid,
+                                    thisfd->fd,
+                                    "Error sending ENOENT reply to local user");
+                       thisfd->bits.localsock.expected_replies = 0;
+                       thisfd->bits.localsock.num_replies = 0;
+                       thisfd->bits.localsock.in_progress = FALSE;
+                       thisfd->bits.localsock.sent_out = FALSE;
+                       return len;
+               }
+
+               /* If we already have a subthread then just signal it to start */
+               if (thisfd->bits.localsock.threadid) {
+                       pthread_mutex_lock(&thisfd->bits.localsock.mutex);
+                       thisfd->bits.localsock.state = PRE_COMMAND;
+                       pthread_cond_signal(&thisfd->bits.localsock.cond);
+                       pthread_mutex_unlock(&thisfd->bits.localsock.mutex);
+                       return len;
+               }
+
+               /* Create a pipe and add the reading end to our FD list */
+               pipe(comms_pipe);
+               newfd = malloc(sizeof(struct local_client));
+               if (!newfd) {
+                       struct clvm_header reply;
+                       close(comms_pipe[0]);
+                       close(comms_pipe[1]);
+
+                       reply.cmd = CLVMD_CMD_REPLY;
+                       reply.status = ENOMEM;
+                       reply.arglen = 0;
+                       reply.flags = 0;
+                       send_message(&reply, sizeof(reply), our_csid,
+                                    thisfd->fd,
+                                    "Error sending ENOMEM reply to local user");
+                       return len;
+               }
+               DEBUGLOG("creating pipe, [%d, %d]\n", comms_pipe[0],
+                        comms_pipe[1]);
+
+               if (fcntl(comms_pipe[0], F_SETFD, 1))
+                       DEBUGLOG("setting CLOEXEC on pipe[0] failed: %s\n", strerror(errno));
+               if (fcntl(comms_pipe[1], F_SETFD, 1))
+                       DEBUGLOG("setting CLOEXEC on pipe[1] failed: %s\n", strerror(errno));
+
+               newfd->fd = comms_pipe[0];
+               newfd->removeme = 0;
+               newfd->type = THREAD_PIPE;
+               newfd->callback = local_pipe_callback;
+               newfd->next = thisfd->next;
+               newfd->bits.pipe.client = thisfd;
+               newfd->bits.pipe.threadid = 0;
+               thisfd->next = newfd;
+
+               /* Store a cross link to the pipe */
+               thisfd->bits.localsock.pipe_client = newfd;
+
+               thisfd->bits.localsock.pipe = comms_pipe[1];
+
+               /* Make sure the thread has a copy of it's own ID */
+               newfd->bits.pipe.threadid = thisfd->bits.localsock.threadid;
+
+               /* Run the pre routine */
+               thisfd->bits.localsock.in_progress = TRUE;
+               thisfd->bits.localsock.state = PRE_COMMAND;
+               DEBUGLOG("Creating pre&post thread\n");
+               status = pthread_create(&thisfd->bits.localsock.threadid, NULL,
+                              pre_and_post_thread, thisfd);
+               DEBUGLOG("Created pre&post thread, state = %d\n", status);
+       }
+       return len;
+}
+
+/* Add a file descriptor from the cluster or comms interface to
+   our list of FDs for select
+*/
+int add_client(struct local_client *new_client)
+{
+       new_client->next = local_client_head.next;
+       local_client_head.next = new_client;
+
+       return 0;
+}
+
+/* Called when the pre-command has completed successfully - we
+   now execute the real command on all the requested nodes */
+static int distribute_command(struct local_client *thisfd)
+{
+       struct clvm_header *inheader =
+           (struct clvm_header *) thisfd->bits.localsock.cmd;
+       int len = thisfd->bits.localsock.cmd_len;
+
+       thisfd->xid = global_xid++;
+       DEBUGLOG("distribute command: XID = %d\n", thisfd->xid);
+
+       /* Forward it to other nodes in the cluster if needed */
+       if (!(inheader->flags & CLVMD_FLAG_LOCAL)) {
+               /* if node is empty then do it on the whole cluster */
+               if (inheader->node[0] == '\0') {
+                       thisfd->bits.localsock.expected_replies =
+                           clops->get_num_nodes();
+                       thisfd->bits.localsock.num_replies = 0;
+                       thisfd->bits.localsock.sent_time = time(NULL);
+                       thisfd->bits.localsock.in_progress = TRUE;
+                       thisfd->bits.localsock.sent_out = TRUE;
+
+                       /* Do it here first */
+                       add_to_lvmqueue(thisfd, inheader, len, NULL);
+
+                       DEBUGLOG("Sending message to all cluster nodes\n");
+                       inheader->xid = thisfd->xid;
+                       send_message(inheader, len, NULL, -1,
+                                    "Error forwarding message to cluster");
+               } else {
+                        /* Do it on a single node */
+                       char csid[MAX_CSID_LEN];
+
+                       if (clops->csid_from_name(csid, inheader->node)) {
+                               /* This has already been checked so should not happen */
+                               return 0;
+                       } else {
+                               /* OK, found a node... */
+                               thisfd->bits.localsock.expected_replies = 1;
+                               thisfd->bits.localsock.num_replies = 0;
+                               thisfd->bits.localsock.in_progress = TRUE;
+
+                               /* Are we the requested node ?? */
+                               if (memcmp(csid, our_csid, max_csid_len) == 0) {
+                                       DEBUGLOG("Doing command on local node only\n");
+                                       add_to_lvmqueue(thisfd, inheader, len, NULL);
+                               } else {
+                                       DEBUGLOG("Sending message to single node: %s\n",
+                                                inheader->node);
+                                       inheader->xid = thisfd->xid;
+                                       send_message(inheader, len,
+                                                    csid, -1,
+                                                    "Error forwarding message to cluster node");
+                               }
+                       }
+               }
+       } else {
+               /* Local explicitly requested, ignore nodes */
+               thisfd->bits.localsock.in_progress = TRUE;
+               thisfd->bits.localsock.expected_replies = 1;
+               thisfd->bits.localsock.num_replies = 0;
+               add_to_lvmqueue(thisfd, inheader, len, NULL);
+       }
+       return 0;
+}
+
+/* Process a command from a remote node and return the result */
+static void process_remote_command(struct clvm_header *msg, int msglen, int fd,
+                                  const char *csid)
+{
+       char *replyargs;
+       char nodename[max_cluster_member_name_len];
+       int replylen = 0;
+       int buflen = max_cluster_message - sizeof(struct clvm_header) - 1;
+       int status;
+       int msg_malloced = 0;
+
+       /* Get the node name as we /may/ need it later */
+       clops->name_from_csid(csid, nodename);
+
+       DEBUGLOG("process_remote_command %s for clientid 0x%x XID %d on node %s\n",
+                decode_cmd(msg->cmd), msg->clientid, msg->xid, nodename);
+
+       /* Check for GOAWAY and sulk */
+       if (msg->cmd == CLVMD_CMD_GOAWAY) {
+
+               DEBUGLOG("Told to go away by %s\n", nodename);
+               log_error("Told to go away by %s\n", nodename);
+               exit(99);
+       }
+
+       /* Version check is internal - don't bother exposing it in
+          clvmd-command.c */
+       if (msg->cmd == CLVMD_CMD_VERSION) {
+               int version_nums[3];
+               char node[256];
+
+               memcpy(version_nums, msg->args, sizeof(version_nums));
+
+               clops->name_from_csid(csid, node);
+               DEBUGLOG("Remote node %s is version %d.%d.%d\n",
+                        node,
+                        ntohl(version_nums[0]),
+                        ntohl(version_nums[1]), ntohl(version_nums[2]));
+
+               if (ntohl(version_nums[0]) != CLVMD_MAJOR_VERSION) {
+                       struct clvm_header byebyemsg;
+                       DEBUGLOG
+                           ("Telling node %s to go away because of incompatible version number\n",
+                            node);
+                       log_notice
+                           ("Telling node %s to go away because of incompatible version number %d.%d.%d\n",
+                            node, ntohl(version_nums[0]),
+                            ntohl(version_nums[1]), ntohl(version_nums[2]));
+
+                       byebyemsg.cmd = CLVMD_CMD_GOAWAY;
+                       byebyemsg.status = 0;
+                       byebyemsg.flags = 0;
+                       byebyemsg.arglen = 0;
+                       byebyemsg.clientid = 0;
+                       clops->cluster_send_message(&byebyemsg, sizeof(byebyemsg),
+                                            our_csid,
+                                            "Error Sending GOAWAY message");
+               } else {
+                       clops->add_up_node(csid);
+               }
+               return;
+       }
+
+       /* Allocate a default reply buffer */
+       replyargs = malloc(max_cluster_message - sizeof(struct clvm_header));
+
+       if (replyargs != NULL) {
+               /* Run the command */
+               status =
+                   do_command(NULL, msg, msglen, &replyargs, buflen,
+                              &replylen);
+       } else {
+               status = ENOMEM;
+       }
+
+       /* If it wasn't a reply, then reply */
+       if (msg->cmd != CLVMD_CMD_REPLY) {
+               char *aggreply;
+
+               aggreply =
+                   realloc(replyargs, replylen + sizeof(struct clvm_header));
+               if (aggreply) {
+                       struct clvm_header *agghead =
+                           (struct clvm_header *) aggreply;
+
+                       replyargs = aggreply;
+                       /* Move it up so there's room for a header in front of the data */
+                       memmove(aggreply + offsetof(struct clvm_header, args),
+                               replyargs, replylen);
+
+                       agghead->xid = msg->xid;
+                       agghead->cmd = CLVMD_CMD_REPLY;
+                       agghead->status = status;
+                       agghead->flags = 0;
+                       agghead->clientid = msg->clientid;
+                       agghead->arglen = replylen;
+                       agghead->node[0] = '\0';
+                       send_message(aggreply,
+                                    sizeof(struct clvm_header) +
+                                    replylen, csid, fd,
+                                    "Error sending command reply");
+               } else {
+                       struct clvm_header head;
+
+                       DEBUGLOG("Error attempting to realloc return buffer\n");
+                       /* Return a failure response */
+                       head.cmd = CLVMD_CMD_REPLY;
+                       head.status = ENOMEM;
+                       head.flags = 0;
+                       head.clientid = msg->clientid;
+                       head.arglen = 0;
+                       head.node[0] = '\0';
+                       send_message(&head, sizeof(struct clvm_header), csid,
+                                    fd, "Error sending ENOMEM command reply");
+                       return;
+               }
+       }
+
+       /* Free buffer if it was malloced */
+       if (msg_malloced) {
+               free(msg);
+       }
+       free(replyargs);
+}
+
+/* Add a reply to a command to the list of replies for this client.
+   If we have got a full set then send them to the waiting client down the local
+   socket */
+static void add_reply_to_list(struct local_client *client, int status,
+                             const char *csid, const char *buf, int len)
+{
+       struct node_reply *reply;
+
+       pthread_mutex_lock(&client->bits.localsock.reply_mutex);
+
+       /* Add it to the list of replies */
+       reply = malloc(sizeof(struct node_reply));
+       if (reply) {
+               reply->status = status;
+               clops->name_from_csid(csid, reply->node);
+               DEBUGLOG("Reply from node %s: %d bytes\n", reply->node, len);
+
+               if (len > 0) {
+                       reply->replymsg = malloc(len);
+                       if (!reply->replymsg) {
+                               reply->status = ENOMEM;
+                       } else {
+                               memcpy(reply->replymsg, buf, len);
+                       }
+               } else {
+                       reply->replymsg = NULL;
+               }
+               /* Hook it onto the reply chain */
+               reply->next = client->bits.localsock.replies;
+               client->bits.localsock.replies = reply;
+       } else {
+               /* It's all gone horribly wrong... */
+               pthread_mutex_unlock(&client->bits.localsock.reply_mutex);
+               send_local_reply(client, ENOMEM, client->fd);
+               return;
+       }
+       DEBUGLOG("Got %d replies, expecting: %d\n",
+                client->bits.localsock.num_replies + 1,
+                client->bits.localsock.expected_replies);
+
+       /* If we have the whole lot then do the post-process */
+       if (++client->bits.localsock.num_replies ==
+           client->bits.localsock.expected_replies) {
+               /* Post-process the command */
+               if (client->bits.localsock.threadid) {
+                       pthread_mutex_lock(&client->bits.localsock.mutex);
+                       client->bits.localsock.state = POST_COMMAND;
+                       pthread_cond_signal(&client->bits.localsock.cond);
+                       pthread_mutex_unlock(&client->bits.localsock.mutex);
+               }
+       }
+       pthread_mutex_unlock(&client->bits.localsock.reply_mutex);
+}
+
+/* This is the thread that runs the PRE and post commands for a particular connection */
+static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
+{
+       struct local_client *client = (struct local_client *) arg;
+       int status;
+       int write_status;
+       sigset_t ss;
+       int pipe_fd = client->bits.localsock.pipe;
+
+       DEBUGLOG("in sub thread: client = %p\n", client);
+       pthread_mutex_lock(&client->bits.localsock.mutex);
+
+       /* Don't start until the LVM thread is ready */
+       pthread_mutex_lock(&lvm_start_mutex);
+       pthread_mutex_unlock(&lvm_start_mutex);
+       DEBUGLOG("Sub thread ready for work.\n");
+
+       /* Ignore SIGUSR1 (handled by master process) but enable
+          SIGUSR2 (kills subthreads) */
+       sigemptyset(&ss);
+       sigaddset(&ss, SIGUSR1);
+       pthread_sigmask(SIG_BLOCK, &ss, NULL);
+
+       sigdelset(&ss, SIGUSR1);
+       sigaddset(&ss, SIGUSR2);
+       pthread_sigmask(SIG_UNBLOCK, &ss, NULL);
+
+       /* Loop around doing PRE and POST functions until the client goes away */
+       while (!client->bits.localsock.finished) {
+               /* Execute the code */
+               status = do_pre_command(client);
+
+               if (status)
+                       client->bits.localsock.all_success = 0;
+
+               DEBUGLOG("Writing status %d down pipe %d\n", status, pipe_fd);
+
+               /* Tell the parent process we have finished this bit */
+               do {
+                       write_status = write(pipe_fd, &status, sizeof(int));
+                       if (write_status == sizeof(int))
+                               break;
+                       if (write_status < 0 &&
+                           (errno == EINTR || errno == EAGAIN))
+                               continue;
+                       log_error("Error sending to pipe: %m\n");
+                       break;
+               } while(1);
+
+               if (status) {
+                       client->bits.localsock.state = POST_COMMAND;
+                       goto next_pre;
+               }
+
+               /* We may need to wait for the condition variable before running the post command */
+               DEBUGLOG("Waiting to do post command - state = %d\n",
+                        client->bits.localsock.state);
+
+               if (client->bits.localsock.state != POST_COMMAND &&
+                   !client->bits.localsock.finished) {
+                       pthread_cond_wait(&client->bits.localsock.cond,
+                                         &client->bits.localsock.mutex);
+               }
+
+               DEBUGLOG("Got post command condition...\n");
+
+               /* POST function must always run, even if the client aborts */
+               status = 0;
+               do_post_command(client);
+
+               do {
+                       write_status = write(pipe_fd, &status, sizeof(int));
+                       if (write_status == sizeof(int))
+                               break;
+                       if (write_status < 0 &&
+                           (errno == EINTR || errno == EAGAIN))
+                               continue;
+                       log_error("Error sending to pipe: %m\n");
+                       break;
+               } while(1);
+next_pre:
+               DEBUGLOG("Waiting for next pre command\n");
+
+               if (client->bits.localsock.state != PRE_COMMAND &&
+                   !client->bits.localsock.finished) {
+                       pthread_cond_wait(&client->bits.localsock.cond,
+                                         &client->bits.localsock.mutex);
+               }
+
+               DEBUGLOG("Got pre command condition...\n");
+       }
+       pthread_mutex_unlock(&client->bits.localsock.mutex);
+       DEBUGLOG("Subthread finished\n");
+       pthread_exit((void *) 0);
+}
+
+/* Process a command on the local node and store the result */
+static int process_local_command(struct clvm_header *msg, int msglen,
+                                struct local_client *client,
+                                unsigned short xid)
+{
+       char *replybuf = malloc(max_cluster_message);
+       int buflen = max_cluster_message - sizeof(struct clvm_header) - 1;
+       int replylen = 0;
+       int status;
+
+       DEBUGLOG("process_local_command: %s msg=%p, msglen =%d, client=%p\n",
+                decode_cmd(msg->cmd), msg, msglen, client);
+
+       if (replybuf == NULL)
+               return -1;
+
+       status = do_command(client, msg, msglen, &replybuf, buflen, &replylen);
+
+       if (status)
+               client->bits.localsock.all_success = 0;
+
+       /* If we took too long then discard the reply */
+       if (xid == client->xid) {
+               add_reply_to_list(client, status, our_csid, replybuf, replylen);
+       } else {
+               DEBUGLOG
+                   ("Local command took too long, discarding xid %d, current is %d\n",
+                    xid, client->xid);
+       }
+
+       free(replybuf);
+       return status;
+}
+
+static int process_reply(const struct clvm_header *msg, int msglen, const char *csid)
+{
+       struct local_client *client = NULL;
+
+       client = find_client(msg->clientid);
+       if (!client) {
+               DEBUGLOG("Got message for unknown client 0x%x\n",
+                        msg->clientid);
+               log_error("Got message for unknown client 0x%x\n",
+                         msg->clientid);
+               return -1;
+       }
+
+       if (msg->status)
+               client->bits.localsock.all_success = 0;
+
+       /* Gather replies together for this client id */
+       if (msg->xid == client->xid) {
+               add_reply_to_list(client, msg->status, csid, msg->args,
+                                 msg->arglen);
+       } else {
+               DEBUGLOG("Discarding reply with old XID %d, current = %d\n",
+                        msg->xid, client->xid);
+       }
+       return 0;
+}
+
+/* Send an aggregated reply back to the client */
+static void send_local_reply(struct local_client *client, int status, int fd)
+{
+       struct clvm_header *clientreply;
+       struct node_reply *thisreply = client->bits.localsock.replies;
+       char *replybuf;
+       char *ptr;
+       int message_len = 0;
+
+       DEBUGLOG("Send local reply\n");
+
+       /* Work out the total size of the reply */
+       while (thisreply) {
+               if (thisreply->replymsg)
+                       message_len += strlen(thisreply->replymsg) + 1;
+               else
+                       message_len++;
+
+               message_len += strlen(thisreply->node) + 1 + sizeof(int);
+
+               thisreply = thisreply->next;
+       }
+
+       /* Add in the size of our header */
+       message_len = message_len + sizeof(struct clvm_header) + 1;
+       replybuf = malloc(message_len);
+
+       clientreply = (struct clvm_header *) replybuf;
+       clientreply->status = status;
+       clientreply->cmd = CLVMD_CMD_REPLY;
+       clientreply->node[0] = '\0';
+       clientreply->flags = 0;
+
+       ptr = clientreply->args;
+
+       /* Add in all the replies, and free them as we go */
+       thisreply = client->bits.localsock.replies;
+       while (thisreply) {
+               struct node_reply *tempreply = thisreply;
+
+               strcpy(ptr, thisreply->node);
+               ptr += strlen(thisreply->node) + 1;
+
+               if (thisreply->status)
+                       clientreply->flags |= CLVMD_FLAG_NODEERRS;
+
+               memcpy(ptr, &thisreply->status, sizeof(int));
+               ptr += sizeof(int);
+
+               if (thisreply->replymsg) {
+                       strcpy(ptr, thisreply->replymsg);
+                       ptr += strlen(thisreply->replymsg) + 1;
+               } else {
+                       ptr[0] = '\0';
+                       ptr++;
+               }
+               thisreply = thisreply->next;
+
+               free(tempreply->replymsg);
+               free(tempreply);
+       }
+
+       /* Terminate with an empty node name */
+       *ptr = '\0';
+
+       clientreply->arglen = ptr - clientreply->args + 1;
+
+       /* And send it */
+       send_message(replybuf, message_len, our_csid, fd,
+                    "Error sending REPLY to client");
+       free(replybuf);
+
+       /* Reset comms variables */
+       client->bits.localsock.replies = NULL;
+       client->bits.localsock.expected_replies = 0;
+       client->bits.localsock.in_progress = FALSE;
+       client->bits.localsock.sent_out = FALSE;
+}
+
+/* Just free a reply chain baceuse it wasn't used. */
+static void free_reply(struct local_client *client)
+{
+       /* Add in all the replies, and free them as we go */
+       struct node_reply *thisreply = client->bits.localsock.replies;
+       while (thisreply) {
+               struct node_reply *tempreply = thisreply;
+
+               thisreply = thisreply->next;
+
+               free(tempreply->replymsg);
+               free(tempreply);
+       }
+       client->bits.localsock.replies = NULL;
+}
+
+/* Send our version number to the cluster */
+static void send_version_message()
+{
+       char message[sizeof(struct clvm_header) + sizeof(int) * 3];
+       struct clvm_header *msg = (struct clvm_header *) message;
+       int version_nums[3];
+
+       msg->cmd = CLVMD_CMD_VERSION;
+       msg->status = 0;
+       msg->flags = 0;
+       msg->clientid = 0;
+       msg->arglen = sizeof(version_nums);
+
+       version_nums[0] = htonl(CLVMD_MAJOR_VERSION);
+       version_nums[1] = htonl(CLVMD_MINOR_VERSION);
+       version_nums[2] = htonl(CLVMD_PATCH_VERSION);
+
+       memcpy(&msg->args, version_nums, sizeof(version_nums));
+
+       hton_clvm(msg);
+
+       clops->cluster_send_message(message, sizeof(message), NULL,
+                            "Error Sending version number");
+}
+
+/* Send a message to either a local client or another server */
+static int send_message(void *buf, int msglen, const char *csid, int fd,
+                       const char *errtext)
+{
+       int len = 0;
+       int saved_errno = 0;
+       struct timespec delay;
+       struct timespec remtime;
+
+       int retry_cnt = 0;
+
+       /* Send remote messages down the cluster socket */
+       if (csid == NULL || !ISLOCAL_CSID(csid)) {
+               hton_clvm((struct clvm_header *) buf);
+               return clops->cluster_send_message(buf, msglen, csid, errtext);
+       } else {
+               int ptr = 0;
+
+               /* Make sure it all goes */
+               do {
+                       if (retry_cnt > MAX_RETRIES)
+                       {
+                               errno = saved_errno;
+                               log_error("%s", errtext);
+                               errno = saved_errno;
+                               break;
+                       }
+
+                       len = write(fd, buf + ptr, msglen - ptr);
+
+                       if (len <= 0) {
+                               if (errno == EINTR)
+                                       continue;
+                               if (errno == EAGAIN ||
+                                   errno == EIO ||
+                                   errno == ENOSPC) {
+                                       saved_errno = errno;
+                                       retry_cnt++;
+
+                                       delay.tv_sec = 0;
+                                       delay.tv_nsec = 100000;
+                                       remtime.tv_sec = 0;
+                                       remtime.tv_nsec = 0;
+                                       (void) nanosleep (&delay, &remtime);
+
+                                       continue;
+                               }
+                               log_error("%s", errtext);
+                               break;
+                       }
+                       ptr += len;
+               } while (ptr < msglen);
+       }
+       return len;
+}
+
+static int process_work_item(struct lvm_thread_cmd *cmd)
+{
+       /* If msg is NULL then this is a cleanup request */
+       if (cmd->msg == NULL) {
+               DEBUGLOG("process_work_item: free fd %d\n", cmd->client->fd);
+               cmd_client_cleanup(cmd->client);
+               free(cmd->client);
+               return 0;
+       }
+
+       if (!cmd->remote) {
+               DEBUGLOG("process_work_item: local\n");
+               process_local_command(cmd->msg, cmd->msglen, cmd->client,
+                                     cmd->xid);
+       } else {
+               DEBUGLOG("process_work_item: remote\n");
+               process_remote_command(cmd->msg, cmd->msglen, cmd->client->fd,
+                                      cmd->csid);
+       }
+       return 0;
+}
+
+/*
+ * Routine that runs in the "LVM thread".
+ */
+static void lvm_thread_fn(void *arg)
+{
+       struct dm_list *cmdl, *tmp;
+       sigset_t ss;
+       struct lvm_startup_params *lvm_params = arg;
+
+       DEBUGLOG("LVM thread function started\n");
+
+       /* Ignore SIGUSR1 & 2 */
+       sigemptyset(&ss);
+       sigaddset(&ss, SIGUSR1);
+       sigaddset(&ss, SIGUSR2);
+       pthread_sigmask(SIG_BLOCK, &ss, NULL);
+
+       /* Initialise the interface to liblvm */
+       init_clvm(lvm_params->using_gulm, lvm_params->argv);
+
+       /* Allow others to get moving */
+       pthread_mutex_unlock(&lvm_start_mutex);
+
+       /* Now wait for some actual work */
+       for (;;) {
+               DEBUGLOG("LVM thread waiting for work\n");
+
+               pthread_mutex_lock(&lvm_thread_mutex);
+               if (dm_list_empty(&lvm_cmd_head))
+                       pthread_cond_wait(&lvm_thread_cond, &lvm_thread_mutex);
+
+               dm_list_iterate_safe(cmdl, tmp, &lvm_cmd_head) {
+                       struct lvm_thread_cmd *cmd;
+
+                       cmd =
+                           dm_list_struct_base(cmdl, struct lvm_thread_cmd, list);
+                       dm_list_del(&cmd->list);
+                       pthread_mutex_unlock(&lvm_thread_mutex);
+
+                       process_work_item(cmd);
+                       free(cmd->msg);
+                       free(cmd);
+
+                       pthread_mutex_lock(&lvm_thread_mutex);
+               }
+               pthread_mutex_unlock(&lvm_thread_mutex);
+       }
+}
+
+/* Pass down some work to the LVM thread */
+static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
+                          int msglen, const char *csid)
+{
+       struct lvm_thread_cmd *cmd;
+
+       cmd = malloc(sizeof(struct lvm_thread_cmd));
+       if (!cmd)
+               return ENOMEM;
+
+       if (msglen) {
+               cmd->msg = malloc(msglen);
+               if (!cmd->msg) {
+                       log_error("Unable to allocate buffer space\n");
+                       free(cmd);
+                       return -1;
+               }
+               memcpy(cmd->msg, msg, msglen);
+       }
+       else {
+               cmd->msg = NULL;
+       }
+       cmd->client = client;
+       cmd->msglen = msglen;
+       cmd->xid = client->xid;
+
+       if (csid) {
+               memcpy(cmd->csid, csid, max_csid_len);
+               cmd->remote = 1;
+       } else {
+               cmd->remote = 0;
+       }
+
+       DEBUGLOG
+           ("add_to_lvmqueue: cmd=%p. client=%p, msg=%p, len=%d, csid=%p, xid=%d\n",
+            cmd, client, msg, msglen, csid, cmd->xid);
+       pthread_mutex_lock(&lvm_thread_mutex);
+       dm_list_add(&lvm_cmd_head, &cmd->list);
+       pthread_cond_signal(&lvm_thread_cond);
+       pthread_mutex_unlock(&lvm_thread_mutex);
+
+       return 0;
+}
+
+/* Return 0 if we can talk to an existing clvmd */
+static int check_local_clvmd(void)
+{
+       int local_socket;
+       struct sockaddr_un sockaddr;
+       int ret = 0;
+
+       /* Open local socket */
+       if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+               return -1;
+       }
+
+       memset(&sockaddr, 0, sizeof(sockaddr));
+       memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+       sockaddr.sun_family = AF_UNIX;
+
+       if (connect(local_socket,(struct sockaddr *) &sockaddr,
+                   sizeof(sockaddr))) {
+               ret = -1;
+       }
+
+       close(local_socket);
+       return ret;
+}
+
+static void close_local_sock(int local_socket)
+{
+       if (local_socket != -1 && close(local_socket))
+               stack;
+
+       if (CLVMD_SOCKNAME[0] != '\0' && unlink(CLVMD_SOCKNAME))
+               stack;
+}
+
+/* Open the local socket, that's the one we talk to libclvm down */
+static int open_local_sock()
+{
+       int local_socket = -1;
+       struct sockaddr_un sockaddr;
+       mode_t old_mask;
+
+       close_local_sock(local_socket);
+
+       (void) dm_prepare_selinux_context(CLVMD_SOCKNAME, S_IFSOCK);
+       old_mask = umask(0077);
+
+       /* Open local socket */
+       local_socket = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (local_socket < 0) {
+               log_error("Can't create local socket: %m");
+               goto error;
+       }
+
+       /* Set Close-on-exec & non-blocking */
+       if (fcntl(local_socket, F_SETFD, 1))
+               DEBUGLOG("setting CLOEXEC on local_socket failed: %s\n", strerror(errno));
+       fcntl(local_socket, F_SETFL, fcntl(local_socket, F_GETFL, 0) | O_NONBLOCK);
+
+       memset(&sockaddr, 0, sizeof(sockaddr));
+       memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+       sockaddr.sun_family = AF_UNIX;
+
+       if (bind(local_socket, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) {
+               log_error("can't bind local socket: %m");
+               goto error;
+       }
+       if (listen(local_socket, 1) != 0) {
+               log_error("listen local: %m");
+               goto error;
+       }
+
+       umask(old_mask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+       return local_socket;
+error:
+       close_local_sock(local_socket);
+       umask(old_mask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+       return -1;
+}
+
+void process_message(struct local_client *client, const char *buf, int len,
+                    const char *csid)
+{
+       struct clvm_header *inheader;
+
+       inheader = (struct clvm_header *) buf;
+       ntoh_clvm(inheader);    /* Byteswap fields */
+       if (inheader->cmd == CLVMD_CMD_REPLY)
+               process_reply(inheader, len, csid);
+       else
+               add_to_lvmqueue(client, inheader, len, csid);
+}
+
+
+static void check_all_callback(struct local_client *client, const char *csid,
+                              int node_up)
+{
+       if (!node_up)
+               add_reply_to_list(client, EHOSTDOWN, csid, "CLVMD not running",
+                                 18);
+}
+
+/* Check to see if all CLVMDs are running (ie one on
+   every node in the cluster).
+   If not, returns -1 and prints out a list of errant nodes */
+static int check_all_clvmds_running(struct local_client *client)
+{
+       DEBUGLOG("check_all_clvmds_running\n");
+       return clops->cluster_do_node_callback(client, check_all_callback);
+}
+
+/* Return a local_client struct given a client ID.
+   client IDs are in network byte order */
+static struct local_client *find_client(int clientid)
+{
+       struct local_client *thisfd;
+       for (thisfd = &local_client_head; thisfd != NULL; thisfd = thisfd->next) {
+               if (thisfd->fd == ntohl(clientid))
+                       return thisfd;
+       }
+       return NULL;
+}
+
+/* Byte-swapping routines for the header so we
+   work in a heterogeneous environment */
+static void hton_clvm(struct clvm_header *hdr)
+{
+       hdr->status = htonl(hdr->status);
+       hdr->arglen = htonl(hdr->arglen);
+       hdr->xid = htons(hdr->xid);
+       /* Don't swap clientid as it's only a token as far as
+          remote nodes are concerned */
+}
+
+static void ntoh_clvm(struct clvm_header *hdr)
+{
+       hdr->status = ntohl(hdr->status);
+       hdr->arglen = ntohl(hdr->arglen);
+       hdr->xid = ntohs(hdr->xid);
+}
+
+/* Handler for SIGUSR2 - sent to kill subthreads */
+static void sigusr2_handler(int sig)
+{
+       DEBUGLOG("SIGUSR2 received\n");
+       return;
+}
+
+static void sigterm_handler(int sig)
+{
+       DEBUGLOG("SIGTERM received\n");
+       quit = 1;
+       return;
+}
+
+static void sighup_handler(int sig)
+{
+        DEBUGLOG("got SIGHUP\n");
+       reread_config = 1;
+}
+
+int sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+       return clops->sync_lock(resource, mode, flags, lockid);
+}
+
+int sync_unlock(const char *resource, int lockid)
+{
+       return clops->sync_unlock(resource, lockid);
+}
+
+static if_type_t parse_cluster_interface(char *ifname)
+{
+       if_type_t iface = IF_AUTO;
+
+       if (!strcmp(ifname, "auto"))
+               iface = IF_AUTO;
+       if (!strcmp(ifname, "cman"))
+               iface = IF_CMAN;
+       if (!strcmp(ifname, "gulm"))
+               iface = IF_GULM;
+       if (!strcmp(ifname, "openais"))
+               iface = IF_OPENAIS;
+       if (!strcmp(ifname, "corosync"))
+               iface = IF_COROSYNC;
+       if (!strcmp(ifname, "singlenode"))
+               iface = IF_SINGLENODE;
+
+       return iface;
+}
+
+/*
+ * Try and find a cluster system in corosync's objdb, if it is running. This is
+ * only called if the command-line option is not present, and if it fails
+ * we still try the interfaces in order.
+ */
+static if_type_t get_cluster_type()
+{
+#ifdef HAVE_COROSYNC_CONFDB_H
+       confdb_handle_t handle;
+       if_type_t type = IF_AUTO;
+       int result;
+       char buf[255];
+       size_t namelen = sizeof(buf);
+       hdb_handle_t cluster_handle;
+       hdb_handle_t clvmd_handle;
+       confdb_callbacks_t callbacks = {
+               .confdb_key_change_notify_fn = NULL,
+               .confdb_object_create_change_notify_fn = NULL,
+               .confdb_object_delete_change_notify_fn = NULL
+       };
+
+       result = confdb_initialize (&handle, &callbacks);
+        if (result != CS_OK)
+               return type;
+
+        result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
+       if (result != CS_OK)
+               goto out;
+
+        result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, (void *)"cluster", strlen("cluster"), &cluster_handle);
+        if (result != CS_OK)
+               goto out;
+
+        result = confdb_object_find_start(handle, cluster_handle);
+       if (result != CS_OK)
+               goto out;
+
+        result = confdb_object_find(handle, cluster_handle, (void *)"clvmd", strlen("clvmd"), &clvmd_handle);
+        if (result != CS_OK)
+               goto out;
+
+        result = confdb_key_get(handle, clvmd_handle, (void *)"interface", strlen("interface"), buf, &namelen);
+        if (result != CS_OK)
+               goto out;
+
+       buf[namelen] = '\0';
+       type = parse_cluster_interface(buf);
+       DEBUGLOG("got interface type '%s' from confdb\n", buf);
+out:
+       confdb_finalize(handle);
+       return type;
+#else
+       return IF_AUTO;
+#endif
+}
diff --git a/daemons/clvmd/clvmd.h b/daemons/clvmd/clvmd.h
new file mode 100644 (file)
index 0000000..ccc79cc
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#ifndef _CLVMD_H
+#define _CLVMD_H
+
+#define CLVMD_MAJOR_VERSION 0
+#define CLVMD_MINOR_VERSION 2
+#define CLVMD_PATCH_VERSION 1
+
+/* Default time (in seconds) we will wait for all remote commands to execute
+   before declaring them dead */
+#define DEFAULT_CMD_TIMEOUT 60
+
+/* One of these for each reply we get from command execution on a node */
+struct node_reply {
+       char node[MAX_CLUSTER_MEMBER_NAME_LEN];
+       char *replymsg;
+       int status;
+       struct node_reply *next;
+};
+
+typedef enum {DEBUG_OFF, DEBUG_STDERR, DEBUG_SYSLOG} debug_t;
+
+/*
+ * These exist for the use of local sockets only when we are
+ * collecting responses from all cluster nodes
+ */
+struct localsock_bits {
+       struct node_reply *replies;
+       int num_replies;
+       int expected_replies;
+       time_t sent_time;       /* So we can check for timeouts */
+       int in_progress;        /* Only execute one cmd at a time per client */
+       int sent_out;           /* Flag to indicate that a command was sent
+                                  to remote nodes */
+       void *private;          /* Private area for command processor use */
+       void *cmd;              /* Whole command as passed down local socket */
+       int cmd_len;            /* Length of above */
+       int pipe;               /* Pipe to send PRE completion status down */
+       int finished;           /* Flag to tell subthread to exit */
+       int all_success;        /* Set to 0 if any node (or the pre_command)
+                                  failed */
+       struct local_client *pipe_client;
+       pthread_t threadid;
+       enum { PRE_COMMAND, POST_COMMAND, QUIT } state;
+       pthread_mutex_t mutex;  /* Main thread and worker synchronisation */
+       pthread_cond_t cond;
+
+       pthread_mutex_t reply_mutex;    /* Protect reply structure */
+};
+
+/* Entries for PIPE clients */
+struct pipe_bits {
+       struct local_client *client;    /* Actual (localsock) client */
+       pthread_t threadid;             /* Our own copy of the thread id */
+};
+
+/* Entries for Network socket clients */
+struct netsock_bits {
+       void *private;
+       int flags;
+};
+
+typedef int (*fd_callback_t) (struct local_client * fd, char *buf, int len,
+                             const char *csid,
+                             struct local_client ** new_client);
+
+/* One of these for each fd we are listening on */
+struct local_client {
+       int fd;
+       enum { CLUSTER_MAIN_SOCK, CLUSTER_DATA_SOCK, LOCAL_RENDEZVOUS,
+                   LOCAL_SOCK, THREAD_PIPE, CLUSTER_INTERNAL } type;
+       struct local_client *next;
+       unsigned short xid;
+       fd_callback_t callback;
+       uint8_t removeme;
+
+       union {
+               struct localsock_bits localsock;
+               struct pipe_bits pipe;
+               struct netsock_bits net;
+       } bits;
+};
+
+#define DEBUGLOG(fmt, args...) debuglog(fmt, ## args);
+
+#ifndef max
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
+/* The real command processor is in clvmd-command.c */
+extern int do_command(struct local_client *client, struct clvm_header *msg,
+                     int msglen, char **buf, int buflen, int *retlen);
+
+/* Pre and post command routines are called only on the local node */
+extern int do_pre_command(struct local_client *client);
+extern int do_post_command(struct local_client *client);
+extern void cmd_client_cleanup(struct local_client *client);
+extern int add_client(struct local_client *new_client);
+
+extern void clvmd_cluster_init_completed(void);
+extern void process_message(struct local_client *client, const char *buf,
+                           int len, const char *csid);
+extern void debuglog(const char *fmt, ... )
+  __attribute__ ((format(printf, 1, 2)));
+
+int sync_lock(const char *resource, int mode, int flags, int *lockid);
+int sync_unlock(const char *resource, int lockid);
+
+#endif
diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c
new file mode 100644 (file)
index 0000000..214f229
--- /dev/null
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "lvm-types.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+/* LVM2 headers */
+#include "toolcontext.h"
+#include "lvmcache.h"
+#include "lvm-globals.h"
+#include "activate.h"
+#include "archiver.h"
+#include "memlock.h"
+
+#include <syslog.h>
+
+static struct cmd_context *cmd = NULL;
+static struct dm_hash_table *lv_hash = NULL;
+static pthread_mutex_t lv_hash_lock;
+static pthread_mutex_t lvm_lock;
+static char last_error[1024];
+
+struct lv_info {
+       int lock_id;
+       int lock_mode;
+};
+
+static const char *decode_full_locking_cmd(uint32_t cmdl)
+{
+       static char buf[128];
+       const char *type;
+       const char *scope;
+       const char *command;
+
+       switch (cmdl & LCK_TYPE_MASK) {
+       case LCK_NULL:
+               type = "NULL";
+               break;
+       case LCK_READ:
+               type = "READ";
+               break;
+       case LCK_PREAD:
+               type = "PREAD";
+               break;
+       case LCK_WRITE:
+               type = "WRITE";
+               break;
+       case LCK_EXCL:
+               type = "EXCL";
+               break;
+       case LCK_UNLOCK:
+               type = "UNLOCK";
+               break;
+       default:
+               type = "unknown";
+               break;
+       }
+
+       switch (cmdl & LCK_SCOPE_MASK) {
+       case LCK_VG:
+               scope = "VG";
+               command = "LCK_VG";
+               break;
+       case LCK_LV:
+               scope = "LV";
+               switch (cmdl & LCK_MASK) {
+               case LCK_LV_EXCLUSIVE & LCK_MASK:
+                       command = "LCK_LV_EXCLUSIVE";
+                       break;
+               case LCK_LV_SUSPEND & LCK_MASK:
+                       command = "LCK_LV_SUSPEND";
+                       break;
+               case LCK_LV_RESUME & LCK_MASK:
+                       command = "LCK_LV_RESUME";
+                       break;
+               case LCK_LV_ACTIVATE & LCK_MASK:
+                       command = "LCK_LV_ACTIVATE";
+                       break;
+               case LCK_LV_DEACTIVATE & LCK_MASK:
+                       command = "LCK_LV_DEACTIVATE";
+                       break;
+               default:
+                       command = "unknown";
+                       break;
+               }
+               break;
+       default:
+               scope = "unknown";
+               command = "unknown";
+               break;
+       }
+
+       sprintf(buf, "0x%x %s (%s|%s%s%s%s%s%s)", cmdl, command, type, scope,
+               cmdl & LCK_NONBLOCK   ? "|NONBLOCK" : "",
+               cmdl & LCK_HOLD       ? "|HOLD" : "",
+               cmdl & LCK_LOCAL      ? "|LOCAL" : "",
+               cmdl & LCK_CLUSTER_VG ? "|CLUSTER_VG" : "",
+               cmdl & LCK_CACHE      ? "|CACHE" : "");
+
+       return buf;
+}
+
+/*
+ * Only processes 8 bits: excludes LCK_CACHE.
+ */
+static const char *decode_locking_cmd(unsigned char cmdl)
+{
+       return decode_full_locking_cmd((uint32_t) cmdl);
+}
+
+static const char *decode_flags(unsigned char flags)
+{
+       static char buf[128];
+       int len;
+
+       len = sprintf(buf, "0x%x ( %s%s%s%s%s)", flags,
+               flags & LCK_PARTIAL_MODE          ? "PARTIAL_MODE|" : "",
+               flags & LCK_MIRROR_NOSYNC_MODE    ? "MIRROR_NOSYNC|" : "",
+               flags & LCK_DMEVENTD_MONITOR_MODE ? "DMEVENTD_MONITOR|" : "",
+               flags & LCK_ORIGIN_ONLY_MODE ? "ORIGIN_ONLY|" : "",
+               flags & LCK_CONVERT ? "CONVERT|" : "");
+
+       if (len > 1)
+               buf[len - 2] = ' ';
+       else
+               buf[0] = '\0';
+
+       return buf;
+}
+
+char *get_last_lvm_error()
+{
+       return last_error;
+}
+
+/*
+ * Hash lock info helpers
+ */
+static struct lv_info *lookup_info(const char *resource)
+{
+       struct lv_info *lvi;
+
+       pthread_mutex_lock(&lv_hash_lock);
+       lvi = dm_hash_lookup(lv_hash, resource);
+       pthread_mutex_unlock(&lv_hash_lock);
+
+       return lvi;
+}
+
+static void insert_info(const char *resource, struct lv_info *lvi)
+{
+       pthread_mutex_lock(&lv_hash_lock);
+       dm_hash_insert(lv_hash, resource, lvi);
+       pthread_mutex_unlock(&lv_hash_lock);
+}
+
+static void remove_info(const char *resource)
+{
+       pthread_mutex_lock(&lv_hash_lock);
+       dm_hash_remove(lv_hash, resource);
+       pthread_mutex_unlock(&lv_hash_lock);
+}
+
+/*
+ * Return the mode a lock is currently held at (or -1 if not held)
+ */
+static int get_current_lock(char *resource)
+{
+       struct lv_info *lvi;
+
+       if ((lvi = lookup_info(resource)))
+               return lvi->lock_mode;
+
+       return -1;
+}
+
+
+void init_lvhash()
+{
+       /* Create hash table for keeping LV locks & status */
+       lv_hash = dm_hash_create(100);
+       pthread_mutex_init(&lv_hash_lock, NULL);
+       pthread_mutex_init(&lvm_lock, NULL);
+}
+
+/* Called at shutdown to tidy the lockspace */
+void destroy_lvhash()
+{
+       struct dm_hash_node *v;
+       struct lv_info *lvi;
+       char *resource;
+       int status;
+
+       pthread_mutex_lock(&lv_hash_lock);
+
+       dm_hash_iterate(v, lv_hash) {
+               lvi = dm_hash_get_data(lv_hash, v);
+               resource = dm_hash_get_key(lv_hash, v);
+
+               if ((status = sync_unlock(resource, lvi->lock_id)))
+                       DEBUGLOG("unlock_all. unlock failed(%d): %s\n",
+                                status,  strerror(errno));
+               free(lvi);
+       }
+
+       dm_hash_destroy(lv_hash);
+       lv_hash = NULL;
+
+       pthread_mutex_unlock(&lv_hash_lock);
+}
+
+/* Gets a real lock and keeps the info in the hash table */
+static int hold_lock(char *resource, int mode, int flags)
+{
+       int status;
+       int saved_errno;
+       struct lv_info *lvi;
+
+       /* Mask off invalid options */
+       flags &= LCKF_NOQUEUE | LCKF_CONVERT;
+
+       lvi = lookup_info(resource);
+
+       if (lvi && lvi->lock_mode == mode) {
+               DEBUGLOG("hold_lock, lock mode %d already held\n", mode);
+               return 0;
+       }
+
+       /* Only allow explicit conversions */
+       if (lvi && !(flags & LCKF_CONVERT)) {
+               errno = EBUSY;
+               return -1;
+       }
+       if (lvi) {
+               /* Already exists - convert it */
+               status =
+                   sync_lock(resource, mode, flags, &lvi->lock_id);
+               saved_errno = errno;
+               if (!status)
+                       lvi->lock_mode = mode;
+
+               if (status) {
+                       DEBUGLOG("hold_lock. convert to %d failed: %s\n", mode,
+                                strerror(errno));
+               }
+               errno = saved_errno;
+       } else {
+               lvi = malloc(sizeof(struct lv_info));
+               if (!lvi)
+                       return -1;
+
+               lvi->lock_mode = mode;
+               status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id);
+               saved_errno = errno;
+               if (status) {
+                       free(lvi);
+                       DEBUGLOG("hold_lock. lock at %d failed: %s\n", mode,
+                                strerror(errno));
+               } else
+                       insert_info(resource, lvi);
+
+               errno = saved_errno;
+       }
+       return status;
+}
+
+/* Unlock and remove it from the hash table */
+static int hold_unlock(char *resource)
+{
+       struct lv_info *lvi;
+       int status;
+       int saved_errno;
+
+       if (!(lvi = lookup_info(resource))) {
+               DEBUGLOG("hold_unlock, lock not already held\n");
+               return 0;
+       }
+
+       status = sync_unlock(resource, lvi->lock_id);
+       saved_errno = errno;
+       if (!status) {
+               remove_info(resource);
+               free(lvi);
+       } else {
+               DEBUGLOG("hold_unlock. unlock failed(%d): %s\n", status,
+                        strerror(errno));
+       }
+
+       errno = saved_errno;
+       return status;
+}
+
+/* Watch the return codes here.
+   liblvm API functions return 1(true) for success, 0(false) for failure and don't set errno.
+   libdlm API functions return 0 for success, -1 for failure and do set errno.
+   These functions here return 0 for success or >0 for failure (where the retcode is errno)
+*/
+
+/* Activate LV exclusive or non-exclusive */
+static int do_activate_lv(char *resource, unsigned char lock_flags, int mode)
+{
+       int oldmode;
+       int status;
+       int activate_lv;
+       int exclusive = 0;
+       struct lvinfo lvi;
+
+       /* Is it already open ? */
+       oldmode = get_current_lock(resource);
+       if (oldmode == mode && (lock_flags & LCK_CLUSTER_VG)) {
+               DEBUGLOG("do_activate_lv, lock already held at %d\n", oldmode);
+               return 0;       /* Nothing to do */
+       }
+
+       /* Does the config file want us to activate this LV ? */
+       if (!lv_activation_filter(cmd, resource, &activate_lv))
+               return EIO;
+
+       if (!activate_lv)
+               return 0;       /* Success, we did nothing! */
+
+       /* Do we need to activate exclusively? */
+       if ((activate_lv == 2) || (mode == LCK_EXCL)) {
+               exclusive = 1;
+               mode = LCK_EXCL;
+       }
+
+       /*
+        * Try to get the lock if it's a clustered volume group.
+        * Use lock conversion only if requested, to prevent implicit conversion
+        * of exclusive lock to shared one during activation.
+        */
+       if (lock_flags & LCK_CLUSTER_VG) {
+               status = hold_lock(resource, mode, LCKF_NOQUEUE | (lock_flags & LCK_CONVERT ? LCKF_CONVERT:0));
+               if (status) {
+                       /* Return an LVM-sensible error for this.
+                        * Forcing EIO makes the upper level return this text
+                        * rather than the strerror text for EAGAIN.
+                        */
+                       if (errno == EAGAIN) {
+                               sprintf(last_error, "Volume is busy on another node");
+                               errno = EIO;
+                       }
+                       return errno;
+               }
+       }
+
+       /* If it's suspended then resume it */
+       if (!lv_info_by_lvid(cmd, resource, 0, &lvi, 0, 0))
+               goto error;
+
+       if (lvi.suspended) {
+               memlock_inc(cmd);
+               if (!lv_resume(cmd, resource, 0)) {
+                       memlock_dec(cmd);
+                       goto error;
+               }
+       }
+
+       /* Now activate it */
+       if (!lv_activate(cmd, resource, exclusive))
+               goto error;
+
+       return 0;
+
+error:
+       if (oldmode == -1 || oldmode != mode)
+               (void)hold_unlock(resource);
+       return EIO;
+}
+
+/* Resume the LV if it was active */
+static int do_resume_lv(char *resource, unsigned char lock_flags)
+{
+       int oldmode;
+
+       /* Is it open ? */
+       oldmode = get_current_lock(resource);
+       if (oldmode == -1 && (lock_flags & LCK_CLUSTER_VG)) {
+               DEBUGLOG("do_resume_lv, lock not already held\n");
+               return 0;       /* We don't need to do anything */
+       }
+
+       if (!lv_resume_if_active(cmd, resource, (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0))
+               return EIO;
+
+       return 0;
+}
+
+/* Suspend the device if active */
+static int do_suspend_lv(char *resource, unsigned char lock_flags)
+{
+       int oldmode;
+       struct lvinfo lvi;
+       unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
+
+       /* Is it open ? */
+       oldmode = get_current_lock(resource);
+       if (oldmode == -1 && (lock_flags & LCK_CLUSTER_VG)) {
+               DEBUGLOG("do_suspend_lv, lock not already held\n");
+               return 0; /* Not active, so it's OK */
+       }
+
+       /* Only suspend it if it exists */
+       if (!lv_info_by_lvid(cmd, resource, origin_only, &lvi, 0, 0))
+               return EIO;
+
+       if (lvi.exists && !lv_suspend_if_active(cmd, resource, origin_only))
+               return EIO;
+
+       return 0;
+}
+
+static int do_deactivate_lv(char *resource, unsigned char lock_flags)
+{
+       int oldmode;
+       int status;
+
+       /* Is it open ? */
+       oldmode = get_current_lock(resource);
+       if (oldmode == -1 && (lock_flags & LCK_CLUSTER_VG)) {
+               DEBUGLOG("do_deactivate_lock, lock not already held\n");
+               return 0;       /* We don't need to do anything */
+       }
+
+       if (!lv_deactivate(cmd, resource))
+               return EIO;
+
+       if (lock_flags & LCK_CLUSTER_VG) {
+               status = hold_unlock(resource);
+               if (status)
+                       return errno;
+       }
+
+       return 0;
+}
+
+const char *do_lock_query(char *resource)
+{
+       int mode;
+       const char *type = NULL;
+
+       mode = get_current_lock(resource);
+       switch (mode) {
+               case LCK_NULL: type = "NL"; break;
+               case LCK_READ: type = "CR"; break;
+               case LCK_PREAD:type = "PR"; break;
+               case LCK_WRITE:type = "PW"; break;
+               case LCK_EXCL: type = "EX"; break;
+       }
+
+       DEBUGLOG("do_lock_query: resource '%s', mode %i (%s)\n", resource, mode, type ?: "?");
+
+       return type;
+}
+
+/* This is the LOCK_LV part that happens on all nodes in the cluster -
+   it is responsible for the interaction with device-mapper and LVM */
+int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
+{
+       int status = 0;
+
+       DEBUGLOG("do_lock_lv: resource '%s', cmd = %s, flags = %s, memlock = %d\n",
+                resource, decode_locking_cmd(command), decode_flags(lock_flags), memlock());
+
+       if (!cmd->config_valid || config_files_changed(cmd)) {
+               /* Reinitialise various settings inc. logging, filters */
+               if (do_refresh_cache()) {
+                       log_error("Updated config file invalid. Aborting.");
+                       return EINVAL;
+               }
+       }
+
+       pthread_mutex_lock(&lvm_lock);
+       if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
+               init_mirror_in_sync(1);
+
+       if (lock_flags & LCK_DMEVENTD_MONITOR_MODE)
+               init_dmeventd_monitor(1);
+       else
+               init_dmeventd_monitor(0);
+
+       cmd->partial_activation = (lock_flags & LCK_PARTIAL_MODE) ? 1 : 0;
+
+       /* clvmd should never try to read suspended device */
+       init_ignore_suspended_devices(1);
+
+       switch (command & LCK_MASK) {
+       case LCK_LV_EXCLUSIVE:
+               status = do_activate_lv(resource, lock_flags, LCK_EXCL);
+               break;
+
+       case LCK_LV_SUSPEND:
+               status = do_suspend_lv(resource, lock_flags);
+               break;
+
+       case LCK_UNLOCK:
+       case LCK_LV_RESUME:     /* if active */
+               status = do_resume_lv(resource, lock_flags);
+               break;
+
+       case LCK_LV_ACTIVATE:
+               status = do_activate_lv(resource, lock_flags, LCK_READ);
+               break;
+
+       case LCK_LV_DEACTIVATE:
+               status = do_deactivate_lv(resource, lock_flags);
+               break;
+
+       default:
+               DEBUGLOG("Invalid LV command 0x%x\n", command);
+               status = EINVAL;
+               break;
+       }
+
+       if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
+               init_mirror_in_sync(0);
+
+       cmd->partial_activation = 0;
+
+       /* clean the pool for another command */
+       dm_pool_empty(cmd->mem);
+       pthread_mutex_unlock(&lvm_lock);
+
+       DEBUGLOG("Command return is %d, memlock is %d\n", status, memlock());
+       return status;
+}
+
+/* Functions to do on the local node only BEFORE the cluster-wide stuff above happens */
+int pre_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
+{
+       /* Nearly all the stuff happens cluster-wide. Apart from SUSPEND. Here we get the
+          lock out on this node (because we are the node modifying the metadata)
+          before suspending cluster-wide.
+          LCKF_CONVERT is used always, local node is going to modify metadata
+        */
+       if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_SUSPEND &&
+           (lock_flags & LCK_CLUSTER_VG)) {
+               DEBUGLOG("pre_lock_lv: resource '%s', cmd = %s, flags = %s\n",
+                        resource, decode_locking_cmd(command), decode_flags(lock_flags));
+
+               if (hold_lock(resource, LCK_WRITE, LCKF_NOQUEUE | LCKF_CONVERT))
+                       return errno;
+       }
+       return 0;
+}
+
+/* Functions to do on the local node only AFTER the cluster-wide stuff above happens */
+int post_lock_lv(unsigned char command, unsigned char lock_flags,
+                char *resource)
+{
+       int status;
+       unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
+
+       /* Opposite of above, done on resume after a metadata update */
+       if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_RESUME &&
+           (lock_flags & LCK_CLUSTER_VG)) {
+               int oldmode;
+
+               DEBUGLOG
+                   ("post_lock_lv: resource '%s', cmd = %s, flags = %s\n",
+                    resource, decode_locking_cmd(command), decode_flags(lock_flags));
+
+               /* If the lock state is PW then restore it to what it was */
+               oldmode = get_current_lock(resource);
+               if (oldmode == LCK_WRITE) {
+                       struct lvinfo lvi;
+
+                       pthread_mutex_lock(&lvm_lock);
+                       status = lv_info_by_lvid(cmd, resource, origin_only, &lvi, 0, 0);
+                       pthread_mutex_unlock(&lvm_lock);
+                       if (!status)
+                               return EIO;
+
+                       if (lvi.exists) {
+                               if (hold_lock(resource, LCK_READ, LCKF_CONVERT))
+                                       return errno;
+                       } else if (hold_unlock(resource))
+                               return errno;
+               }
+       }
+       return 0;
+}
+
+/* Check if a VG is in use by LVM1 so we don't stomp on it */
+int do_check_lvm1(const char *vgname)
+{
+       int status;
+
+       status = check_lvm1_vg_inactive(cmd, vgname);
+
+       return status == 1 ? 0 : EBUSY;
+}
+
+int do_refresh_cache()
+{
+       DEBUGLOG("Refreshing context\n");
+       log_notice("Refreshing context");
+
+       pthread_mutex_lock(&lvm_lock);
+
+       if (!refresh_toolcontext(cmd)) {
+               pthread_mutex_unlock(&lvm_lock);
+               return -1;
+       }
+
+       init_full_scan_done(0);
+       init_ignore_suspended_devices(1);
+       lvmcache_label_scan(cmd, 2);
+       dm_pool_empty(cmd->mem);
+
+       pthread_mutex_unlock(&lvm_lock);
+
+       return 0;
+}
+
+
+/* Only called at gulm startup. Drop any leftover VG or P_orphan locks
+   that might be hanging around if we died for any reason
+*/
+static void drop_vg_locks(void)
+{
+       char vg[128];
+       char line[255];
+       FILE *vgs =
+           popen
+           (LVM_PATH " pvs  --config 'log{command_names=0 prefix=\"\"}' --nolocking --noheadings -o vg_name", "r");
+
+       sync_unlock("P_" VG_ORPHANS, LCK_EXCL);
+       sync_unlock("P_" VG_GLOBAL, LCK_EXCL);
+
+       if (!vgs)
+               return;
+
+       while (fgets(line, sizeof(line), vgs)) {
+               char *vgend;
+               char *vgstart;
+
+               if (line[strlen(line)-1] == '\n')
+                       line[strlen(line)-1] = '\0';
+
+               vgstart = line + strspn(line, " ");
+               vgend = vgstart + strcspn(vgstart, " ");
+               *vgend = '\0';
+
+               if (strncmp(vgstart, "WARNING:", 8) == 0)
+                       continue;
+
+               sprintf(vg, "V_%s", vgstart);
+               sync_unlock(vg, LCK_EXCL);
+
+       }
+       if (fclose(vgs))
+               DEBUGLOG("vgs fclose failed: %s\n", strerror(errno));
+}
+
+/*
+ * Handle VG lock - drop metadata or update lvmcache state
+ */
+void do_lock_vg(unsigned char command, unsigned char lock_flags, char *resource)
+{
+       uint32_t lock_cmd = command;
+       char *vgname = resource + 2;
+
+       lock_cmd &= (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_HOLD);
+
+       /*
+        * Check if LCK_CACHE should be set. All P_ locks except # are cache related.
+        */
+       if (strncmp(resource, "P_#", 3) && !strncmp(resource, "P_", 2))
+               lock_cmd |= LCK_CACHE;
+
+       DEBUGLOG("do_lock_vg: resource '%s', cmd = %s, flags = %s, memlock = %d\n",
+                resource, decode_full_locking_cmd(lock_cmd), decode_flags(lock_flags), memlock());
+
+       /* P_#global causes a full cache refresh */
+       if (!strcmp(resource, "P_" VG_GLOBAL)) {
+               do_refresh_cache();
+               return;
+       }
+
+       pthread_mutex_lock(&lvm_lock);
+       switch (lock_cmd) {
+               case LCK_VG_COMMIT:
+                       DEBUGLOG("vg_commit notification for VG %s\n", vgname);
+                       lvmcache_commit_metadata(vgname);
+                       break;
+               case LCK_VG_REVERT:
+                       DEBUGLOG("vg_revert notification for VG %s\n", vgname);
+                       lvmcache_drop_metadata(vgname, 1);
+                       break;
+               case LCK_VG_DROP_CACHE:
+               default:
+                       DEBUGLOG("Invalidating cached metadata for VG %s\n", vgname);
+                       lvmcache_drop_metadata(vgname, 0);
+       }
+       pthread_mutex_unlock(&lvm_lock);
+}
+
+/*
+ * Compare the uuid with the list of exclusive locks that clvmd
+ * held before it was restarted, so we can get the right kind
+ * of lock now we are restarting.
+ */
+static int was_ex_lock(char *uuid, char **argv)
+{
+       int optnum = 0;
+       char *opt = argv[optnum];
+
+       while (opt) {
+               if (strcmp(opt, "-E") == 0) {
+                       opt = argv[++optnum];
+                       if (opt && (strcmp(opt, uuid) == 0)) {
+                               DEBUGLOG("Lock %s is exclusive\n", uuid);
+                               return 1;
+                       }
+               }
+               opt = argv[++optnum];
+       }
+       return 0;
+}
+
+/*
+ * Ideally, clvmd should be started before any LVs are active
+ * but this may not be the case...
+ * I suppose this also comes in handy if clvmd crashes, not that it would!
+ */
+static void *get_initial_state(char **argv)
+{
+       int lock_mode;
+       char lv[64], vg[64], flags[25], vg_flags[25];
+       char uuid[65];
+       char line[255];
+       FILE *lvs =
+           popen
+           (LVM_PATH " lvs  --config 'log{command_names=0 prefix=\"\"}' --nolocking --noheadings -o vg_uuid,lv_uuid,lv_attr,vg_attr",
+            "r");
+
+       if (!lvs)
+               return NULL;
+
+       while (fgets(line, sizeof(line), lvs)) {
+               if (sscanf(line, "%s %s %s %s\n", vg, lv, flags, vg_flags) == 4) {
+
+                       /* States: s:suspended a:active S:dropped snapshot I:invalid snapshot */
+                       if (strlen(vg) == 38 &&                         /* is is a valid UUID ? */
+                           (flags[4] == 'a' || flags[4] == 's') &&     /* is it active or suspended? */
+                           vg_flags[5] == 'c') {                       /* is it clustered ? */
+                               /* Convert hyphen-separated UUIDs into one */
+                               memcpy(&uuid[0], &vg[0], 6);
+                               memcpy(&uuid[6], &vg[7], 4);
+                               memcpy(&uuid[10], &vg[12], 4);
+                               memcpy(&uuid[14], &vg[17], 4);
+                               memcpy(&uuid[18], &vg[22], 4);
+                               memcpy(&uuid[22], &vg[27], 4);
+                               memcpy(&uuid[26], &vg[32], 6);
+                               memcpy(&uuid[32], &lv[0], 6);
+                               memcpy(&uuid[38], &lv[7], 4);
+                               memcpy(&uuid[42], &lv[12], 4);
+                               memcpy(&uuid[46], &lv[17], 4);
+                               memcpy(&uuid[50], &lv[22], 4);
+                               memcpy(&uuid[54], &lv[27], 4);
+                               memcpy(&uuid[58], &lv[32], 6);
+                               uuid[64] = '\0';
+
+                               lock_mode = LCK_READ;
+
+                               /* Look for this lock in the list of EX locks
+                                  we were passed on the command-line */
+                               if (was_ex_lock(uuid, argv))
+                                       lock_mode = LCK_EXCL;
+
+                               DEBUGLOG("getting initial lock for %s\n", uuid);
+                               hold_lock(uuid, lock_mode, LCKF_NOQUEUE);
+                       }
+               }
+       }
+       if (fclose(lvs))
+               DEBUGLOG("lvs fclose failed: %s\n", strerror(errno));
+       return NULL;
+}
+
+static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
+                       const char *message)
+{
+
+       /* Send messages to the normal LVM2 logging system too,
+          so we get debug output when it's asked for.
+          We need to NULL the function ptr otherwise it will just call
+          back into here! */
+       init_log_fn(NULL);
+       print_log(level, file, line, dm_errno, "%s", message);
+       init_log_fn(lvm2_log_fn);
+
+       /*
+        * Ignore non-error messages, but store the latest one for returning
+        * to the user.
+        */
+       if (level != _LOG_ERR && level != _LOG_FATAL)
+               return;
+
+       strncpy(last_error, message, sizeof(last_error));
+       last_error[sizeof(last_error)-1] = '\0';
+}
+
+/* This checks some basic cluster-LVM configuration stuff */
+static void check_config(void)
+{
+       int locking_type;
+
+       locking_type = find_config_tree_int(cmd, "global/locking_type", 1);
+
+       if (locking_type == 3) /* compiled-in cluster support */
+               return;
+
+       if (locking_type == 2) { /* External library, check name */
+               const char *libname;
+
+               libname = find_config_tree_str(cmd, "global/locking_library",
+                                         "");
+               if (strstr(libname, "liblvm2clusterlock.so"))
+                       return;
+
+               log_error("Incorrect LVM locking library specified in lvm.conf, cluster operations may not work.");
+               return;
+       }
+       log_error("locking_type not set correctly in lvm.conf, cluster operations will not work.");
+}
+
+/* Backups up the LVM metadata if it's changed */
+void lvm_do_backup(const char *vgname)
+{
+       struct volume_group * vg;
+       int consistent = 0;
+
+       DEBUGLOG("Triggering backup of VG metadata for %s.\n", vgname);
+
+       pthread_mutex_lock(&lvm_lock);
+
+       vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, 1, &consistent);
+
+       if (vg && consistent)
+               check_current_backup(vg);
+       else
+               log_error("Error backing up metadata, can't find VG for group %s", vgname);
+
+       free_vg(vg);
+       dm_pool_empty(cmd->mem);
+
+       pthread_mutex_unlock(&lvm_lock);
+}
+
+struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name)
+{
+       struct lv_info *lvi;
+
+       *name = NULL;
+       if (!v)
+               v = dm_hash_get_first(lv_hash);
+
+       do {
+               if (v) {
+                       lvi = dm_hash_get_data(lv_hash, v);
+                       DEBUGLOG("Looking for EX locks. found %x mode %d\n", lvi->lock_id, lvi->lock_mode);
+
+                       if (lvi->lock_mode == LCK_EXCL) {
+                               *name = dm_hash_get_key(lv_hash, v);
+                       }
+                       v = dm_hash_get_next(lv_hash, v);
+               }
+       } while (v && !*name);
+
+       if (*name)
+               DEBUGLOG("returning EXclusive UUID %s\n", *name);
+       return v;
+}
+
+/* Called to initialise the LVM context of the daemon */
+int init_clvm(int using_gulm, char **argv)
+{
+       if (!(cmd = create_toolcontext(1, NULL))) {
+               log_error("Failed to allocate command context");
+               return 0;
+       }
+
+       if (stored_errno()) {
+               destroy_toolcontext(cmd);
+               return 0;
+       }
+
+       /* Use LOG_DAEMON for syslog messages instead of LOG_USER */
+       init_syslog(LOG_DAEMON);
+       openlog("clvmd", LOG_PID, LOG_DAEMON);
+       cmd->cmd_line = "clvmd";
+
+       /* Check lvm.conf is setup for cluster-LVM */
+       check_config();
+       init_ignore_suspended_devices(1);
+
+       /* Remove any non-LV locks that may have been left around */
+       if (using_gulm)
+               drop_vg_locks();
+
+       get_initial_state(argv);
+
+       /* Trap log messages so we can pass them back to the user */
+       init_log_fn(lvm2_log_fn);
+       memlock_inc_daemon(cmd);
+
+       return 1;
+}
+
+void destroy_lvm(void)
+{
+       if (cmd) {
+               memlock_dec_daemon(cmd);
+               destroy_toolcontext(cmd);
+       }
+       cmd = NULL;
+}
diff --git a/daemons/clvmd/lvm-functions.h b/daemons/clvmd/lvm-functions.h
new file mode 100644 (file)
index 0000000..97153d4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/* Functions in lvm-functions.c */
+
+#ifndef _LVM_FUNCTIONS_H
+#define _LVM_FUNCTIONS_H
+
+extern int pre_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
+                      char *resource);
+extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
+                     char *resource);
+extern const char *do_lock_query(char *resource);
+extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
+                       char *resource);
+extern int do_check_lvm1(const char *vgname);
+extern int do_refresh_cache(void);
+extern int init_clvm(int using_gulm, char **argv);
+extern void destroy_lvm(void);
+extern void init_lvhash(void);
+extern void destroy_lvhash(void);
+extern void lvm_do_backup(const char *vgname);
+extern char *get_last_lvm_error(void);
+extern void do_lock_vg(unsigned char command, unsigned char lock_flags,
+                     char *resource);
+extern struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name);
+
+#endif
diff --git a/daemons/clvmd/refresh_clvmd.c b/daemons/clvmd/refresh_clvmd.c
new file mode 100644 (file)
index 0000000..1e88b5c
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/*
+ * Send a command to a running clvmd from the command-line
+ */
+
+#include "clvmd-common.h"
+
+#include "clvm.h"
+#include "refresh_clvmd.h"
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+typedef struct lvm_response {
+       char node[255];
+       char *response;
+       int status;
+       int len;
+} lvm_response_t;
+
+/*
+ * This gets stuck at the start of memory we allocate so we
+ * can sanity-check it at deallocation time
+ */
+#define LVM_SIGNATURE 0x434C564D
+
+static int _clvmd_sock = -1;
+
+/* Open connection to the clvm daemon */
+static int _open_local_sock(void)
+{
+       int local_socket;
+       struct sockaddr_un sockaddr;
+
+       /* Open local socket */
+       if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+               fprintf(stderr, "Local socket creation failed: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&sockaddr, 0, sizeof(sockaddr));
+       memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+
+       sockaddr.sun_family = AF_UNIX;
+
+       if (connect(local_socket,(struct sockaddr *) &sockaddr,
+                   sizeof(sockaddr))) {
+               int saved_errno = errno;
+
+               fprintf(stderr, "connect() failed on local socket: %s\n",
+                         strerror(errno));
+               if (close(local_socket))
+                       return -1;
+
+               errno = saved_errno;
+               return -1;
+       }
+
+       return local_socket;
+}
+
+/* Send a request and return the status */
+static int _send_request(const char *inbuf, int inlen, char **retbuf, int no_response)
+{
+       char outbuf[PIPE_BUF];
+       struct clvm_header *outheader = (struct clvm_header *) outbuf;
+       int len;
+       int off;
+       int buflen;
+       int err;
+
+       /* Send it to CLVMD */
+ rewrite:
+       if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
+               if (err == -1 && errno == EINTR)
+                       goto rewrite;
+               fprintf(stderr, "Error writing data to clvmd: %s", strerror(errno));
+               return 0;
+       }
+       if (no_response)
+               return 1;
+
+       /* Get the response */
+ reread:
+       if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
+               if (errno == EINTR)
+                       goto reread;
+               fprintf(stderr, "Error reading data from clvmd: %s", strerror(errno));
+               return 0;
+       }
+
+       if (len == 0) {
+               fprintf(stderr, "EOF reading CLVMD");
+               errno = ENOTCONN;
+               return 0;
+       }
+
+       /* Allocate buffer */
+       buflen = len + outheader->arglen;
+       *retbuf = dm_malloc(buflen);
+       if (!*retbuf) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* Copy the header */
+       memcpy(*retbuf, outbuf, len);
+       outheader = (struct clvm_header *) *retbuf;
+
+       /* Read the returned values */
+       off = 1;                /* we've already read the first byte */
+       while (off <= outheader->arglen && len > 0) {
+               len = read(_clvmd_sock, outheader->args + off,
+                          buflen - off - offsetof(struct clvm_header, args));
+               if (len > 0)
+                       off += len;
+       }
+
+       /* Was it an error ? */
+       if (outheader->status != 0) {
+               errno = outheader->status;
+
+               /* Only return an error here if there are no node-specific
+                  errors present in the message that might have more detail */
+               if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
+                       fprintf(stderr, "cluster request failed: %s\n", strerror(errno));
+                       return 0;
+               }
+
+       }
+
+       return 1;
+}
+
+/* Build the structure header and parse-out wildcard node names */
+static void _build_header(struct clvm_header *head, int cmd, const char *node,
+                         int len)
+{
+       head->cmd = cmd;
+       head->status = 0;
+       head->flags = 0;
+       head->clientid = 0;
+       head->arglen = len;
+
+       if (node) {
+               /*
+                * Allow a couple of special node names:
+                * "*" for all nodes,
+                * "." for the local node only
+                */
+               if (strcmp(node, "*") == 0) {
+                       head->node[0] = '\0';
+               } else if (strcmp(node, ".") == 0) {
+                       head->node[0] = '\0';
+                       head->flags = CLVMD_FLAG_LOCAL;
+               } else
+                       strcpy(head->node, node);
+       } else
+               head->node[0] = '\0';
+}
+
+/*
+ * Send a message to a(or all) node(s) in the cluster and wait for replies
+ */
+static int _cluster_request(char cmd, const char *node, void *data, int len,
+                           lvm_response_t ** response, int *num, int no_response)
+{
+       char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
+       char *inptr;
+       char *retbuf = NULL;
+       int status;
+       int i;
+       int num_responses = 0;
+       struct clvm_header *head = (struct clvm_header *) outbuf;
+       lvm_response_t *rarray;
+
+       *num = 0;
+
+       if (_clvmd_sock == -1)
+               _clvmd_sock = _open_local_sock();
+
+       if (_clvmd_sock == -1)
+               return 0;
+
+       _build_header(head, cmd, node, len);
+       memcpy(head->node + strlen(head->node) + 1, data, len);
+
+       status = _send_request(outbuf, sizeof(struct clvm_header) +
+                              strlen(head->node) + len, &retbuf, no_response);
+       if (!status || no_response)
+               goto out;
+
+       /* Count the number of responses we got */
+       head = (struct clvm_header *) retbuf;
+       inptr = head->args;
+       while (inptr[0]) {
+               num_responses++;
+               inptr += strlen(inptr) + 1;
+               inptr += sizeof(int);
+               inptr += strlen(inptr) + 1;
+       }
+
+       /*
+        * Allocate response array.
+        * With an extra pair of INTs on the front to sanity
+        * check the pointer when we are given it back to free
+        */
+       *response = dm_malloc(sizeof(lvm_response_t) * num_responses +
+                           sizeof(int) * 2);
+       if (!*response) {
+               errno = ENOMEM;
+               status = 0;
+               goto out;
+       }
+
+       rarray = *response;
+
+       /* Unpack the response into an lvm_response_t array */
+       inptr = head->args;
+       i = 0;
+       while (inptr[0]) {
+               strcpy(rarray[i].node, inptr);
+               inptr += strlen(inptr) + 1;
+
+               memcpy(&rarray[i].status, inptr, sizeof(int));
+               inptr += sizeof(int);
+
+               rarray[i].response = dm_malloc(strlen(inptr) + 1);
+               if (rarray[i].response == NULL) {
+                       /* Free up everything else and return error */
+                       int j;
+                       for (j = 0; j < i; j++)
+                               dm_free(rarray[i].response);
+                       free(*response);
+                       errno = ENOMEM;
+                       status = -1;
+                       goto out;
+               }
+
+               strcpy(rarray[i].response, inptr);
+               rarray[i].len = strlen(inptr);
+               inptr += strlen(inptr) + 1;
+               i++;
+       }
+       *num = num_responses;
+       *response = rarray;
+
+      out:
+       if (retbuf)
+               dm_free(retbuf);
+
+       return status;
+}
+
+/* Free reply array */
+static int _cluster_free_request(lvm_response_t * response, int num)
+{
+       int i;
+
+       for (i = 0; i < num; i++) {
+               dm_free(response[i].response);
+       }
+
+       dm_free(response);
+
+       return 1;
+}
+
+int refresh_clvmd(int all_nodes)
+{
+       int num_responses;
+       char args[1]; // No args really.
+       lvm_response_t *response = NULL;
+       int saved_errno;
+       int status;
+       int i;
+
+       status = _cluster_request(CLVMD_CMD_REFRESH, all_nodes?"*":".", args, 0, &response, &num_responses, 0);
+
+       /* If any nodes were down then display them and return an error */
+       for (i = 0; i < num_responses; i++) {
+               if (response[i].status == EHOSTDOWN) {
+                       fprintf(stderr, "clvmd not running on node %s",
+                                 response[i].node);
+                       status = 0;
+                       errno = response[i].status;
+               } else if (response[i].status) {
+                       fprintf(stderr, "Error resetting node %s: %s",
+                                 response[i].node,
+                                 response[i].response[0] ?
+                                       response[i].response :
+                                       strerror(response[i].status));
+                       status = 0;
+                       errno = response[i].status;
+               }
+       }
+
+       saved_errno = errno;
+       _cluster_free_request(response, num_responses);
+       errno = saved_errno;
+
+       return status;
+}
+
+int restart_clvmd(int all_nodes)
+{
+       int dummy, status;
+
+       status = _cluster_request(CLVMD_CMD_RESTART, all_nodes?"*":".", NULL, 0, NULL, &dummy, 1);
+
+       /*
+        * FIXME: we cannot receive response, clvmd re-exec before it.
+        *        but also should not close socket too early (the whole rq is dropped then).
+        * FIXME: This should be handled this way:
+        *  - client waits for RESTART ack (and socket close)
+        *  - server restarts
+        *  - client checks that server is ready again (VERSION command?)
+        */
+       usleep(500000);
+
+       return status;
+}
+
+int debug_clvmd(int level, int clusterwide)
+{
+       int num_responses;
+       char args[1];
+       const char *nodes;
+       lvm_response_t *response = NULL;
+       int saved_errno;
+       int status;
+       int i;
+
+       args[0] = level;
+       if (clusterwide)
+               nodes = "*";
+       else
+               nodes = ".";
+
+       status = _cluster_request(CLVMD_CMD_SET_DEBUG, nodes, args, 1, &response, &num_responses, 0);
+
+       /* If any nodes were down then display them and return an error */
+       for (i = 0; i < num_responses; i++) {
+               if (response[i].status == EHOSTDOWN) {
+                       fprintf(stderr, "clvmd not running on node %s",
+                                 response[i].node);
+                       status = 0;
+                       errno = response[i].status;
+               } else if (response[i].status) {
+                       fprintf(stderr, "Error setting debug on node %s: %s",
+                                 response[i].node,
+                                 response[i].response[0] ?
+                                       response[i].response :
+                                       strerror(response[i].status));
+                       status = 0;
+                       errno = response[i].status;
+               }
+       }
+
+       saved_errno = errno;
+       _cluster_free_request(response, num_responses);
+       errno = saved_errno;
+
+       return status;
+}
diff --git a/daemons/clvmd/refresh_clvmd.h b/daemons/clvmd/refresh_clvmd.h
new file mode 100644 (file)
index 0000000..e8d1698
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+
+int refresh_clvmd(int all_nodes);
+int restart_clvmd(int all_nodes);
+int debug_clvmd(int level, int clusterwide);
+
diff --git a/daemons/clvmd/tcp-comms.c b/daemons/clvmd/tcp-comms.c
new file mode 100644 (file)
index 0000000..5f86556
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ *  Copyright (C) 2002-2003 Sistina Software, Inc. All rights reserved.
+ *  Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This provides the inter-clvmd communications for a system without CMAN.
+ * There is a listening TCP socket which accepts new connections in the
+ * normal way.
+ * It can also make outgoing connnections to the other clvmd nodes.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "clvmd.h"
+#include "clvmd-gulm.h"
+
+#define DEFAULT_TCP_PORT 21064
+
+static int listen_fd = -1;
+static int tcp_port;
+struct dm_hash_table *sock_hash;
+
+static int get_our_ip_address(char *addr, int *family);
+static int read_from_tcpsock(struct local_client *fd, char *buf, int len, char *csid,
+                            struct local_client **new_client);
+
+/* Called by init_cluster() to open up the listening socket */
+int init_comms(unsigned short port)
+{
+    struct sockaddr_in6 addr;
+
+    sock_hash = dm_hash_create(100);
+    tcp_port = port ? : DEFAULT_TCP_PORT;
+
+    listen_fd = socket(AF_INET6, SOCK_STREAM, 0);
+
+    if (listen_fd < 0)
+    {
+       return -1;
+    }
+    else
+    {
+       int one = 1;
+       setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+       setsockopt(listen_fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
+    }
+
+    memset(&addr, 0, sizeof(addr)); // Bind to INADDR_ANY
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(tcp_port);
+
+    if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+    {
+       DEBUGLOG("Can't bind to port: %s\n", strerror(errno));
+       syslog(LOG_ERR, "Can't bind to port %d, is clvmd already running ?", tcp_port);
+       close(listen_fd);
+       return -1;
+    }
+
+    listen(listen_fd, 5);
+
+    /* Set Close-on-exec */
+    fcntl(listen_fd, F_SETFD, 1);
+
+    return 0;
+}
+
+void tcp_remove_client(const char *c_csid)
+{
+    struct local_client *client;
+    char csid[GULM_MAX_CSID_LEN];
+    unsigned int i;
+    memcpy(csid, c_csid, sizeof csid);
+    DEBUGLOG("tcp_remove_client\n");
+
+    /* Don't actually close the socket here - that's the
+       job of clvmd.c whch will do the job when it notices the
+       other end has gone. We just need to remove the client(s) from
+       the hash table so we don't try to use it for sending any more */
+    for (i = 0; i < 2; i++)
+    {
+       client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+       if (client)
+       {
+           dm_hash_remove_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+           client->removeme = 1;
+           close(client->fd);
+       }
+       /* Look for a mangled one too, on the 2nd iteration. */
+       csid[0] ^= 0x80;
+    }
+}
+
+int alloc_client(int fd, const char *c_csid, struct local_client **new_client)
+{
+    struct local_client *client;
+    char csid[GULM_MAX_CSID_LEN];
+    memcpy(csid, c_csid, sizeof csid);
+
+    DEBUGLOG("alloc_client %d csid = %s\n", fd, print_csid(csid));
+
+    /* Create a local_client and return it */
+    client = malloc(sizeof(struct local_client));
+    if (!client)
+    {
+       DEBUGLOG("malloc failed\n");
+       return -1;
+    }
+
+    memset(client, 0, sizeof(struct local_client));
+    client->fd = fd;
+    client->type = CLUSTER_DATA_SOCK;
+    client->callback = read_from_tcpsock;
+    if (new_client)
+       *new_client = client;
+
+    /* Add to our list of node sockets */
+    if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
+    {
+       DEBUGLOG("alloc_client mangling CSID for second connection\n");
+       /* This is a duplicate connection but we can't close it because
+          the other end may already have started sending.
+          So, we mangle the IP address and keep it, all sending will
+          go out of the main FD
+       */
+       csid[0] ^= 0x80;
+       client->bits.net.flags = 1; /* indicate mangled CSID */
+
+        /* If it still exists then kill the connection as we should only
+           ever have one incoming connection from each node */
+        if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
+        {
+           DEBUGLOG("Multiple incoming connections from node\n");
+            syslog(LOG_ERR, " Bogus incoming connection from %d.%d.%d.%d\n", csid[0],csid[1],csid[2],csid[3]);
+
+           free(client);
+            errno = ECONNREFUSED;
+            return -1;
+        }
+    }
+    dm_hash_insert_binary(sock_hash, csid, GULM_MAX_CSID_LEN, client);
+
+    return 0;
+}
+
+int get_main_gulm_cluster_fd()
+{
+    return listen_fd;
+}
+
+
+/* Read on main comms (listen) socket, accept it */
+int cluster_fd_gulm_callback(struct local_client *fd, char *buf, int len, const char *csid,
+                       struct local_client **new_client)
+{
+    int newfd;
+    struct sockaddr_in6 addr;
+    socklen_t addrlen = sizeof(addr);
+    int status;
+    char name[GULM_MAX_CLUSTER_MEMBER_NAME_LEN];
+
+    DEBUGLOG("cluster_fd_callback\n");
+    *new_client = NULL;
+    newfd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
+
+    DEBUGLOG("cluster_fd_callback, newfd=%d (errno=%d)\n", newfd, errno);
+    if (!newfd)
+    {
+       syslog(LOG_ERR, "error in accept: %m");
+       errno = EAGAIN;
+       return -1; /* Don't return an error or clvmd will close the listening FD */
+    }
+
+    /* Check that the client is a member of the cluster
+       and reject if not.
+    */
+    if (gulm_name_from_csid((char *)&addr.sin6_addr, name) < 0)
+    {
+       syslog(LOG_ERR, "Got connect from non-cluster node %s\n",
+              print_csid((char *)&addr.sin6_addr));
+       DEBUGLOG("Got connect from non-cluster node %s\n",
+                print_csid((char *)&addr.sin6_addr));
+       close(newfd);
+
+       errno = EAGAIN;
+       return -1;
+    }
+
+    status = alloc_client(newfd, (char *)&addr.sin6_addr, new_client);
+    if (status)
+    {
+       DEBUGLOG("cluster_fd_callback, alloc_client failed, status = %d\n", status);
+       close(newfd);
+       /* See above... */
+       errno = EAGAIN;
+       return -1;
+    }
+    DEBUGLOG("cluster_fd_callback, returning %d, %p\n", newfd, *new_client);
+    return newfd;
+}
+
+/* Try to get at least 'len' bytes from the socket */
+static int really_read(int fd, char *buf, int len)
+{
+       int got, offset;
+
+       got = offset = 0;
+
+       do {
+               got = read(fd, buf+offset, len-offset);
+               DEBUGLOG("really_read. got %d bytes\n", got);
+               offset += got;
+       } while (got > 0 && offset < len);
+
+       if (got < 0)
+               return got;
+       else
+               return offset;
+}
+
+
+static int read_from_tcpsock(struct local_client *client, char *buf, int len, char *csid,
+                            struct local_client **new_client)
+{
+    struct sockaddr_in6 addr;
+    socklen_t slen = sizeof(addr);
+    struct clvm_header *header = (struct clvm_header *)buf;
+    int status;
+    uint32_t arglen;
+
+    DEBUGLOG("read_from_tcpsock fd %d\n", client->fd);
+    *new_client = NULL;
+
+    /* Get "csid" */
+    getpeername(client->fd, (struct sockaddr *)&addr, &slen);
+    memcpy(csid, &addr.sin6_addr, GULM_MAX_CSID_LEN);
+
+    /* Read just the header first, then get the rest if there is any.
+     * Stream sockets, sigh.
+     */
+    status = really_read(client->fd, buf, sizeof(struct clvm_header));
+    if (status > 0)
+    {
+           int status2;
+
+           arglen = ntohl(header->arglen);
+
+           /* Get the rest */
+           if (arglen && arglen < GULM_MAX_CLUSTER_MESSAGE)
+           {
+                   status2 = really_read(client->fd, buf+status, arglen);
+                   if (status2 > 0)
+                           status += status2;
+                   else
+                           status = status2;
+           }
+    }
+
+    DEBUGLOG("read_from_tcpsock, status = %d(errno = %d)\n", status, errno);
+
+    /* Remove it from the hash table if there's an error, clvmd will
+       remove the socket from its lists and free the client struct */
+    if (status == 0 ||
+       (status < 0 && errno != EAGAIN && errno != EINTR))
+    {
+       char remcsid[GULM_MAX_CSID_LEN];
+
+       memcpy(remcsid, csid, GULM_MAX_CSID_LEN);
+       close(client->fd);
+
+       /* If the csid was mangled, then make sure we remove the right entry */
+       if (client->bits.net.flags)
+           remcsid[0] ^= 0x80;
+       dm_hash_remove_binary(sock_hash, remcsid, GULM_MAX_CSID_LEN);
+
+       /* Tell cluster manager layer */
+       add_down_node(remcsid);
+    }
+    else {
+           gulm_add_up_node(csid);
+           /* Send it back to clvmd */
+           process_message(client, buf, status, csid);
+    }
+    return status;
+}
+
+int gulm_connect_csid(const char *csid, struct local_client **newclient)
+{
+    int fd;
+    struct sockaddr_in6 addr;
+    int status;
+    int one = 1;
+
+    DEBUGLOG("Connecting socket\n");
+    fd = socket(PF_INET6, SOCK_STREAM, 0);
+
+    if (fd < 0)
+    {
+       syslog(LOG_ERR, "Unable to create new socket: %m");
+       return -1;
+    }
+
+    addr.sin6_family = AF_INET6;
+    memcpy(&addr.sin6_addr, csid, GULM_MAX_CSID_LEN);
+    addr.sin6_port = htons(tcp_port);
+
+    DEBUGLOG("Connecting socket %d\n", fd);
+    if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in6)) < 0)
+    {
+       /* "Connection refused" is "normal" because clvmd may not yet be running
+        * on that node.
+        */
+       if (errno != ECONNREFUSED)
+       {
+           syslog(LOG_ERR, "Unable to connect to remote node: %m");
+       }
+       DEBUGLOG("Unable to connect to remote node: %s\n", strerror(errno));
+       close(fd);
+       return -1;
+    }
+
+    /* Set Close-on-exec */
+    fcntl(fd, F_SETFD, 1);
+    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
+
+    status = alloc_client(fd, csid, newclient);
+    if (status)
+       close(fd);
+    else
+       add_client(*newclient);
+
+    /* If we can connect to it, it must be running a clvmd */
+    gulm_add_up_node(csid);
+    return status;
+}
+
+/* Send a message to a known CSID */
+static int tcp_send_message(void *buf, int msglen, const char *csid, const char *errtext)
+{
+    int status;
+    struct local_client *client;
+    char ourcsid[GULM_MAX_CSID_LEN];
+
+    assert(csid);
+
+    DEBUGLOG("tcp_send_message, csid = %s, msglen = %d\n", print_csid(csid), msglen);
+
+    /* Don't connect to ourself */
+    get_our_gulm_csid(ourcsid);
+    if (memcmp(csid, ourcsid, GULM_MAX_CSID_LEN) == 0)
+       return msglen;
+
+    client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+    if (!client)
+    {
+       status = gulm_connect_csid(csid, &client);
+       if (status)
+           return -1;
+    }
+    DEBUGLOG("tcp_send_message, fd = %d\n", client->fd);
+
+    return write(client->fd, buf, msglen);
+}
+
+
+int gulm_cluster_send_message(void *buf, int msglen, const char *csid, const char *errtext)
+{
+    int status=0;
+
+    DEBUGLOG("cluster send message, csid = %p, msglen = %d\n", csid, msglen);
+
+    /* If csid is NULL then send to all known (not just connected) nodes */
+    if (!csid)
+    {
+       void *context = NULL;
+       char loop_csid[GULM_MAX_CSID_LEN];
+
+       /* Loop round all gulm-known nodes */
+       while (get_next_node_csid(&context, loop_csid))
+       {
+           status = tcp_send_message(buf, msglen, loop_csid, errtext);
+           if (status == 0 ||
+               (status < 0 && (errno == EAGAIN || errno == EINTR)))
+               break;
+       }
+    }
+    else
+    {
+
+       status = tcp_send_message(buf, msglen, csid, errtext);
+    }
+    return status;
+}
+
+/* To get our own IP address we get the locally bound address of the
+   socket that's talking to GULM in the assumption(eek) that it will
+   be on the "right" network in a multi-homed system */
+static int get_our_ip_address(char *addr, int *family)
+{
+       struct utsname info;
+
+       uname(&info);
+       get_ip_address(info.nodename, addr);
+
+       return 0;
+}
+
+/* Public version of above for those that don't care what protocol
+   we're using */
+void get_our_gulm_csid(char *csid)
+{
+    static char our_csid[GULM_MAX_CSID_LEN];
+    static int got_csid = 0;
+
+    if (!got_csid)
+    {
+       int family;
+
+       memset(our_csid, 0, sizeof(our_csid));
+       if (get_our_ip_address(our_csid, &family))
+       {
+           got_csid = 1;
+       }
+    }
+    memcpy(csid, our_csid, GULM_MAX_CSID_LEN);
+}
+
+static void map_v4_to_v6(struct in_addr *ip4, struct in6_addr *ip6)
+{
+   ip6->s6_addr32[0] = 0;
+   ip6->s6_addr32[1] = 0;
+   ip6->s6_addr32[2] = htonl(0xffff);
+   ip6->s6_addr32[3] = ip4->s_addr;
+}
+
+/* Get someone else's IP address from DNS */
+int get_ip_address(const char *node, char *addr)
+{
+    struct hostent *he;
+
+    memset(addr, 0, GULM_MAX_CSID_LEN);
+
+    // TODO: what do we do about multi-homed hosts ???
+    // CCSs ip_interfaces solved this but some bugger removed it.
+
+    /* Try IPv6 first. The man page for gethostbyname implies that
+       it will lookup ip6 & ip4 names, but it seems not to */
+    he = gethostbyname2(node, AF_INET6);
+    if (he)
+    {
+       memcpy(addr, he->h_addr_list[0],
+              he->h_length);
+    }
+    else
+    {
+       he = gethostbyname2(node, AF_INET);
+       if (!he)
+           return -1;
+       map_v4_to_v6((struct in_addr *)he->h_addr_list[0], (struct in6_addr *)addr);
+    }
+
+    return 0;
+}
+
+char *print_csid(const char *csid)
+{
+    static char buf[128];
+    int *icsid = (int *)csid;
+
+    sprintf(buf, "[%x.%x.%x.%x]",
+           icsid[0],icsid[1],icsid[2],icsid[3]);
+
+    return buf;
+}
diff --git a/daemons/clvmd/tcp-comms.h b/daemons/clvmd/tcp-comms.h
new file mode 100644 (file)
index 0000000..9260e12
--- /dev/null
@@ -0,0 +1,13 @@
+#include <netinet/in.h>
+
+#define GULM_MAX_CLUSTER_MESSAGE 1600
+#define GULM_MAX_CSID_LEN sizeof(struct in6_addr)
+#define GULM_MAX_CLUSTER_MEMBER_NAME_LEN 128
+
+extern int init_comms(unsigned short);
+extern char *print_csid(const char *);
+int get_main_gulm_cluster_fd(void);
+int cluster_fd_gulm_callback(struct local_client *fd, char *buf, int len, const char *csid, struct local_client **new_client);
+int gulm_cluster_send_message(void *buf, int msglen, const char *csid, const char *errtext);
+void get_our_gulm_csid(char *csid);
+int gulm_connect_csid(const char *csid, struct local_client **newclient);
diff --git a/daemons/cmirrord/Makefile.in b/daemons/cmirrord/Makefile.in
new file mode 100644 (file)
index 0000000..0efc8d4
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CPG_LIBS = @CPG_LIBS@
+CPG_CFLAGS = @CPG_CFLAGS@
+SACKPT_LIBS = @SACKPT_LIBS@
+SACKPT_CFLAGS = @SACKPT_CFLAGS@
+
+SOURCES = clogd.c cluster.c compat.c functions.c link_mon.c local.c logging.c
+
+TARGETS = cmirrord
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper
+LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
+CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS)
+
+cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
+               $(LVMLIBS) $(LMLIBS) $(LIBS)
+
+install: $(TARGETS)
+       $(INSTALL_PROGRAM) -D cmirrord $(usrsbindir)/cmirrord
diff --git a/daemons/cmirrord/clogd.c b/daemons/cmirrord/clogd.c
new file mode 100644 (file)
index 0000000..8d9a7b9
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+#include "logging.h"
+#include "common.h"
+#include "functions.h"
+#include "link_mon.h"
+#include "local.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static volatile sig_atomic_t exit_now = 0;
+/* FIXME Review signal handling.  Should be volatile sig_atomic_t */
+static sigset_t signal_mask;
+static volatile sig_atomic_t signal_received;
+
+static void process_signals(void);
+static void daemonize(void);
+static void init_all(void);
+static void cleanup_all(void);
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+       daemonize();
+
+       init_all();
+
+       /* Parent can now exit, we're ready to handle requests */
+       kill(getppid(), SIGTERM);
+
+       LOG_PRINT("Starting cmirrord:");
+       LOG_PRINT(" Built: "__DATE__" "__TIME__"\n");
+       LOG_DBG(" Compiled with debugging.");
+
+       while (!exit_now) {
+               links_monitor();
+
+               links_issue_callbacks();
+
+               process_signals();
+       }
+       exit(EXIT_SUCCESS);
+}
+
+/*
+ * parent_exit_handler: exit the parent
+ * @sig: the signal
+ *
+ */
+static void parent_exit_handler(int sig __attribute__((unused)))
+{
+       exit_now = 1;
+}
+
+static void sig_handler(int sig)
+{
+       /* FIXME Races - don't touch signal_mask here. */
+       sigaddset(&signal_mask, sig);
+       signal_received = 1;
+}
+
+static void process_signal(int sig){
+       int r = 0;
+
+       switch(sig) {
+       case SIGINT:
+       case SIGQUIT:
+       case SIGTERM:
+       case SIGHUP:
+               r += log_status();
+               break;
+       case SIGUSR1:
+       case SIGUSR2:
+               log_debug();
+               /*local_debug();*/
+               cluster_debug();
+               return;
+       default:
+               LOG_PRINT("Unknown signal received... ignoring");
+               return;
+       }
+
+       if (!r) {
+               LOG_DBG("No current cluster logs... safe to exit.");
+               cleanup_all();
+               exit(EXIT_SUCCESS);
+       }
+
+       LOG_ERROR("Cluster logs exist.  Refusing to exit.");
+}
+
+static void process_signals(void)
+{
+       int x;
+
+       if (!signal_received)
+               return;
+
+       signal_received = 0;
+
+       for (x = 1; x < _NSIG; x++) {
+               if (sigismember(&signal_mask, x)) {
+                       sigdelset(&signal_mask, x);
+                       process_signal(x);
+               }
+       }
+}
+
+static void remove_lockfile(void)
+{
+       unlink(CMIRRORD_PIDFILE);
+}
+
+/*
+ * daemonize
+ *
+ * Performs the steps necessary to become a daemon.
+ */
+static void daemonize(void)
+{
+       int pid;
+       int status;
+
+       signal(SIGTERM, &parent_exit_handler);
+
+       pid = fork();
+
+       if (pid < 0) {
+               LOG_ERROR("Unable to fork()");
+               exit(EXIT_FAILURE);
+       }
+
+       if (pid) {
+               /* Parent waits here for child to get going */
+               while (!waitpid(pid, &status, WNOHANG) && !exit_now);
+               if (exit_now)
+                       exit(EXIT_SUCCESS);
+
+               switch (WEXITSTATUS(status)) {
+               case EXIT_LOCKFILE:
+                       LOG_ERROR("Failed to create lockfile");
+                       LOG_ERROR("Process already running?");
+                       break;
+               case EXIT_KERNEL_SOCKET:
+                       LOG_ERROR("Unable to create netlink socket");
+                       break;
+               case EXIT_KERNEL_BIND:
+                       LOG_ERROR("Unable to bind to netlink socket");
+                       break;
+               case EXIT_KERNEL_SETSOCKOPT:
+                       LOG_ERROR("Unable to setsockopt on netlink socket");
+                       break;
+               case EXIT_CLUSTER_CKPT_INIT:
+                       LOG_ERROR("Unable to initialize checkpoint service");
+                       LOG_ERROR("Has the cluster infrastructure been started?");
+                       break;
+               case EXIT_FAILURE:
+                       LOG_ERROR("Failed to start: Generic error");
+                       break;
+               default:
+                       LOG_ERROR("Failed to start: Unknown error");
+                       break;
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       setsid();
+       chdir("/");
+       umask(0);
+
+       close(0); close(1); close(2);
+       open("/dev/null", O_RDONLY); /* reopen stdin */
+       open("/dev/null", O_WRONLY); /* reopen stdout */
+       open("/dev/null", O_WRONLY); /* reopen stderr */
+
+       LOG_OPEN("cmirrord", LOG_PID, LOG_DAEMON);
+
+       (void) dm_prepare_selinux_context(CMIRRORD_PIDFILE, S_IFREG);
+       if (dm_create_lockfile(CMIRRORD_PIDFILE) == 0)
+               exit(EXIT_LOCKFILE);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       atexit(remove_lockfile);
+
+       /* FIXME Replace with sigaction. (deprecated) */
+       signal(SIGINT, &sig_handler);
+       signal(SIGQUIT, &sig_handler);
+       signal(SIGTERM, &sig_handler);
+       signal(SIGHUP, &sig_handler);
+       signal(SIGPIPE, SIG_IGN);
+       signal(SIGUSR1, &sig_handler);
+       signal(SIGUSR2, &sig_handler);
+       sigemptyset(&signal_mask);
+       signal_received = 0;
+}
+
+/*
+ * init_all
+ *
+ * Initialize modules.  Exit on failure.
+ */
+static void init_all(void)
+{
+       int r;
+
+       if ((r = init_local()) ||
+           (r = init_cluster())) {
+               exit(r);
+       }
+}
+
+/*
+ * cleanup_all
+ *
+ * Clean up before exiting
+ */
+static void cleanup_all(void)
+{
+       cleanup_local();
+       cleanup_cluster();
+}
diff --git a/daemons/cmirrord/cluster.c b/daemons/cmirrord/cluster.c
new file mode 100644 (file)
index 0000000..b6c925a
--- /dev/null
@@ -0,0 +1,1661 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#include "logging.h"
+#include "cluster.h"
+#include "common.h"
+#include "compat.h"
+#include "functions.h"
+#include "link_mon.h"
+#include "local.h"
+#include "xlate.h"
+
+#include <corosync/cpg.h>
+#include <errno.h>
+#include <openais/saAis.h>
+#include <openais/saCkpt.h>
+#include <signal.h>
+#include <unistd.h>
+
+/* Open AIS error codes */
+#define str_ais_error(x)                                               \
+       ((x) == SA_AIS_OK) ? "SA_AIS_OK" :                              \
+       ((x) == SA_AIS_ERR_LIBRARY) ? "SA_AIS_ERR_LIBRARY" :            \
+       ((x) == SA_AIS_ERR_VERSION) ? "SA_AIS_ERR_VERSION" :            \
+       ((x) == SA_AIS_ERR_INIT) ? "SA_AIS_ERR_INIT" :                  \
+       ((x) == SA_AIS_ERR_TIMEOUT) ? "SA_AIS_ERR_TIMEOUT" :            \
+       ((x) == SA_AIS_ERR_TRY_AGAIN) ? "SA_AIS_ERR_TRY_AGAIN" :        \
+       ((x) == SA_AIS_ERR_INVALID_PARAM) ? "SA_AIS_ERR_INVALID_PARAM" : \
+       ((x) == SA_AIS_ERR_NO_MEMORY) ? "SA_AIS_ERR_NO_MEMORY" :        \
+       ((x) == SA_AIS_ERR_BAD_HANDLE) ? "SA_AIS_ERR_BAD_HANDLE" :      \
+       ((x) == SA_AIS_ERR_BUSY) ? "SA_AIS_ERR_BUSY" :                  \
+       ((x) == SA_AIS_ERR_ACCESS) ? "SA_AIS_ERR_ACCESS" :              \
+       ((x) == SA_AIS_ERR_NOT_EXIST) ? "SA_AIS_ERR_NOT_EXIST" :        \
+       ((x) == SA_AIS_ERR_NAME_TOO_LONG) ? "SA_AIS_ERR_NAME_TOO_LONG" : \
+       ((x) == SA_AIS_ERR_EXIST) ? "SA_AIS_ERR_EXIST" :                \
+       ((x) == SA_AIS_ERR_NO_SPACE) ? "SA_AIS_ERR_NO_SPACE" :          \
+       ((x) == SA_AIS_ERR_INTERRUPT) ? "SA_AIS_ERR_INTERRUPT" :        \
+       ((x) == SA_AIS_ERR_NAME_NOT_FOUND) ? "SA_AIS_ERR_NAME_NOT_FOUND" : \
+       ((x) == SA_AIS_ERR_NO_RESOURCES) ? "SA_AIS_ERR_NO_RESOURCES" :  \
+       ((x) == SA_AIS_ERR_NOT_SUPPORTED) ? "SA_AIS_ERR_NOT_SUPPORTED" : \
+       ((x) == SA_AIS_ERR_BAD_OPERATION) ? "SA_AIS_ERR_BAD_OPERATION" : \
+       ((x) == SA_AIS_ERR_FAILED_OPERATION) ? "SA_AIS_ERR_FAILED_OPERATION" : \
+       ((x) == SA_AIS_ERR_MESSAGE_ERROR) ? "SA_AIS_ERR_MESSAGE_ERROR" : \
+       ((x) == SA_AIS_ERR_QUEUE_FULL) ? "SA_AIS_ERR_QUEUE_FULL" :      \
+       ((x) == SA_AIS_ERR_QUEUE_NOT_AVAILABLE) ? "SA_AIS_ERR_QUEUE_NOT_AVAILABLE" : \
+       ((x) == SA_AIS_ERR_BAD_FLAGS) ? "SA_AIS_ERR_BAD_FLAGS" :        \
+       ((x) == SA_AIS_ERR_TOO_BIG) ? "SA_AIS_ERR_TOO_BIG" :            \
+       ((x) == SA_AIS_ERR_NO_SECTIONS) ? "SA_AIS_ERR_NO_SECTIONS" :    \
+       "ais_error_unknown"
+
+#define _RQ_TYPE(x)                                                    \
+       ((x) == DM_ULOG_CHECKPOINT_READY) ? "DM_ULOG_CHECKPOINT_READY": \
+       ((x) == DM_ULOG_MEMBER_JOIN) ? "DM_ULOG_MEMBER_JOIN":           \
+       RQ_TYPE((x) & ~DM_ULOG_RESPONSE)
+
+static uint32_t my_cluster_id = 0xDEAD;
+static SaCkptHandleT ckpt_handle = 0;
+static SaCkptCallbacksT callbacks = { 0, 0 };
+static SaVersionT version = { 'B', 1, 1 };
+
+#define DEBUGGING_HISTORY 100
+//static char debugging[DEBUGGING_HISTORY][128];
+//static int idx = 0;
+#define LOG_SPRINT(cc, f, arg...) do {                         \
+               cc->idx++;                                      \
+               cc->idx = cc->idx % DEBUGGING_HISTORY;          \
+               sprintf(cc->debugging[cc->idx], f, ## arg);     \
+       } while (0)
+
+static int log_resp_rec = 0;
+
+struct checkpoint_data {
+       uint32_t requester;
+       char uuid[CPG_MAX_NAME_LENGTH];
+
+       int bitmap_size; /* in bytes */
+       char *sync_bits;
+       char *clean_bits;
+       char *recovering_region;
+       struct checkpoint_data *next;
+};     
+
+#define INVALID 0
+#define VALID   1
+#define LEAVING 2
+
+#define MAX_CHECKPOINT_REQUESTERS 10
+struct clog_cpg {
+       struct dm_list list;
+
+       uint32_t lowest_id;
+       cpg_handle_t handle;
+       struct cpg_name name;
+       uint64_t luid;
+
+       /* Are we the first, or have we received checkpoint? */
+       int state;
+       int cpg_state;  /* FIXME: debugging */
+       int free_me;
+       int delay;
+       int resend_requests;
+       struct dm_list startup_list;
+       struct dm_list working_list;
+
+       int checkpoints_needed;
+       uint32_t checkpoint_requesters[MAX_CHECKPOINT_REQUESTERS];
+       struct checkpoint_data *checkpoint_list;
+       int idx;
+       char debugging[DEBUGGING_HISTORY][128];
+};
+
+static struct dm_list clog_cpg_list;
+
+/*
+ * cluster_send
+ * @rq
+ *
+ * Returns: 0 on success, -Exxx on error
+ */
+int cluster_send(struct clog_request *rq)
+{
+       int r;
+       int count=0;
+       int found = 0;
+       struct iovec iov;
+       struct clog_cpg *entry;
+
+       dm_list_iterate_items(entry, &clog_cpg_list)
+               if (!strncmp(entry->name.value, rq->u_rq.uuid,
+                            CPG_MAX_NAME_LENGTH)) {
+                       found = 1;
+                       break;
+               }
+
+       if (!found) {
+               rq->u_rq.error = -ENOENT;
+               return -ENOENT;
+       }
+
+       /*
+        * Once the request heads for the cluster, the luid looses
+        * all its meaning.
+        */
+       rq->u_rq.luid = 0;
+
+       iov.iov_base = rq;
+       iov.iov_len = sizeof(struct clog_request) + rq->u_rq.data_size;
+
+       rq->u.version[0] = xlate64(CLOG_TFR_VERSION);
+       rq->u.version[1] = CLOG_TFR_VERSION;
+
+       r = clog_request_to_network(rq);
+       if (r < 0)
+               /* FIXME: Better error code for byteswap failure? */
+               return -EINVAL;
+
+       if (entry->cpg_state != VALID)
+               return -EINVAL;
+
+       do {
+               r = cpg_mcast_joined(entry->handle, CPG_TYPE_AGREED, &iov, 1);
+               if (r != SA_AIS_ERR_TRY_AGAIN)
+                       break;
+               count++;
+               if (count < 10)
+                       LOG_PRINT("[%s]  Retry #%d of cpg_mcast_joined: %s",
+                                 SHORT_UUID(rq->u_rq.uuid), count,
+                                 str_ais_error(r));
+               else if ((count < 100) && !(count % 10))
+                       LOG_ERROR("[%s]  Retry #%d of cpg_mcast_joined: %s",
+                                 SHORT_UUID(rq->u_rq.uuid), count,
+                                 str_ais_error(r));
+               else if ((count < 1000) && !(count % 100))
+                       LOG_ERROR("[%s]  Retry #%d of cpg_mcast_joined: %s",
+                                 SHORT_UUID(rq->u_rq.uuid), count,
+                                 str_ais_error(r));
+               else if ((count < 10000) && !(count % 1000))
+                       LOG_ERROR("[%s]  Retry #%d of cpg_mcast_joined: %s - "
+                                 "OpenAIS not handling the load?",
+                                 SHORT_UUID(rq->u_rq.uuid), count,
+                                 str_ais_error(r));
+               usleep(1000);
+       } while (1);
+
+       if (r == CPG_OK)
+               return 0;
+
+       /* error codes found in openais/cpg.h */
+       LOG_ERROR("cpg_mcast_joined error: %s", str_ais_error(r));
+
+       rq->u_rq.error = -EBADE;
+       return -EBADE;
+}
+
+static struct clog_request *get_matching_rq(struct clog_request *rq,
+                                           struct dm_list *l)
+{
+       struct clog_request *match, *n;
+
+       dm_list_iterate_items_gen_safe(match, n, l, u.list)
+               if (match->u_rq.seq == rq->u_rq.seq) {
+                       dm_list_del(&match->u.list);
+                       return match;
+               }
+
+       return NULL;
+}
+
+static char rq_buffer[DM_ULOG_REQUEST_SIZE];
+static int handle_cluster_request(struct clog_cpg *entry __attribute__((unused)),
+                                 struct clog_request *rq, int server)
+{
+       int r = 0;
+       struct clog_request *tmp = (struct clog_request *)rq_buffer;
+
+       /*
+        * We need a separate dm_ulog_request struct, one that can carry
+        * a return payload.  Otherwise, the memory address after
+        * rq will be altered - leading to problems
+        */
+       memset(rq_buffer, 0, sizeof(rq_buffer));
+       memcpy(tmp, rq, sizeof(struct clog_request) + rq->u_rq.data_size);
+
+       /*
+        * With resumes, we only handle our own.
+        * Resume is a special case that requires
+        * local action (to set up CPG), followed by
+        * a cluster action to co-ordinate reading
+        * the disk and checkpointing
+        */
+       if (tmp->u_rq.request_type == DM_ULOG_RESUME) {
+               if (tmp->originator == my_cluster_id) {
+                       r = do_request(tmp, server);
+
+                       r = kernel_send(&tmp->u_rq);
+                       if (r < 0)
+                               LOG_ERROR("Failed to send resume response to kernel");
+               }
+               return r;
+       }
+
+       r = do_request(tmp, server);
+
+       if (server &&
+           (tmp->u_rq.request_type != DM_ULOG_CLEAR_REGION) &&
+           (tmp->u_rq.request_type != DM_ULOG_POSTSUSPEND)) {
+               tmp->u_rq.request_type |= DM_ULOG_RESPONSE;
+
+               /*
+                * Errors from previous functions are in the rq struct.
+                */
+               r = cluster_send(tmp);
+               if (r < 0)
+                       LOG_ERROR("cluster_send failed: %s", strerror(-r));
+       }
+
+       return r;
+}
+
+static int handle_cluster_response(struct clog_cpg *entry,
+                                  struct clog_request *rq)
+{
+       int r = 0;
+       struct clog_request *orig_rq;
+
+       /*
+        * If I didn't send it, then I don't care about the response
+        */
+       if (rq->originator != my_cluster_id)
+               return 0;
+
+       rq->u_rq.request_type &= ~DM_ULOG_RESPONSE;
+       orig_rq = get_matching_rq(rq, &entry->working_list);
+
+       if (!orig_rq) {
+               /* Unable to find match for response */
+
+               LOG_ERROR("[%s] No match for cluster response: %s:%u",
+                         SHORT_UUID(rq->u_rq.uuid),
+                         _RQ_TYPE(rq->u_rq.request_type),
+                         rq->u_rq.seq);
+
+               LOG_ERROR("Current local list:");
+               if (dm_list_empty(&entry->working_list))
+                       LOG_ERROR("   [none]");
+
+               dm_list_iterate_items_gen(orig_rq, &entry->working_list, u.list)
+                       LOG_ERROR("   [%s]  %s:%u",
+                                 SHORT_UUID(orig_rq->u_rq.uuid),
+                                 _RQ_TYPE(orig_rq->u_rq.request_type),
+                                 orig_rq->u_rq.seq);
+
+               return -EINVAL;
+       }
+
+       if (log_resp_rec > 0) {
+               LOG_COND(log_resend_requests,
+                        "[%s] Response received to %s/#%u",
+                        SHORT_UUID(rq->u_rq.uuid),
+                        _RQ_TYPE(rq->u_rq.request_type),
+                        rq->u_rq.seq);
+               log_resp_rec--;
+       }
+
+       /* FIXME: Ensure memcpy cannot explode */
+       memcpy(orig_rq, rq, sizeof(*rq) + rq->u_rq.data_size);
+
+       r = kernel_send(&orig_rq->u_rq);
+       if (r)
+               LOG_ERROR("Failed to send response to kernel");
+
+       free(orig_rq);
+       return r;
+}
+
+static struct clog_cpg *find_clog_cpg(cpg_handle_t handle)
+{
+       struct clog_cpg *match;
+
+       dm_list_iterate_items(match, &clog_cpg_list)
+               if (match->handle == handle)
+                       return match;
+
+       return NULL;
+}
+
+/*
+ * prepare_checkpoint
+ * @entry: clog_cpg describing the log
+ * @cp_requester: nodeid requesting the checkpoint
+ *
+ * Creates and fills in a new checkpoint_data struct.
+ *
+ * Returns: checkpoint_data on success, NULL on error
+ */
+static struct checkpoint_data *prepare_checkpoint(struct clog_cpg *entry,
+                                                 uint32_t cp_requester)
+{
+       int r;
+       struct checkpoint_data *new;
+
+       if (entry->state != VALID) {
+               /*
+                * We can't store bitmaps yet, because the log is not
+                * valid yet.
+                */
+               LOG_ERROR("Forced to refuse checkpoint for nodeid %u - log not valid yet",
+                         cp_requester);
+               return NULL;
+       }
+
+       new = malloc(sizeof(*new));
+       if (!new) {
+               LOG_ERROR("Unable to create checkpoint data for %u",
+                         cp_requester);
+               return NULL;
+       }
+       memset(new, 0, sizeof(*new));
+       new->requester = cp_requester;
+       strncpy(new->uuid, entry->name.value, entry->name.length);
+
+       new->bitmap_size = push_state(entry->name.value, entry->luid,
+                                     "clean_bits",
+                                     &new->clean_bits, cp_requester);
+       if (new->bitmap_size <= 0) {
+               LOG_ERROR("Failed to store clean_bits to checkpoint for node %u",
+                         new->requester);
+               free(new);
+               return NULL;
+       }
+
+       new->bitmap_size = push_state(entry->name.value, entry->luid,
+                                     "sync_bits",
+                                     &new->sync_bits, cp_requester);
+       if (new->bitmap_size <= 0) {
+               LOG_ERROR("Failed to store sync_bits to checkpoint for node %u",
+                         new->requester);
+               free(new->clean_bits);
+               free(new);
+               return NULL;
+       }
+
+       r = push_state(entry->name.value, entry->luid,
+                      "recovering_region",
+                      &new->recovering_region, cp_requester);
+       if (r <= 0) {
+               LOG_ERROR("Failed to store recovering_region to checkpoint for node %u",
+                         new->requester);
+               free(new->sync_bits);
+               free(new->clean_bits);
+               free(new);
+               return NULL;
+       }
+       LOG_DBG("[%s] Checkpoint prepared for node %u:",
+               SHORT_UUID(new->uuid), new->requester);
+       LOG_DBG("  bitmap_size = %d", new->bitmap_size);
+
+       return new;
+}
+
+/*
+ * free_checkpoint
+ * @cp: the checkpoint_data struct to free
+ *
+ */
+static void free_checkpoint(struct checkpoint_data *cp)
+{
+       free(cp->recovering_region);
+       free(cp->sync_bits);
+       free(cp->clean_bits);
+       free(cp);
+}
+
+static int export_checkpoint(struct checkpoint_data *cp)
+{
+       SaCkptCheckpointCreationAttributesT attr;
+       SaCkptCheckpointHandleT h;
+       SaCkptSectionIdT section_id;
+       SaCkptSectionCreationAttributesT section_attr;
+       SaCkptCheckpointOpenFlagsT flags;
+       SaNameT name;
+       SaAisErrorT rv;
+       struct clog_request *rq;
+       int len, r = 0;
+       char buf[32];
+
+       LOG_DBG("Sending checkpointed data to %u", cp->requester);
+
+       len = snprintf((char *)(name.value), SA_MAX_NAME_LENGTH,
+                      "bitmaps_%s_%u", SHORT_UUID(cp->uuid), cp->requester);
+       name.length = (SaUint16T)len;
+
+       len = (int)strlen(cp->recovering_region) + 1;
+
+       attr.creationFlags = SA_CKPT_WR_ALL_REPLICAS;
+       attr.checkpointSize = cp->bitmap_size * 2 + len;
+
+       attr.retentionDuration = SA_TIME_MAX;
+       attr.maxSections = 4;      /* don't know why we need +1 */
+
+       attr.maxSectionSize = (cp->bitmap_size > len) ? cp->bitmap_size : len;
+       attr.maxSectionIdSize = 22;
+
+       flags = SA_CKPT_CHECKPOINT_READ |
+               SA_CKPT_CHECKPOINT_WRITE |
+               SA_CKPT_CHECKPOINT_CREATE;
+
+open_retry:
+       rv = saCkptCheckpointOpen(ckpt_handle, &name, &attr, flags, 0, &h);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("export_checkpoint: ckpt open retry");
+               usleep(1000);
+               goto open_retry;
+       }
+
+       if (rv == SA_AIS_ERR_EXIST) {
+               LOG_DBG("export_checkpoint: checkpoint already exists");
+               return -EEXIST;
+       }
+
+       if (rv != SA_AIS_OK) {
+               LOG_ERROR("[%s] Failed to open checkpoint for %u: %s",
+                         SHORT_UUID(cp->uuid), cp->requester,
+                         str_ais_error(rv));
+               return -EIO; /* FIXME: better error */
+       }
+
+       /*
+        * Add section for sync_bits
+        */
+       section_id.idLen = (SaUint16T)snprintf(buf, 32, "sync_bits");
+       section_id.id = (unsigned char *)buf;
+       section_attr.sectionId = &section_id;
+       section_attr.expirationTime = SA_TIME_END;
+
+sync_create_retry:
+       rv = saCkptSectionCreate(h, &section_attr,
+                                cp->sync_bits, cp->bitmap_size);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("Sync checkpoint section create retry");
+               usleep(1000);
+               goto sync_create_retry;
+       }
+
+       if (rv == SA_AIS_ERR_EXIST) {
+               LOG_DBG("Sync checkpoint section already exists");
+               saCkptCheckpointClose(h);
+               return -EEXIST;
+       }
+
+       if (rv != SA_AIS_OK) {
+               LOG_ERROR("Sync checkpoint section creation failed: %s",
+                         str_ais_error(rv));
+               saCkptCheckpointClose(h);
+               return -EIO; /* FIXME: better error */
+       }
+
+       /*
+        * Add section for clean_bits
+        */
+       section_id.idLen = snprintf(buf, 32, "clean_bits");
+       section_id.id = (unsigned char *)buf;
+       section_attr.sectionId = &section_id;
+       section_attr.expirationTime = SA_TIME_END;
+
+clean_create_retry:
+       rv = saCkptSectionCreate(h, &section_attr, cp->clean_bits, cp->bitmap_size);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("Clean checkpoint section create retry");
+               usleep(1000);
+               goto clean_create_retry;
+       }
+
+       if (rv == SA_AIS_ERR_EXIST) {
+               LOG_DBG("Clean checkpoint section already exists");
+               saCkptCheckpointClose(h);
+               return -EEXIST;
+       }
+
+       if (rv != SA_AIS_OK) {
+               LOG_ERROR("Clean checkpoint section creation failed: %s",
+                         str_ais_error(rv));
+               saCkptCheckpointClose(h);
+               return -EIO; /* FIXME: better error */
+       }
+
+       /*
+        * Add section for recovering_region
+        */
+       section_id.idLen = snprintf(buf, 32, "recovering_region");
+       section_id.id = (unsigned char *)buf;
+       section_attr.sectionId = &section_id;
+       section_attr.expirationTime = SA_TIME_END;
+
+rr_create_retry:
+       rv = saCkptSectionCreate(h, &section_attr, cp->recovering_region,
+                                strlen(cp->recovering_region) + 1);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("RR checkpoint section create retry");
+               usleep(1000);
+               goto rr_create_retry;
+       }
+
+       if (rv == SA_AIS_ERR_EXIST) {
+               LOG_DBG("RR checkpoint section already exists");
+               saCkptCheckpointClose(h);
+               return -EEXIST;
+       }
+
+       if (rv != SA_AIS_OK) {
+               LOG_ERROR("RR checkpoint section creation failed: %s",
+                         str_ais_error(rv));
+               saCkptCheckpointClose(h);
+               return -EIO; /* FIXME: better error */
+       }
+
+       LOG_DBG("export_checkpoint: closing checkpoint");
+       saCkptCheckpointClose(h);
+
+       rq = malloc(DM_ULOG_REQUEST_SIZE);
+       if (!rq) {
+               LOG_ERROR("export_checkpoint: Unable to allocate transfer structs");
+               return -ENOMEM;
+       }
+       memset(rq, 0, sizeof(*rq));
+
+       dm_list_init(&rq->u.list);
+       rq->u_rq.request_type = DM_ULOG_CHECKPOINT_READY;
+       rq->originator = cp->requester;  /* FIXME: hack to overload meaning of originator */
+       strncpy(rq->u_rq.uuid, cp->uuid, CPG_MAX_NAME_LENGTH);
+       rq->u_rq.seq = my_cluster_id;
+
+       r = cluster_send(rq);
+       if (r)
+               LOG_ERROR("Failed to send checkpoint ready notice: %s",
+                         strerror(-r));
+
+       free(rq);
+       return 0;
+}
+
+static int import_checkpoint(struct clog_cpg *entry, int no_read)
+{
+       int rtn = 0;
+       SaCkptCheckpointHandleT h;
+       SaCkptSectionIterationHandleT itr;
+       SaCkptSectionDescriptorT desc;
+       SaCkptIOVectorElementT iov;
+       SaNameT name;
+       SaAisErrorT rv;
+       char *bitmap = NULL;
+       int len;
+
+       bitmap = malloc(1024*1024);
+       if (!bitmap)
+               return -ENOMEM;
+
+       len = snprintf((char *)(name.value), SA_MAX_NAME_LENGTH, "bitmaps_%s_%u",
+                      SHORT_UUID(entry->name.value), my_cluster_id);
+       name.length = (SaUint16T)len;
+
+open_retry:
+       rv = saCkptCheckpointOpen(ckpt_handle, &name, NULL,
+                                 SA_CKPT_CHECKPOINT_READ, 0, &h);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("import_checkpoint: ckpt open retry");
+               usleep(1000);
+               goto open_retry;
+       }
+
+       if (rv != SA_AIS_OK) {
+               LOG_ERROR("[%s] Failed to open checkpoint: %s",
+                         SHORT_UUID(entry->name.value), str_ais_error(rv));
+               return -EIO; /* FIXME: better error */
+       }
+
+unlink_retry:
+       rv = saCkptCheckpointUnlink(ckpt_handle, &name);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("import_checkpoint: ckpt unlink retry");
+               usleep(1000);
+               goto unlink_retry;
+       }
+
+       if (no_read) {
+               LOG_DBG("Checkpoint for this log already received");
+               goto no_read;
+       }
+
+init_retry:
+       rv = saCkptSectionIterationInitialize(h, SA_CKPT_SECTIONS_ANY,
+                                             SA_TIME_END, &itr);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("import_checkpoint: sync create retry");
+               usleep(1000);
+               goto init_retry;
+       }
+
+       if (rv != SA_AIS_OK) {
+               LOG_ERROR("[%s] Sync checkpoint section creation failed: %s",
+                         SHORT_UUID(entry->name.value), str_ais_error(rv));
+               return -EIO; /* FIXME: better error */
+       }
+
+       len = 0;
+       while (1) {
+               rv = saCkptSectionIterationNext(itr, &desc);
+               if (rv == SA_AIS_OK)
+                       len++;
+               else if ((rv == SA_AIS_ERR_NO_SECTIONS) && len)
+                       break;
+               else if (rv != SA_AIS_ERR_TRY_AGAIN) {
+                       LOG_ERROR("saCkptSectionIterationNext failure: %d", rv);
+                       break;
+               }
+       }
+       saCkptSectionIterationFinalize(itr);
+       if (len != 3) {
+               LOG_ERROR("import_checkpoint: %d checkpoint sections found",
+                         len);
+               usleep(1000);
+               goto init_retry;
+       }
+       saCkptSectionIterationInitialize(h, SA_CKPT_SECTIONS_ANY,
+                                        SA_TIME_END, &itr);
+
+       while (1) {
+               rv = saCkptSectionIterationNext(itr, &desc);
+               if (rv == SA_AIS_ERR_NO_SECTIONS)
+                       break;
+
+               if (rv == SA_AIS_ERR_TRY_AGAIN) {
+                       LOG_ERROR("import_checkpoint: ckpt iternext retry");
+                       usleep(1000);
+                       continue;
+               }
+
+               if (rv != SA_AIS_OK) {
+                       LOG_ERROR("import_checkpoint: clean checkpoint section "
+                                 "creation failed: %s", str_ais_error(rv));
+                       rtn = -EIO; /* FIXME: better error */
+                       goto fail;
+               }
+
+               if (!desc.sectionSize) {
+                       LOG_ERROR("Checkpoint section empty");
+                       continue;
+               }
+
+               memset(bitmap, 0, sizeof(*bitmap));
+               iov.sectionId = desc.sectionId;
+               iov.dataBuffer = bitmap;
+               iov.dataSize = desc.sectionSize;
+               iov.dataOffset = 0;
+
+       read_retry:
+               rv = saCkptCheckpointRead(h, &iov, 1, NULL);
+               if (rv == SA_AIS_ERR_TRY_AGAIN) {
+                       LOG_ERROR("ckpt read retry");
+                       usleep(1000);
+                       goto read_retry;
+               }
+
+               if (rv != SA_AIS_OK) {
+                       LOG_ERROR("import_checkpoint: ckpt read error: %s",
+                                 str_ais_error(rv));
+                       rtn = -EIO; /* FIXME: better error */
+                       goto fail;
+               }
+
+               if (iov.readSize) {
+                       if (pull_state(entry->name.value, entry->luid,
+                                      (char *)desc.sectionId.id, bitmap,
+                                      iov.readSize)) {
+                               LOG_ERROR("Error loading state");
+                               rtn = -EIO;
+                               goto fail;
+                       }
+               } else {
+                       /* Need to request new checkpoint */
+                       rtn = -EAGAIN;
+                       goto fail;
+               }
+       }
+
+fail:
+       saCkptSectionIterationFinalize(itr);
+no_read:
+       saCkptCheckpointClose(h);
+
+       free(bitmap);
+       return rtn;
+}
+
+static void do_checkpoints(struct clog_cpg *entry, int leaving)
+{
+       struct checkpoint_data *cp;
+
+       for (cp = entry->checkpoint_list; cp;) {
+               /*
+                * FIXME: Check return code.  Could send failure
+                * notice in rq in export_checkpoint function
+                * by setting rq->error
+                */
+               switch (export_checkpoint(cp)) {
+               case -EEXIST:
+                       LOG_SPRINT(entry, "[%s] Checkpoint for %u already handled%s",
+                                  SHORT_UUID(entry->name.value), cp->requester,
+                                  (leaving) ? "(L)": "");
+                       LOG_COND(log_checkpoint,
+                                "[%s] Checkpoint for %u already handled%s",
+                                SHORT_UUID(entry->name.value), cp->requester,
+                                (leaving) ? "(L)": "");
+                       entry->checkpoint_list = cp->next;
+                       free_checkpoint(cp);
+                       cp = entry->checkpoint_list;
+                       break;
+               case 0:
+                       LOG_SPRINT(entry, "[%s] Checkpoint data available for node %u%s",
+                                  SHORT_UUID(entry->name.value), cp->requester,
+                                  (leaving) ? "(L)": "");
+                       LOG_COND(log_checkpoint,
+                                "[%s] Checkpoint data available for node %u%s",
+                                SHORT_UUID(entry->name.value), cp->requester,
+                                (leaving) ? "(L)": "");
+                       entry->checkpoint_list = cp->next;
+                       free_checkpoint(cp);
+                       cp = entry->checkpoint_list;
+                       break;
+               default:
+                       /* FIXME: Skipping will cause list corruption */
+                       LOG_ERROR("[%s] Failed to export checkpoint for %u%s",
+                                 SHORT_UUID(entry->name.value), cp->requester,
+                                 (leaving) ? "(L)": "");
+               }
+       }
+}
+
+static int resend_requests(struct clog_cpg *entry)
+{
+       int r = 0;
+       struct clog_request *rq, *n;
+
+       if (!entry->resend_requests || entry->delay)
+               return 0;
+
+       if (entry->state != VALID)
+               return 0;
+
+       entry->resend_requests = 0;
+
+       dm_list_iterate_items_gen_safe(rq, n, &entry->working_list, u.list) {
+               dm_list_del(&rq->u.list);
+
+               if (strcmp(entry->name.value, rq->u_rq.uuid)) {
+                       LOG_ERROR("[%s]  Stray request from another log (%s)",
+                                 SHORT_UUID(entry->name.value),
+                                 SHORT_UUID(rq->u_rq.uuid));
+                       free(rq);
+                       continue;
+               }
+
+               switch (rq->u_rq.request_type) {
+               case DM_ULOG_SET_REGION_SYNC:
+                       /*
+                        * Some requests simply do not need to be resent.
+                        * If it is a request that just changes log state,
+                        * then it doesn't need to be resent (everyone makes
+                        * updates).
+                        */
+                       LOG_COND(log_resend_requests,
+                                "[%s] Skipping resend of %s/#%u...",
+                                SHORT_UUID(entry->name.value),
+                                _RQ_TYPE(rq->u_rq.request_type),
+                                rq->u_rq.seq);
+                       LOG_SPRINT(entry, "###  No resend: [%s] %s/%u ###",
+                                  SHORT_UUID(entry->name.value),
+                                  _RQ_TYPE(rq->u_rq.request_type),
+                                  rq->u_rq.seq);
+
+                       rq->u_rq.data_size = 0;
+                       kernel_send(&rq->u_rq);
+                               
+                       break;
+                       
+               default:
+                       /*
+                        * If an action or a response is required, then
+                        * the request must be resent.
+                        */
+                       LOG_COND(log_resend_requests,
+                                "[%s] Resending %s(#%u) due to new server(%u)",
+                                SHORT_UUID(entry->name.value),
+                                _RQ_TYPE(rq->u_rq.request_type),
+                                rq->u_rq.seq, entry->lowest_id);
+                       LOG_SPRINT(entry, "***  Resending: [%s] %s/%u ***",
+                                  SHORT_UUID(entry->name.value),
+                                  _RQ_TYPE(rq->u_rq.request_type),
+                                  rq->u_rq.seq);
+                       r = cluster_send(rq);
+                       if (r < 0)
+                               LOG_ERROR("Failed resend");
+               }
+               free(rq);
+       }
+
+       return r;
+}
+
+static int do_cluster_work(void *data __attribute__((unused)))
+{
+       int r = SA_AIS_OK;
+       struct clog_cpg *entry, *tmp;
+
+       dm_list_iterate_items_safe(entry, tmp, &clog_cpg_list) {
+               r = cpg_dispatch(entry->handle, CPG_DISPATCH_ALL);
+               if (r != SA_AIS_OK)
+                       LOG_ERROR("cpg_dispatch failed: %s", str_ais_error(r));
+
+               if (entry->free_me) {
+                       free(entry);
+                       continue;
+               }
+               do_checkpoints(entry, 0);
+
+               resend_requests(entry);
+       }
+
+       return (r == SA_AIS_OK) ? 0 : -1;  /* FIXME: good error number? */
+}
+
+static int flush_startup_list(struct clog_cpg *entry)
+{
+       int r = 0;
+       int i_was_server;
+       struct clog_request *rq, *n;
+       struct checkpoint_data *new;
+
+       dm_list_iterate_items_gen_safe(rq, n, &entry->startup_list, u.list) {
+               dm_list_del(&rq->u.list);
+
+               if (rq->u_rq.request_type == DM_ULOG_MEMBER_JOIN) {
+                       new = prepare_checkpoint(entry, rq->originator);
+                       if (!new) {
+                               /*
+                                * FIXME: Need better error handling.  Other nodes
+                                * will be trying to send the checkpoint too, and we
+                                * must continue processing the list; so report error
+                                * but continue.
+                                */
+                               LOG_ERROR("Failed to prepare checkpoint for %u!!!",
+                                         rq->originator);
+                               free(rq);
+                               continue;
+                       }
+                       LOG_SPRINT(entry, "[%s] Checkpoint prepared for %u",
+                                  SHORT_UUID(entry->name.value), rq->originator);
+                       LOG_COND(log_checkpoint, "[%s] Checkpoint prepared for %u",
+                                SHORT_UUID(entry->name.value), rq->originator);
+                       new->next = entry->checkpoint_list;
+                       entry->checkpoint_list = new;
+               } else {
+                       LOG_DBG("[%s] Processing delayed request: %s",
+                               SHORT_UUID(rq->u_rq.uuid),
+                               _RQ_TYPE(rq->u_rq.request_type));
+                       i_was_server = (rq->pit_server == my_cluster_id) ? 1 : 0;
+                       r = handle_cluster_request(entry, rq, i_was_server);
+
+                       if (r)
+                               /*
+                                * FIXME: If we error out here, we will never get
+                                * another opportunity to retry these requests
+                                */
+                               LOG_ERROR("Error while processing delayed CPG message");
+               }
+               free(rq);
+       }
+
+       return 0;
+}
+
+static void cpg_message_callback(cpg_handle_t handle, const struct cpg_name *gname __attribute__((unused)),
+                                uint32_t nodeid, uint32_t pid __attribute__((unused)),
+                                void *msg, size_t msg_len)
+{
+       int i;
+       int r = 0;
+       int i_am_server;
+       int response = 0;
+       struct clog_request *rq = msg;
+       struct clog_request *tmp_rq;
+       struct clog_cpg *match;
+
+       if (clog_request_from_network(rq, msg_len) < 0)
+               /* Error message comes from 'clog_request_from_network' */
+               return;
+
+       match = find_clog_cpg(handle);
+       if (!match) {
+               LOG_ERROR("Unable to find clog_cpg for cluster message");
+               return;
+       }
+
+       if ((nodeid == my_cluster_id) &&
+           !(rq->u_rq.request_type & DM_ULOG_RESPONSE) &&
+           (rq->u_rq.request_type != DM_ULOG_RESUME) &&
+           (rq->u_rq.request_type != DM_ULOG_CLEAR_REGION) &&
+           (rq->u_rq.request_type != DM_ULOG_CHECKPOINT_READY)) {
+               tmp_rq = malloc(DM_ULOG_REQUEST_SIZE);
+               if (!tmp_rq) {
+                       /*
+                        * FIXME: It may be possible to continue... but we
+                        * would not be able to resend any messages that might
+                        * be necessary during membership changes
+                        */
+                       LOG_ERROR("[%s] Unable to record request: -ENOMEM",
+                                 SHORT_UUID(rq->u_rq.uuid));
+                       return;
+               }
+               memcpy(tmp_rq, rq, sizeof(*rq) + rq->u_rq.data_size);
+               dm_list_init(&tmp_rq->u.list);
+               dm_list_add( &match->working_list, &tmp_rq->u.list);
+       }
+
+       if (rq->u_rq.request_type == DM_ULOG_POSTSUSPEND) {
+               /*
+                * If the server (lowest_id) indicates it is leaving,
+                * then we must resend any outstanding requests.  However,
+                * we do not want to resend them if the next server in
+                * line is in the process of leaving.
+                */
+               if (nodeid == my_cluster_id) {
+                       LOG_COND(log_resend_requests, "[%s] I am leaving.1.....",
+                                SHORT_UUID(rq->u_rq.uuid));
+               } else {
+                       if (nodeid < my_cluster_id) {
+                               if (nodeid == match->lowest_id) {
+                                       match->resend_requests = 1;
+                                       LOG_COND(log_resend_requests, "[%s] %u is leaving, resend required%s",
+                                                SHORT_UUID(rq->u_rq.uuid), nodeid,
+                                                (dm_list_empty(&match->working_list)) ? " -- working_list empty": "");
+
+                                       dm_list_iterate_items_gen(tmp_rq, &match->working_list, u.list)
+                                               LOG_COND(log_resend_requests,
+                                                        "[%s]                %s/%u",
+                                                        SHORT_UUID(tmp_rq->u_rq.uuid),
+                                                        _RQ_TYPE(tmp_rq->u_rq.request_type),
+                                                        tmp_rq->u_rq.seq);
+                               }
+
+                               match->delay++;
+                               LOG_COND(log_resend_requests, "[%s] %u is leaving, delay = %d",
+                                        SHORT_UUID(rq->u_rq.uuid), nodeid, match->delay);
+                       }
+                       rq->originator = nodeid; /* don't really need this, but nice for debug */
+                       goto out;
+               }
+       }
+
+       /*
+        * We can receive messages after we do a cpg_leave but before we
+        * get our config callback.  However, since we can't respond after
+        * leaving, we simply return.
+        */
+       if (match->state == LEAVING)
+               return;
+
+       i_am_server = (my_cluster_id == match->lowest_id) ? 1 : 0;
+
+       if (rq->u_rq.request_type == DM_ULOG_CHECKPOINT_READY) {
+               if (my_cluster_id == rq->originator) {
+                       /* Redundant checkpoints ignored if match->valid */
+                       LOG_SPRINT(match, "[%s] CHECKPOINT_READY notification from %u",
+                                  SHORT_UUID(rq->u_rq.uuid), nodeid);
+                       if (import_checkpoint(match, (match->state != INVALID))) {
+                               LOG_SPRINT(match,
+                                          "[%s] Failed to import checkpoint from %u",
+                                          SHORT_UUID(rq->u_rq.uuid), nodeid);
+                               LOG_ERROR("[%s] Failed to import checkpoint from %u",
+                                         SHORT_UUID(rq->u_rq.uuid), nodeid);
+                               kill(getpid(), SIGUSR1);
+                               /* Could we retry? */
+                               goto out;
+                       } else if (match->state == INVALID) {
+                               LOG_SPRINT(match,
+                                          "[%s] Checkpoint data received from %u.  Log is now valid",
+                                          SHORT_UUID(match->name.value), nodeid);
+                               LOG_COND(log_checkpoint,
+                                        "[%s] Checkpoint data received from %u.  Log is now valid",
+                                        SHORT_UUID(match->name.value), nodeid);
+                               match->state = VALID;
+
+                               flush_startup_list(match);
+                       } else {
+                               LOG_SPRINT(match,
+                                          "[%s] Redundant checkpoint from %u ignored.",
+                                          SHORT_UUID(rq->u_rq.uuid), nodeid);
+                       }
+               }
+               goto out;
+       }
+
+       if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
+               response = 1;
+               r = handle_cluster_response(match, rq);
+       } else {
+               rq->originator = nodeid;
+
+               if (match->state == LEAVING) {
+                       LOG_ERROR("[%s]  Ignoring %s from %u.  Reason: I'm leaving",
+                                 SHORT_UUID(rq->u_rq.uuid), _RQ_TYPE(rq->u_rq.request_type),
+                                 rq->originator);
+                       goto out;
+               }
+
+               if (match->state == INVALID) {
+                       LOG_DBG("Log not valid yet, storing request");
+                       tmp_rq = malloc(DM_ULOG_REQUEST_SIZE);
+                       if (!tmp_rq) {
+                               LOG_ERROR("cpg_message_callback:  Unable to"
+                                         " allocate transfer structs");
+                               r = -ENOMEM; /* FIXME: Better error #? */
+                               goto out;
+                       }
+
+                       memcpy(tmp_rq, rq, sizeof(*rq) + rq->u_rq.data_size);
+                       tmp_rq->pit_server = match->lowest_id;
+                       dm_list_init(&tmp_rq->u.list);
+                       dm_list_add(&match->startup_list, &tmp_rq->u.list);
+                       goto out;
+               }
+
+               r = handle_cluster_request(match, rq, i_am_server);
+       }
+
+       /*
+        * If the log is now valid, we can queue the checkpoints
+        */
+       for (i = match->checkpoints_needed; i; ) {
+               struct checkpoint_data *new;
+
+               if (log_get_state(&rq->u_rq) != LOG_RESUMED) {
+                       LOG_DBG("[%s] Withholding checkpoints until log is valid (%s from %u)",
+                               SHORT_UUID(rq->u_rq.uuid), _RQ_TYPE(rq->u_rq.request_type), nodeid);
+                       break;
+               }
+
+               i--;
+               new = prepare_checkpoint(match, match->checkpoint_requesters[i]);
+               if (!new) {
+                       /* FIXME: Need better error handling */
+                       LOG_ERROR("[%s] Failed to prepare checkpoint for %u!!!",
+                                 SHORT_UUID(rq->u_rq.uuid), match->checkpoint_requesters[i]);
+                       break;
+               }
+               LOG_SPRINT(match, "[%s] Checkpoint prepared for %u* (%s)",
+                          SHORT_UUID(rq->u_rq.uuid), match->checkpoint_requesters[i],
+                          (log_get_state(&rq->u_rq) != LOG_RESUMED)? "LOG_RESUMED": "LOG_SUSPENDED");
+               LOG_COND(log_checkpoint, "[%s] Checkpoint prepared for %u*",
+                        SHORT_UUID(rq->u_rq.uuid), match->checkpoint_requesters[i]);
+               match->checkpoints_needed--;
+
+               new->next = match->checkpoint_list;
+               match->checkpoint_list = new;
+       }
+
+out:
+       /* nothing happens after this point.  It is just for debugging */
+       if (r) {
+               LOG_ERROR("[%s] Error while processing CPG message, %s: %s",
+                         SHORT_UUID(rq->u_rq.uuid),
+                         _RQ_TYPE(rq->u_rq.request_type & ~DM_ULOG_RESPONSE),
+                         strerror(-r));
+               LOG_ERROR("[%s]    Response  : %s", SHORT_UUID(rq->u_rq.uuid),
+                         (response) ? "YES" : "NO");
+               LOG_ERROR("[%s]    Originator: %u",
+                         SHORT_UUID(rq->u_rq.uuid), rq->originator);
+               if (response)
+                       LOG_ERROR("[%s]    Responder : %u",
+                                 SHORT_UUID(rq->u_rq.uuid), nodeid);
+
+               LOG_ERROR("HISTORY::");
+               for (i = 0; i < DEBUGGING_HISTORY; i++) {
+                       match->idx++;
+                       match->idx = match->idx % DEBUGGING_HISTORY;
+                       if (match->debugging[match->idx][0] == '\0')
+                               continue;
+                       LOG_ERROR("%d:%d) %s", i, match->idx,
+                                 match->debugging[match->idx]);
+               }
+       } else if (!(rq->u_rq.request_type & DM_ULOG_RESPONSE) ||
+                  (rq->originator == my_cluster_id)) {
+               if (!response)
+                       LOG_SPRINT(match, "SEQ#=%u, UUID=%s, TYPE=%s, ORIG=%u, RESP=%s",
+                                  rq->u_rq.seq, SHORT_UUID(rq->u_rq.uuid),
+                                  _RQ_TYPE(rq->u_rq.request_type),
+                                  rq->originator, (response) ? "YES" : "NO");
+               else
+                       LOG_SPRINT(match, "SEQ#=%u, UUID=%s, TYPE=%s, ORIG=%u, RESP=%s, RSPR=%u",
+                                  rq->u_rq.seq, SHORT_UUID(rq->u_rq.uuid),
+                                  _RQ_TYPE(rq->u_rq.request_type),
+                                  rq->originator, (response) ? "YES" : "NO",
+                                  nodeid);
+       }
+}
+
+static void cpg_join_callback(struct clog_cpg *match,
+                             const struct cpg_address *joined,
+                             const struct cpg_address *member_list,
+                             size_t member_list_entries)
+{
+       unsigned i;
+       uint32_t my_pid = (uint32_t)getpid();
+       uint32_t lowest = match->lowest_id;
+       struct clog_request *rq;
+       char dbuf[32];
+
+       /* Assign my_cluster_id */
+       if ((my_cluster_id == 0xDEAD) && (joined->pid == my_pid))
+               my_cluster_id = joined->nodeid;
+
+       /* Am I the very first to join? */
+       if (member_list_entries == 1) {
+               match->lowest_id = joined->nodeid;
+               match->state = VALID;
+       }
+
+       /* If I am part of the joining list, I do not send checkpoints */
+       if (joined->nodeid == my_cluster_id)
+               goto out;
+
+       memset(dbuf, 0, sizeof(dbuf));
+       for (i = 0; i < member_list_entries - 1; i++)
+               sprintf(dbuf+strlen(dbuf), "%u-", member_list[i].nodeid);
+       sprintf(dbuf+strlen(dbuf), "(%u)", joined->nodeid);
+       LOG_COND(log_checkpoint, "[%s] Joining node, %u needs checkpoint [%s]",
+                SHORT_UUID(match->name.value), joined->nodeid, dbuf);
+
+       /*
+        * FIXME: remove checkpoint_requesters/checkpoints_needed, and use
+        * the startup_list interface exclusively
+        */
+       if (dm_list_empty(&match->startup_list) && (match->state == VALID) &&
+           (match->checkpoints_needed < MAX_CHECKPOINT_REQUESTERS)) {
+               match->checkpoint_requesters[match->checkpoints_needed++] = joined->nodeid;
+               goto out;
+       }
+
+       rq = malloc(DM_ULOG_REQUEST_SIZE);
+       if (!rq) {
+               LOG_ERROR("cpg_config_callback: "
+                         "Unable to allocate transfer structs");
+               LOG_ERROR("cpg_config_callback: "
+                         "Unable to perform checkpoint");
+               goto out;
+       }
+       rq->u_rq.request_type = DM_ULOG_MEMBER_JOIN;
+       rq->originator = joined->nodeid;
+       dm_list_init(&rq->u.list);
+       dm_list_add(&match->startup_list, &rq->u.list);
+
+out:
+       /* Find the lowest_id, i.e. the server */
+       match->lowest_id = member_list[0].nodeid;
+       for (i = 0; i < member_list_entries; i++)
+               if (match->lowest_id > member_list[i].nodeid)
+                       match->lowest_id = member_list[i].nodeid;
+
+       if (lowest == 0xDEAD)
+               LOG_COND(log_membership_change, "[%s]  Server change <none> -> %u (%u %s)",
+                        SHORT_UUID(match->name.value), match->lowest_id,
+                        joined->nodeid, (member_list_entries == 1) ?
+                        "is first to join" : "joined");
+       else if (lowest != match->lowest_id)
+               LOG_COND(log_membership_change, "[%s]  Server change %u -> %u (%u joined)",
+                        SHORT_UUID(match->name.value), lowest,
+                        match->lowest_id, joined->nodeid);
+       else
+               LOG_COND(log_membership_change, "[%s]  Server unchanged at %u (%u joined)",
+                        SHORT_UUID(match->name.value),
+                        lowest, joined->nodeid);
+       LOG_SPRINT(match, "+++  UUID=%s  %u join  +++",
+                  SHORT_UUID(match->name.value), joined->nodeid);
+}
+
+static void cpg_leave_callback(struct clog_cpg *match,
+                              const struct cpg_address *left,
+                              const struct cpg_address *member_list,
+                              size_t member_list_entries)
+{
+       unsigned i;
+       int j, fd;
+       uint32_t lowest = match->lowest_id;
+       struct clog_request *rq, *n;
+       struct checkpoint_data *p_cp, *c_cp;
+
+       LOG_SPRINT(match, "---  UUID=%s  %u left  ---",
+                  SHORT_UUID(match->name.value), left->nodeid);
+
+       /* Am I leaving? */
+       if (my_cluster_id == left->nodeid) {
+               LOG_DBG("Finalizing leave...");
+               dm_list_del(&match->list);
+
+               cpg_fd_get(match->handle, &fd);
+               links_unregister(fd);
+
+               cluster_postsuspend(match->name.value, match->luid);
+
+               dm_list_iterate_items_gen_safe(rq, n, &match->working_list, u.list) {
+                       dm_list_del(&rq->u.list);
+
+                       if (rq->u_rq.request_type == DM_ULOG_POSTSUSPEND)
+                               kernel_send(&rq->u_rq);
+                       free(rq);
+               }
+
+               cpg_finalize(match->handle);
+
+               match->free_me = 1;
+               match->lowest_id = 0xDEAD;
+               match->state = INVALID;
+       }                       
+
+       /* Remove any pending checkpoints for the leaving node. */
+       for (p_cp = NULL, c_cp = match->checkpoint_list;
+            c_cp && (c_cp->requester != left->nodeid);
+            p_cp = c_cp, c_cp = c_cp->next);
+       if (c_cp) {
+               if (p_cp)
+                       p_cp->next = c_cp->next;
+               else
+                       match->checkpoint_list = c_cp->next;
+
+               LOG_COND(log_checkpoint,
+                        "[%s] Removing pending checkpoint (%u is leaving)",
+                        SHORT_UUID(match->name.value), left->nodeid);
+               free_checkpoint(c_cp);
+       }
+       dm_list_iterate_items_gen_safe(rq, n, &match->startup_list, u.list) {
+               if ((rq->u_rq.request_type == DM_ULOG_MEMBER_JOIN) &&
+                   (rq->originator == left->nodeid)) {
+                       LOG_COND(log_checkpoint,
+                                "[%s] Removing pending ckpt from startup list (%u is leaving)",
+                                SHORT_UUID(match->name.value), left->nodeid);
+                       dm_list_del(&rq->u.list);
+                       free(rq);
+               }
+       }
+       for (i = 0, j = 0; i < match->checkpoints_needed; i++, j++) {
+               match->checkpoint_requesters[j] = match->checkpoint_requesters[i];
+               if (match->checkpoint_requesters[i] == left->nodeid) {
+                       LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)",
+                                 SHORT_UUID(match->name.value), left->nodeid);
+                       j--;
+               }
+       }
+       match->checkpoints_needed = j;
+
+       if (left->nodeid < my_cluster_id) {
+               match->delay = (match->delay > 0) ? match->delay - 1 : 0;
+               if (!match->delay && dm_list_empty(&match->working_list))
+                       match->resend_requests = 0;
+               LOG_COND(log_resend_requests, "[%s] %u has left, delay = %d%s",
+                        SHORT_UUID(match->name.value), left->nodeid,
+                        match->delay, (dm_list_empty(&match->working_list)) ?
+                        " -- working_list empty": "");
+       }
+
+       /* Find the lowest_id, i.e. the server */
+       if (!member_list_entries) {
+               match->lowest_id = 0xDEAD;
+               LOG_COND(log_membership_change, "[%s]  Server change %u -> <none> "
+                        "(%u is last to leave)",
+                        SHORT_UUID(match->name.value), left->nodeid,
+                        left->nodeid);
+               return;
+       }
+               
+       match->lowest_id = member_list[0].nodeid;
+       for (i = 0; i < member_list_entries; i++)
+               if (match->lowest_id > member_list[i].nodeid)
+                       match->lowest_id = member_list[i].nodeid;
+
+       if (lowest != match->lowest_id) {
+               LOG_COND(log_membership_change, "[%s]  Server change %u -> %u (%u left)",
+                        SHORT_UUID(match->name.value), lowest,
+                        match->lowest_id, left->nodeid);
+       } else
+               LOG_COND(log_membership_change, "[%s]  Server unchanged at %u (%u left)",
+                        SHORT_UUID(match->name.value), lowest, left->nodeid);
+
+       if ((match->state == INVALID) && !match->free_me) {
+               /*
+                * If all CPG members are waiting for checkpoints and they
+                * are all present in my startup_list, then I was the first to
+                * join and I must assume control.
+                *
+                * We do not normally end up here, but if there was a quick
+                * 'resume -> suspend -> resume' across the cluster, we may
+                * have initially thought we were not the first to join because
+                * of the presence of out-going (and unable to respond) members.
+                */
+
+               i = 1; /* We do not have a DM_ULOG_MEMBER_JOIN entry of our own */
+               dm_list_iterate_items_gen(rq, &match->startup_list, u.list)
+                       if (rq->u_rq.request_type == DM_ULOG_MEMBER_JOIN)
+                               i++;
+
+               if (i == member_list_entries) {
+                       /* 
+                        * Last node who could have given me a checkpoint just left.
+                        * Setting log state to VALID and acting as 'first join'.
+                        */
+                       match->state = VALID;
+                       flush_startup_list(match);
+               }
+       }
+}
+
+static void cpg_config_callback(cpg_handle_t handle, const struct cpg_name *gname __attribute__((unused)),
+                               const struct cpg_address *member_list,
+                               size_t member_list_entries,
+                               const struct cpg_address *left_list,
+                               size_t left_list_entries,
+                               const struct cpg_address *joined_list,
+                               size_t joined_list_entries)
+{
+       struct clog_cpg *match;
+       int found = 0;
+
+       dm_list_iterate_items(match, &clog_cpg_list)
+               if (match->handle == handle) {
+                       found = 1;
+                       break;
+               }
+
+       if (!found) {
+               LOG_ERROR("Unable to find match for CPG config callback");
+               return;
+       }
+
+       if ((joined_list_entries + left_list_entries) > 1)
+               LOG_ERROR("[%s]  More than one node joining/leaving",
+                         SHORT_UUID(match->name.value));
+
+       if (joined_list_entries)
+               cpg_join_callback(match, joined_list,
+                                 member_list, member_list_entries);
+       else
+               cpg_leave_callback(match, left_list,
+                                  member_list, member_list_entries);
+}
+
+cpg_callbacks_t cpg_callbacks = {
+       .cpg_deliver_fn = cpg_message_callback,
+       .cpg_confchg_fn = cpg_config_callback,
+};
+
+/*
+ * remove_checkpoint
+ * @entry
+ *
+ * Returns: 1 if checkpoint removed, 0 if no checkpoints, -EXXX on error
+ */
+static int remove_checkpoint(struct clog_cpg *entry)
+{
+       int len;
+       SaNameT name;
+       SaAisErrorT rv;
+       SaCkptCheckpointHandleT h;
+
+       len = snprintf((char *)(name.value), SA_MAX_NAME_LENGTH, "bitmaps_%s_%u",
+                       SHORT_UUID(entry->name.value), my_cluster_id);
+       name.length = len;
+
+open_retry:
+       rv = saCkptCheckpointOpen(ckpt_handle, &name, NULL,
+                                  SA_CKPT_CHECKPOINT_READ, 0, &h);
+       if (rv == SA_AIS_ERR_TRY_AGAIN) {
+               LOG_ERROR("abort_startup: ckpt open retry");
+                usleep(1000);
+                goto open_retry;
+        }
+
+       if (rv != SA_AIS_OK)
+                return 0;
+
+       LOG_DBG("[%s]  Removing checkpoint", SHORT_UUID(entry->name.value));
+unlink_retry:
+        rv = saCkptCheckpointUnlink(ckpt_handle, &name);
+        if (rv == SA_AIS_ERR_TRY_AGAIN) {
+                LOG_ERROR("abort_startup: ckpt unlink retry");
+                usleep(1000);
+                goto unlink_retry;
+        }
+       
+       if (rv != SA_AIS_OK) {
+                LOG_ERROR("[%s] Failed to unlink checkpoint: %s",
+                          SHORT_UUID(entry->name.value), str_ais_error(rv));
+                return -EIO;
+        }
+
+       saCkptCheckpointClose(h);
+
+       return 1;
+}
+
+int create_cluster_cpg(char *uuid, uint64_t luid)
+{
+       int r;
+       size_t size;
+       struct clog_cpg *new = NULL;
+       struct clog_cpg *tmp;
+
+       dm_list_iterate_items(tmp, &clog_cpg_list)
+               if (!strncmp(tmp->name.value, uuid, CPG_MAX_NAME_LENGTH)) {
+                       LOG_ERROR("Log entry already exists: %s", uuid);
+                       return -EEXIST;
+               }
+
+       new = malloc(sizeof(*new));
+       if (!new) {
+               LOG_ERROR("Unable to allocate memory for clog_cpg");
+               return -ENOMEM;
+       }
+       memset(new, 0, sizeof(*new));
+       dm_list_init(&new->list);
+       new->lowest_id = 0xDEAD;
+       dm_list_init(&new->startup_list);
+       dm_list_init(&new->working_list);
+
+       size = ((strlen(uuid) + 1) > CPG_MAX_NAME_LENGTH) ?
+               CPG_MAX_NAME_LENGTH : (strlen(uuid) + 1);
+       strncpy(new->name.value, uuid, size);
+       new->name.length = (uint32_t)size;
+       new->luid = luid;
+
+       /*
+        * Ensure there are no stale checkpoints around before we join
+        */
+       if (remove_checkpoint(new) == 1)
+               LOG_COND(log_checkpoint,
+                        "[%s]  Removing checkpoints left from previous session",
+                        SHORT_UUID(new->name.value));
+
+       r = cpg_initialize(&new->handle, &cpg_callbacks);
+       if (r != SA_AIS_OK) {
+               LOG_ERROR("cpg_initialize failed:  Cannot join cluster");
+               free(new);
+               return -EPERM;
+       }
+
+       r = cpg_join(new->handle, &new->name);
+       if (r != SA_AIS_OK) {
+               LOG_ERROR("cpg_join failed:  Cannot join cluster");
+               free(new);
+               return -EPERM;
+       }
+
+       new->cpg_state = VALID;
+       dm_list_add(&clog_cpg_list, &new->list);
+       LOG_DBG("New   handle: %llu", (unsigned long long)new->handle);
+       LOG_DBG("New   name: %s", new->name.value);
+
+       /* FIXME: better variable */
+       cpg_fd_get(new->handle, &r);
+       links_register(r, "cluster", do_cluster_work, NULL);
+
+       return 0;
+}
+
+static void abort_startup(struct clog_cpg *del)
+{
+       struct clog_request *rq, *n;
+
+       LOG_DBG("[%s]  CPG teardown before checkpoint received",
+               SHORT_UUID(del->name.value));
+
+       dm_list_iterate_items_gen_safe(rq, n, &del->startup_list, u.list) {
+               dm_list_del(&rq->u.list);
+
+               LOG_DBG("[%s]  Ignoring request from %u: %s",
+                       SHORT_UUID(del->name.value), rq->originator,
+                       _RQ_TYPE(rq->u_rq.request_type));
+               free(rq);
+       }
+
+       remove_checkpoint(del);
+}
+
+static int _destroy_cluster_cpg(struct clog_cpg *del)
+{
+       int r;
+       int state;
+       
+       LOG_COND(log_resend_requests, "[%s] I am leaving.2.....",
+                SHORT_UUID(del->name.value));
+
+       /*
+        * We must send any left over checkpoints before
+        * leaving.  If we don't, an incoming node could
+        * be stuck with no checkpoint and stall.
+        do_checkpoints(del); --- THIS COULD BE CAUSING OUR PROBLEMS:
+
+        - Incoming node deletes old checkpoints before joining
+        - A stale checkpoint is issued here by leaving node
+        - (leaving node leaves)
+        - Incoming node joins cluster and finds stale checkpoint.
+        - (leaving node leaves - option 2)
+       */
+       do_checkpoints(del, 1);
+
+       state = del->state;
+
+       del->cpg_state = INVALID;
+       del->state = LEAVING;
+
+       /*
+        * If the state is VALID, we might be processing the
+        * startup list.  If so, we certainly don't want to
+        * clear the startup_list here by calling abort_startup
+        */
+       if (!dm_list_empty(&del->startup_list) && (state != VALID))
+               abort_startup(del);
+
+       r = cpg_leave(del->handle, &del->name);
+       if (r != CPG_OK)
+               LOG_ERROR("Error leaving CPG!");
+       return 0;
+}
+
+int destroy_cluster_cpg(char *uuid)
+{
+       struct clog_cpg *del, *tmp;
+
+       dm_list_iterate_items_safe(del, tmp, &clog_cpg_list)
+               if (!strncmp(del->name.value, uuid, CPG_MAX_NAME_LENGTH))
+                       _destroy_cluster_cpg(del);
+
+       return 0;
+}
+
+int init_cluster(void)
+{
+       SaAisErrorT rv;
+
+       dm_list_init(&clog_cpg_list);
+       rv = saCkptInitialize(&ckpt_handle, &callbacks, &version);
+
+       if (rv != SA_AIS_OK)
+               return EXIT_CLUSTER_CKPT_INIT;
+
+       return 0;
+}
+
+void cleanup_cluster(void)
+{
+       SaAisErrorT err;
+
+       err = saCkptFinalize(ckpt_handle);
+       if (err != SA_AIS_OK)
+               LOG_ERROR("Failed to finalize checkpoint handle");
+}
+
+void cluster_debug(void)
+{
+       struct checkpoint_data *cp;
+       struct clog_cpg *entry;
+       struct clog_request *rq;
+       int i;
+
+       LOG_ERROR("");
+       LOG_ERROR("CLUSTER COMPONENT DEBUGGING::");
+       dm_list_iterate_items(entry, &clog_cpg_list) {
+               LOG_ERROR("%s::", SHORT_UUID(entry->name.value));
+               LOG_ERROR("  lowest_id         : %u", entry->lowest_id);
+               LOG_ERROR("  state             : %s", (entry->state == INVALID) ?
+                         "INVALID" : (entry->state == VALID) ? "VALID" :
+                         (entry->state == LEAVING) ? "LEAVING" : "UNKNOWN");
+               LOG_ERROR("  cpg_state         : %d", entry->cpg_state);
+               LOG_ERROR("  free_me           : %d", entry->free_me);
+               LOG_ERROR("  delay             : %d", entry->delay);
+               LOG_ERROR("  resend_requests   : %d", entry->resend_requests);
+               LOG_ERROR("  checkpoints_needed: %d", entry->checkpoints_needed);
+               for (i = 0, cp = entry->checkpoint_list;
+                    i < MAX_CHECKPOINT_REQUESTERS; i++)
+                       if (cp)
+                               cp = cp->next;
+                       else
+                               break;
+               LOG_ERROR("  CKPTs waiting     : %d", i);
+               LOG_ERROR("  Working list:");
+               dm_list_iterate_items_gen(rq, &entry->working_list, u.list)
+                       LOG_ERROR("  %s/%u", _RQ_TYPE(rq->u_rq.request_type),
+                                 rq->u_rq.seq);
+
+               LOG_ERROR("  Startup list:");
+               dm_list_iterate_items_gen(rq, &entry->startup_list, u.list)
+                       LOG_ERROR("  %s/%u", _RQ_TYPE(rq->u_rq.request_type),
+                                 rq->u_rq.seq);
+
+               LOG_ERROR("Command History:");
+               for (i = 0; i < DEBUGGING_HISTORY; i++) {
+                       entry->idx++;
+                       entry->idx = entry->idx % DEBUGGING_HISTORY;
+                       if (entry->debugging[entry->idx][0] == '\0')
+                               continue;
+                       LOG_ERROR("%d:%d) %s", i, entry->idx,
+                                 entry->debugging[entry->idx]);
+               }
+       }
+}
diff --git a/daemons/cmirrord/cluster.h b/daemons/cmirrord/cluster.h
new file mode 100644 (file)
index 0000000..50d87dd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_CLOG_CLUSTER_H
+#define _LVM_CLOG_CLUSTER_H
+
+#include "dm-log-userspace.h"
+#include "libdevmapper.h"
+
+#define DM_ULOG_RESPONSE 0x1000U /* in last byte of 32-bit value */
+#define DM_ULOG_CHECKPOINT_READY 21
+#define DM_ULOG_MEMBER_JOIN      22
+
+/*
+ * There is other information in addition to what can
+ * be found in the dm_ulog_request structure that we
+ * need for processing.  'clog_request' is the wrapping
+ * structure we use to make the additional fields
+ * available.
+ */
+struct clog_request {
+       /*
+        * If we don't use a union, the structure size will
+        * vary between 32-bit and 64-bit machines.  So, we
+        * pack two 64-bit version numbers in there to force
+        * the size of the structure to be the same.
+        *
+        * The two version numbers also help us with endian
+        * issues.  The first is always little endian, while
+        * the second is in native format of the sending
+        * machine.  If the two are equal, there is no need
+        * to do endian conversions.
+        */
+       union {
+               uint64_t version[2]; /* LE version and native version */
+               struct dm_list list;
+       } u;
+
+       /*
+        * 'originator' is the machine from which the requests
+        * was made.
+        */
+       uint32_t originator;
+
+       /*
+        * 'pit_server' is the "point-in-time" server for the
+        * request.  (I.e.  The machine that was the server at
+        * the time the request was issued - only important during
+        * startup.
+        */
+       uint32_t pit_server;
+
+       /*
+        * The request from the kernel that is being processed
+        */
+       struct dm_ulog_request u_rq;
+};
+
+int init_cluster(void);
+void cleanup_cluster(void);
+void cluster_debug(void);
+
+int create_cluster_cpg(char *uuid, uint64_t luid);
+int destroy_cluster_cpg(char *uuid);
+
+int cluster_send(struct clog_request *rq);
+
+#endif /* _LVM_CLOG_CLUSTER_H */
diff --git a/daemons/cmirrord/common.h b/daemons/cmirrord/common.h
new file mode 100644 (file)
index 0000000..c71c46b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_CLOG_COMMON_H
+#define _LVM_CLOG_COMMON_H
+
+/*
+ * If there are problems when forking off to become a daemon,
+ * the child will exist with one of these codes.  This allows
+ * the parent to know the reason for the failure and print it
+ * to the launching terminal.
+ *
+ * #define EXIT_SUCCESS 0 (from stdlib.h)
+ * #define EXIT_FAILURE 1 (from stdlib.h)
+ */
+#define EXIT_LOCKFILE              2
+#define EXIT_KERNEL_SOCKET         3 /* Failed netlink socket create */
+#define EXIT_KERNEL_BIND           4
+#define EXIT_KERNEL_SETSOCKOPT     5
+#define EXIT_CLUSTER_CKPT_INIT     6 /* Failed to init checkpoint */
+#define EXIT_QUEUE_NOMEM           7
+
+#define DM_ULOG_REQUEST_SIZE 1024
+
+#endif /* _LVM_CLOG_COMMON_H */
diff --git a/daemons/cmirrord/compat.c b/daemons/cmirrord/compat.c
new file mode 100644 (file)
index 0000000..3f7a9b5
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ */
+#include "logging.h"
+#include "cluster.h"
+#include "compat.h"
+#include "xlate.h"
+
+#include <errno.h>
+
+/*
+ * Older versions of the log daemon communicate with different
+ * versions of the inter-machine communication structure, which
+ * varies in size and fields.  The older versions append the
+ * standard upstream version of the structure to every request.
+ * COMPAT_OFFSET is where the upstream structure starts.
+ */
+#define COMPAT_OFFSET 256
+
+static void v5_data_endian_switch(struct clog_request *rq, int to_network __attribute__((unused)))
+{
+       int i, end;
+       int64_t *pi64;
+       uint64_t *pu64;
+       uint32_t rq_type = rq->u_rq.request_type & ~DM_ULOG_RESPONSE;
+
+       if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
+               switch (rq_type) {
+               case DM_ULOG_CTR:
+               case DM_ULOG_DTR:
+                       LOG_ERROR("Invalid response type in endian switch");
+                       exit(EXIT_FAILURE);
+
+               case DM_ULOG_PRESUSPEND:
+               case DM_ULOG_POSTSUSPEND:
+               case DM_ULOG_RESUME:
+               case DM_ULOG_FLUSH:
+               case DM_ULOG_MARK_REGION:
+               case DM_ULOG_CLEAR_REGION:
+               case DM_ULOG_SET_REGION_SYNC:
+               case DM_ULOG_CHECKPOINT_READY:
+               case DM_ULOG_MEMBER_JOIN:
+               case DM_ULOG_STATUS_INFO:
+               case DM_ULOG_STATUS_TABLE:
+                       /* No outbound data */
+                       break;
+
+               case DM_ULOG_GET_REGION_SIZE:
+               case DM_ULOG_GET_SYNC_COUNT:
+                       pu64 = (uint64_t *)rq->u_rq.data;
+                       *pu64 = xlate64(*pu64);
+                       break;
+               case DM_ULOG_IS_CLEAN:
+               case DM_ULOG_IN_SYNC:
+                       pi64 = (int64_t *)rq->u_rq.data;
+                       *pi64 = xlate64(*pi64);
+                       break;
+               case DM_ULOG_GET_RESYNC_WORK:
+               case DM_ULOG_IS_REMOTE_RECOVERING:
+                       pi64 = (int64_t *)rq->u_rq.data;
+                       pu64 = ((uint64_t *)rq->u_rq.data) + 1;
+                       *pi64 = xlate64(*pi64);
+                       *pu64 = xlate64(*pu64);
+                       break;
+               default:
+                       LOG_ERROR("Unknown request type, %u", rq_type);
+                       return;
+               }
+       } else {
+               switch (rq_type) {
+               case DM_ULOG_CTR:
+               case DM_ULOG_DTR:
+                       LOG_ERROR("Invalid request type in endian switch");
+                       exit(EXIT_FAILURE);
+
+               case DM_ULOG_PRESUSPEND:
+               case DM_ULOG_POSTSUSPEND:
+               case DM_ULOG_RESUME:
+               case DM_ULOG_GET_REGION_SIZE:
+               case DM_ULOG_FLUSH:
+               case DM_ULOG_GET_RESYNC_WORK:
+               case DM_ULOG_GET_SYNC_COUNT:
+               case DM_ULOG_STATUS_INFO:
+               case DM_ULOG_STATUS_TABLE:
+               case DM_ULOG_CHECKPOINT_READY:
+               case DM_ULOG_MEMBER_JOIN:
+                       /* No incoming data */
+                       break;
+               case DM_ULOG_IS_CLEAN:
+               case DM_ULOG_IN_SYNC:
+               case DM_ULOG_IS_REMOTE_RECOVERING:
+                       pu64 = (uint64_t *)rq->u_rq.data;
+                       *pu64 = xlate64(*pu64);
+                       break;
+               case DM_ULOG_MARK_REGION:
+               case DM_ULOG_CLEAR_REGION:
+                       end = rq->u_rq.data_size/sizeof(uint64_t);
+
+                       pu64 = (uint64_t *)rq->u_rq.data;
+                       for (i = 0; i < end; i++)
+                               pu64[i] = xlate64(pu64[i]);
+                       break;
+               case DM_ULOG_SET_REGION_SYNC:
+                       pu64 = (uint64_t *)rq->u_rq.data;
+                       pi64 = ((int64_t *)rq->u_rq.data) + 1;
+                       *pu64 = xlate64(*pu64);
+                       *pi64 = xlate64(*pi64);
+                       break;
+               default:
+                       LOG_ERROR("Unknown request type, %u", rq_type);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+static int v5_endian_to_network(struct clog_request *rq)
+{
+       int size;
+       struct dm_ulog_request *u_rq = &rq->u_rq;
+
+       size = sizeof(*rq) + u_rq->data_size;
+
+       u_rq->error = xlate32(u_rq->error);
+       u_rq->seq = xlate32(u_rq->seq);
+       u_rq->request_type = xlate32(u_rq->request_type);
+       u_rq->data_size = xlate64(u_rq->data_size);
+
+       rq->originator = xlate32(rq->originator);
+
+       v5_data_endian_switch(rq, 1);
+
+       return size;
+}
+
+int clog_request_to_network(struct clog_request *rq)
+{
+       int r;
+
+       /* FIXME: Remove this safety check */
+       if (rq->u.version[0] != xlate64(rq->u.version[1])) {
+               LOG_ERROR("Programmer error:  version[0] must be LE");
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Are we already running in the endian mode we send
+        * over the wire?
+        */
+       if (rq->u.version[0] == rq->u.version[1])
+               return 0;
+
+       r = v5_endian_to_network(rq);
+       if (r < 0)
+               return r;
+       return 0;
+}
+
+static int v5_endian_from_network(struct clog_request *rq)
+{
+       int size;
+       struct dm_ulog_request *u_rq = &rq->u_rq;
+
+       u_rq->error = xlate32(u_rq->error);
+       u_rq->seq = xlate32(u_rq->seq);
+       u_rq->request_type = xlate32(u_rq->request_type);
+       u_rq->data_size = xlate64(u_rq->data_size);
+
+       rq->originator = xlate32(rq->originator);
+
+       size = sizeof(*rq) + u_rq->data_size;
+
+       v5_data_endian_switch(rq, 0);
+
+       return size;
+}
+
+int clog_request_from_network(void *data, size_t data_len)
+{
+       uint64_t *vp = data;
+       uint64_t version = xlate64(vp[0]);
+       uint64_t unconverted_version = vp[1];
+       struct clog_request *rq = data;
+
+       switch (version) {
+       case 5: /* Upstream */
+               if (version == unconverted_version)
+                       return 0;
+               break;
+       case 4: /* RHEL 5.[45] */
+       case 3: /* RHEL 5.3 */
+       case 2: /* RHEL 5.2 */
+               /* FIXME: still need to account for payload */
+               if (data_len < (COMPAT_OFFSET + sizeof(*rq)))
+                       return -ENOSPC;
+
+               rq = (struct clog_request *)((char *)data + COMPAT_OFFSET);
+               break;
+       default:
+               LOG_ERROR("Unable to process cluster message: "
+                         "Incompatible version");
+               return -EINVAL;
+       }
+
+       v5_endian_from_network(rq);
+       return 0;
+}
diff --git a/daemons/cmirrord/compat.h b/daemons/cmirrord/compat.h
new file mode 100644 (file)
index 0000000..1e0edd4
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ */
+#ifndef _LVM_CLOG_COMPAT_H
+#define _LVM_CLOG_COMPAT_H
+
+/*
+ * The intermachine communication structure version are:
+ *     0: Unused
+ *     1: Never in the wild
+ *     2: RHEL 5.2
+ *     3: RHEL 5.3
+ *     4: RHEL 5.4, RHEL 5.5
+ *     5: RHEL 6, Current Upstream Format
+ */
+#define CLOG_TFR_VERSION 5
+
+int clog_request_to_network(struct clog_request *rq);
+int clog_request_from_network(void *data, size_t data_len);
+
+#endif /* _LVM_CLOG_COMPAT_H */
diff --git a/daemons/cmirrord/functions.c b/daemons/cmirrord/functions.c
new file mode 100644 (file)
index 0000000..de80793
--- /dev/null
@@ -0,0 +1,1937 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#include "logging.h"
+#include "functions.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#define BYTE_SHIFT 3
+
+/*
+ * Magic for persistent mirrors: "MiRr"
+ * Following on-disk header information is stolen from
+ * drivers/md/dm-log.c
+ */
+#define MIRROR_MAGIC 0x4D695272
+#define MIRROR_DISK_VERSION 2
+#define LOG_OFFSET 2
+
+#define RESYNC_HISTORY 50
+//static char resync_history[RESYNC_HISTORY][128];
+//static int idx = 0;
+#define LOG_SPRINT(_lc, f, arg...) do {                                        \
+               lc->idx++;                                              \
+               lc->idx = lc->idx % RESYNC_HISTORY;                     \
+               sprintf(lc->resync_history[lc->idx], f, ## arg);        \
+       } while (0)
+
+struct log_header {
+        uint32_t magic;
+        uint32_t version;
+        uint64_t nr_regions;
+};
+
+struct log_c {
+       struct dm_list list;
+
+       char uuid[DM_UUID_LEN];
+       uint64_t luid;
+
+       time_t delay; /* limits how fast a resume can happen after suspend */
+       int touched;
+       int in_sync;  /* An in-sync that stays set until suspend/resume */
+       uint32_t region_size;
+       uint32_t region_count;
+       uint64_t sync_count;
+
+       dm_bitset_t clean_bits;
+       dm_bitset_t sync_bits;
+       uint32_t recoverer;
+       uint64_t recovering_region; /* -1 means not recovering */
+       uint64_t skip_bit_warning; /* used to warn if region skipped */
+       int sync_search;
+
+       int resume_override;
+
+       uint32_t block_on_error;
+        enum sync {
+                DEFAULTSYNC,    /* Synchronize if necessary */
+                NOSYNC,         /* Devices known to be already in sync */
+                FORCESYNC,      /* Force a sync to happen */
+        } sync;
+
+       uint32_t state;         /* current operational state of the log */
+
+       struct dm_list mark_list;
+
+       uint32_t recovery_halted;
+       struct recovery_request *recovery_request_list;
+
+       int disk_fd;            /* -1 means no disk log */
+       int log_dev_failed;
+       uint64_t disk_nr_regions;
+       size_t disk_size;       /* size of disk_buffer in bytes */
+       void *disk_buffer;      /* aligned memory for O_DIRECT */
+       int idx;
+       char resync_history[RESYNC_HISTORY][128];
+};
+
+struct mark_entry {
+       struct dm_list list;
+       uint32_t nodeid;
+       uint64_t region;
+};
+
+struct recovery_request {
+       uint64_t region;
+       struct recovery_request *next;
+};
+
+static DM_LIST_INIT(log_list);
+static DM_LIST_INIT(log_pending_list);
+
+static int log_test_bit(dm_bitset_t bs, int bit)
+{
+       return dm_bit(bs, bit) ? 1 : 0;
+}
+
+static void log_set_bit(struct log_c *lc, dm_bitset_t bs, int bit)
+{
+       dm_bit_set(bs, bit);
+       lc->touched = 1;
+}
+
+static void log_clear_bit(struct log_c *lc, dm_bitset_t bs, int bit)
+{
+       dm_bit_clear(bs, bit);
+       lc->touched = 1;
+}
+
+static uint64_t find_next_zero_bit(dm_bitset_t bs, unsigned start)
+{
+       for (; dm_bit(bs, start); start++)
+               if (start >= *bs)
+                       return (uint64_t)-1;
+
+       return start;
+}
+
+static uint64_t count_bits32(dm_bitset_t bs)
+{
+       unsigned i, size = bs[0]/(unsigned)DM_BITS_PER_INT + 1;
+       unsigned count = 0;
+
+       for (i = 1; i <= size; i++)
+               count += hweight32(bs[i]);
+
+       return (uint64_t)count;
+}
+
+/*
+ * get_log
+ *
+ * Returns: log if found, NULL otherwise
+ */
+static struct log_c *get_log(const char *uuid, uint64_t luid)
+{
+       struct log_c *lc;
+
+       dm_list_iterate_items(lc, &log_list)
+               if (!strcmp(lc->uuid, uuid) &&
+                   (!luid || (luid == lc->luid)))
+                       return lc;
+
+       return NULL;
+}
+
+/*
+ * get_pending_log
+ *
+ * Pending logs are logs that have been 'clog_ctr'ed, but
+ * have not joined the CPG (via clog_resume).
+ *
+ * Returns: log if found, NULL otherwise
+ */
+static struct log_c *get_pending_log(const char *uuid, uint64_t luid)
+{
+       struct log_c *lc;
+
+       dm_list_iterate_items(lc, &log_pending_list)
+               if (!strcmp(lc->uuid, uuid) &&
+                   (!luid || (luid == lc->luid)))
+                       return lc;
+
+       return NULL;
+}
+
+static void header_to_disk(struct log_header *mem, struct log_header *disk)
+{
+       memcpy(disk, mem, sizeof(struct log_header));
+}
+
+static void header_from_disk(struct log_header *mem, struct log_header *disk)
+{
+       memcpy(mem, disk, sizeof(struct log_header));
+}
+
+static int rw_log(struct log_c *lc, int do_write)
+{
+       int r;
+
+       r = (int)lseek(lc->disk_fd, 0, SEEK_SET);
+       if (r < 0) {
+               LOG_ERROR("[%s] rw_log:  lseek failure: %s",
+                         SHORT_UUID(lc->uuid), strerror(errno));
+               return -errno;
+       }
+
+       if (do_write) {
+               /* FIXME Cope with full set of non-error conditions */
+               r = write(lc->disk_fd, lc->disk_buffer, lc->disk_size);
+               if (r < 0) {
+                       LOG_ERROR("[%s] rw_log:  write failure: %s",
+                                 SHORT_UUID(lc->uuid), strerror(errno));
+                       return -EIO; /* Failed disk write */
+               }
+               return 0;
+       }
+
+       /* Read */
+       /* FIXME Cope with full set of non-error conditions */
+       r = read(lc->disk_fd, lc->disk_buffer, lc->disk_size);
+       if (r < 0)
+               LOG_ERROR("[%s] rw_log:  read failure: %s",
+                         SHORT_UUID(lc->uuid), strerror(errno));
+       if (r != lc->disk_size)
+               return -EIO; /* Failed disk read */
+       return 0;
+}
+
+/*
+ * read_log
+ * @lc
+ *
+ * Valid return codes:
+ *   -EINVAL:  Invalid header, bits not copied
+ *   -EIO:     Unable to read disk log
+ *    0:       Valid header, disk bit -> lc->clean_bits
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int read_log(struct log_c *lc)
+{
+       struct log_header lh;
+       size_t bitset_size;
+
+       memset(&lh, 0, sizeof(struct log_header));
+
+       if (rw_log(lc, 0))
+               return -EIO; /* Failed disk read */
+
+       header_from_disk(&lh, lc->disk_buffer);
+       if (lh.magic != MIRROR_MAGIC)
+               return -EINVAL;
+
+       lc->disk_nr_regions = lh.nr_regions;
+
+       /* Read disk bits into sync_bits */
+       bitset_size = lc->region_count / 8;
+       bitset_size += (lc->region_count % 8) ? 1 : 0;
+
+       /* 'lc->clean_bits + 1' becasue dm_bitset_t leads with a uint32_t */
+       memcpy(lc->clean_bits + 1, (char *)lc->disk_buffer + 1024, bitset_size);
+
+       return 0;
+}
+
+/*
+ * write_log
+ * @lc
+ *
+ * Returns: 0 on success, -EIO on failure
+ */
+static int write_log(struct log_c *lc)
+{
+       struct log_header lh;
+       size_t bitset_size;
+
+       lh.magic = MIRROR_MAGIC;
+       lh.version = MIRROR_DISK_VERSION;
+       lh.nr_regions = lc->region_count;
+
+       header_to_disk(&lh, lc->disk_buffer);
+
+       /* Write disk bits from clean_bits */
+       bitset_size = lc->region_count / 8;
+       bitset_size += (lc->region_count % 8) ? 1 : 0;
+
+       /* 'lc->clean_bits + 1' becasue dm_bitset_t leads with a uint32_t */
+       memcpy((char *)lc->disk_buffer + 1024, lc->clean_bits + 1, bitset_size);
+
+       if (rw_log(lc, 1)) {
+               lc->log_dev_failed = 1;
+               return -EIO; /* Failed disk write */
+       }
+       return 0;
+}
+
+/* FIXME Rewrite this function taking advantage of the udev changes (where in use) to improve its efficiency! */
+static int find_disk_path(char *major_minor_str, char *path_rtn, int *unlink_path __attribute__((unused)))
+{
+       int r;
+       DIR *dp;
+       struct dirent *dep;
+       struct stat statbuf;
+       int major, minor;
+
+       if (!strstr(major_minor_str, ":")) {
+               r = stat(major_minor_str, &statbuf);
+               if (r)
+                       return -errno;
+               if (!S_ISBLK(statbuf.st_mode))
+                       return -EINVAL;
+               sprintf(path_rtn, "%s", major_minor_str);
+               return 0;
+       }
+
+       r = sscanf(major_minor_str, "%d:%d", &major, &minor);
+       if (r != 2)
+               return -EINVAL;
+
+       /* FIXME dm_dir() */
+       LOG_DBG("Checking /dev/mapper for device %d:%d", major, minor);
+       /* Check /dev/mapper dir */
+       dp = opendir("/dev/mapper");
+       if (!dp)
+               return -ENOENT;
+
+       while ((dep = readdir(dp)) != NULL) {
+               /*
+                * FIXME: This is racy.  By the time the path is used,
+                * it may point to something else.  'fstat' will be
+                * required upon opening to ensure we got what we
+                * wanted.
+                */
+
+               sprintf(path_rtn, "/dev/mapper/%s", dep->d_name);
+               stat(path_rtn, &statbuf);
+               if (S_ISBLK(statbuf.st_mode) &&
+                   (major(statbuf.st_rdev) == major) &&
+                   (minor(statbuf.st_rdev) == minor)) {
+                       LOG_DBG("  %s: YES", dep->d_name);
+                       closedir(dp);
+                       return 0;
+               } else {
+                       LOG_DBG("  %s: NO", dep->d_name);
+               }
+       }
+
+       closedir(dp);
+
+       /* FIXME Find out why this was here and deal with underlying problem. */
+       LOG_DBG("Path not found for %d/%d", major, minor);
+       return -ENOENT;
+
+       // LOG_DBG("Creating /dev/mapper/%d-%d", major, minor);
+       // sprintf(path_rtn, "/dev/mapper/%d-%d", major, minor);
+       // r = mknod(path_rtn, S_IFBLK | S_IRUSR | S_IWUSR, MKDEV(major, minor));
+       /*
+        * If we have to make the path, we unlink it after we open it
+        */
+       // *unlink_path = 1;
+       // return r ? -errno : 0;
+}
+
+static int _clog_ctr(char *uuid, uint64_t luid,
+                    int argc, char **argv, uint64_t device_size)
+{
+       int i;
+       int r = 0;
+       char *p;
+       uint64_t region_size;
+       uint64_t region_count;
+       struct log_c *lc = NULL;
+       enum sync log_sync = DEFAULTSYNC;
+       uint32_t block_on_error = 0;
+
+       int disk_log = 0;
+       char disk_path[128];
+       int unlink_path = 0;
+       size_t page_size;
+       int pages;
+
+       /* If core log request, then argv[0] will be region_size */
+       if (!strtoll(argv[0], &p, 0) || *p) {
+               disk_log = 1;
+
+               if ((argc < 2) || (argc > 4)) {
+                       LOG_ERROR("Too %s arguments to clustered-disk log type",
+                                 (argc < 3) ? "few" : "many");
+                       r = -EINVAL;
+                       goto fail;
+               }
+
+               r = find_disk_path(argv[0], disk_path, &unlink_path);
+               if (r) {
+                       LOG_ERROR("Unable to find path to device %s", argv[0]);
+                       goto fail;
+               }
+               LOG_DBG("Clustered log disk is %s", disk_path);
+       } else {
+               disk_log = 0;
+
+               if ((argc < 1) || (argc > 3)) {
+                       LOG_ERROR("Too %s arguments to clustered-core log type",
+                                 (argc < 2) ? "few" : "many");
+                       r = -EINVAL;
+                       goto fail;
+               }
+       }
+
+       if (!(region_size = strtoll(argv[disk_log], &p, 0)) || *p) {
+               LOG_ERROR("Invalid region_size argument to clustered-%s log type",
+                         (disk_log) ? "disk" : "core");
+               r = -EINVAL;
+               goto fail;
+       }
+
+       region_count = device_size / region_size;
+       if (device_size % region_size) {
+               /*
+                * I can't remember if device_size must be a multiple
+                * of region_size, so check it anyway.
+                */
+               region_count++;
+       }
+
+       for (i = 0; i < argc; i++) {
+               if (!strcmp(argv[i], "sync"))
+                       log_sync = FORCESYNC;
+               else if (!strcmp(argv[i], "nosync"))
+                       log_sync = NOSYNC;
+               else if (!strcmp(argv[i], "block_on_error"))
+                       block_on_error = 1;
+       }
+
+       lc = dm_zalloc(sizeof(*lc));
+       if (!lc) {
+               LOG_ERROR("Unable to allocate cluster log context");
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       lc->region_size = region_size;
+       lc->region_count = region_count;
+       lc->sync = log_sync;
+       lc->block_on_error = block_on_error;
+       lc->sync_search = 0;
+       lc->recovering_region = (uint64_t)-1;
+       lc->skip_bit_warning = region_count;
+       lc->disk_fd = -1;
+       lc->log_dev_failed = 0;
+       strncpy(lc->uuid, uuid, DM_UUID_LEN);
+       lc->luid = luid;
+
+       if (get_log(lc->uuid, lc->luid) ||
+           get_pending_log(lc->uuid, lc->luid)) {
+               LOG_ERROR("[%s/%" PRIu64 "u] Log already exists, unable to create.",
+                         SHORT_UUID(lc->uuid), lc->luid);
+               dm_free(lc);
+               return -EINVAL;
+       }
+
+       dm_list_init(&lc->mark_list);
+
+       lc->clean_bits = dm_bitset_create(NULL, region_count);
+       if (!lc->clean_bits) {
+               LOG_ERROR("Unable to allocate clean bitset");
+               r = -ENOMEM;
+               goto fail;
+       }
+
+       lc->sync_bits = dm_bitset_create(NULL, region_count);
+       if (!lc->sync_bits) {
+               LOG_ERROR("Unable to allocate sync bitset");
+               r = -ENOMEM;
+               goto fail;
+       }
+       if (log_sync == NOSYNC)
+               dm_bit_set_all(lc->sync_bits);
+
+       lc->sync_count = (log_sync == NOSYNC) ? region_count : 0;
+
+       if (disk_log) {
+               page_size = sysconf(_SC_PAGESIZE);
+               pages = *(lc->clean_bits) / page_size;
+               pages += *(lc->clean_bits) % page_size ? 1 : 0;
+               pages += 1; /* for header */
+
+               r = open(disk_path, O_RDWR | O_DIRECT);
+               if (r < 0) {
+                       LOG_ERROR("Unable to open log device, %s: %s",
+                                 disk_path, strerror(errno));
+                       r = errno;
+                       goto fail;
+               }
+               if (unlink_path)
+                       unlink(disk_path);
+
+               lc->disk_fd = r;
+               lc->disk_size = pages * page_size;
+
+               r = posix_memalign(&(lc->disk_buffer), page_size,
+                                  lc->disk_size);
+               if (r) {
+                       LOG_ERROR("Unable to allocate memory for disk_buffer");
+                       goto fail;
+               }
+               memset(lc->disk_buffer, 0, lc->disk_size);
+               LOG_DBG("Disk log ready");
+       }
+
+       dm_list_add(&log_pending_list, &lc->list);
+
+       return 0;
+fail:
+       if (lc) {
+               if (lc->disk_fd >= 0 && close(lc->disk_fd))
+                       LOG_ERROR("Close device error, %s: %s",
+                                 disk_path, strerror(errno));
+               free(lc->disk_buffer);
+               dm_free(lc->sync_bits);
+               dm_free(lc->clean_bits);
+               dm_free(lc);
+       }
+       return r;
+}
+
+/*
+ * clog_ctr
+ * @rq
+ *
+ * rq->data should contain constructor string as follows:
+ *     <log_type> [disk] <region_size> [[no]sync] <device_len>
+ * The kernel is responsible for adding the <dev_len> argument
+ * to the end; otherwise, we cannot compute the region_count.
+ *
+ * FIXME: Currently relies on caller to fill in rq->error
+ */
+static int clog_dtr(struct dm_ulog_request *rq);
+static int clog_ctr(struct dm_ulog_request *rq)
+{
+       int argc, i, r = 0;
+       char *p, **argv = NULL;
+       char *dev_size_str;
+       uint64_t device_size;
+
+       /* Sanity checks */
+       if (!rq->data_size) {
+               LOG_ERROR("Received constructor request with no data");
+               return -EINVAL;
+       }
+
+       if (strlen(rq->data) > rq->data_size) {
+               LOG_ERROR("Received constructor request with bad data");
+               LOG_ERROR("strlen(rq->data)[%d] != rq->data_size[%llu]",
+                         (int)strlen(rq->data),
+                         (unsigned long long)rq->data_size);
+               LOG_ERROR("rq->data = '%s' [%d]",
+                         rq->data, (int)strlen(rq->data));
+               return -EINVAL;
+       }
+
+       /* Split up args */
+       for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++)
+               *p = '\0';
+
+       argv = malloc(argc * sizeof(char *));
+       if (!argv)
+               return -ENOMEM;
+
+       p = dev_size_str = rq->data;
+       p += strlen(p) + 1;
+       for (i = 0; i < argc; i++, p = p + strlen(p) + 1)
+               argv[i] = p;
+
+       if (strcmp(argv[0], "clustered-disk") &&
+           strcmp(argv[0], "clustered-core")) {
+               LOG_ERROR("Unsupported userspace log type, \"%s\"", argv[0]);
+               free(argv);
+               return -EINVAL;
+       }
+
+       if (!(device_size = strtoll(dev_size_str, &p, 0)) || *p) {
+               LOG_ERROR("Invalid device size argument: %s", dev_size_str);
+               free(argv);
+               return -EINVAL;
+       }
+
+       r = _clog_ctr(rq->uuid, rq->luid, argc - 1, argv + 1, device_size);
+
+       /* We join the CPG when we resume */
+
+       /* No returning data */
+       rq->data_size = 0;
+
+       if (r) {
+               LOG_ERROR("Failed to create cluster log (%s)", rq->uuid);
+               for (i = 0; i < argc; i++)
+                       LOG_ERROR("argv[%d] = %s", i, argv[i]);
+       }
+       else
+               LOG_DBG("[%s] Cluster log created",
+                       SHORT_UUID(rq->uuid));
+
+       free(argv);
+       return r;
+}
+
+/*
+ * clog_dtr
+ * @rq
+ *
+ */
+static int clog_dtr(struct dm_ulog_request *rq)
+{
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (lc) {
+               /*
+                * The log should not be on the official list.  There
+                * should have been a suspend first.
+                */
+               LOG_ERROR("[%s] DTR before SUS: leaving CPG",
+                         SHORT_UUID(rq->uuid));
+               destroy_cluster_cpg(rq->uuid);
+       } else if (!(lc = get_pending_log(rq->uuid, rq->luid))) {
+               LOG_ERROR("clog_dtr called on log that is not official or pending");
+               return -EINVAL;
+       }
+
+       LOG_DBG("[%s] Cluster log removed", SHORT_UUID(lc->uuid));
+
+       dm_list_del(&lc->list);
+       if (lc->disk_fd != -1)
+               close(lc->disk_fd);
+       if (lc->disk_buffer)
+               free(lc->disk_buffer);
+       dm_free(lc->clean_bits);
+       dm_free(lc->sync_bits);
+       dm_free(lc);
+
+       return 0;
+}
+
+/*
+ * clog_presuspend
+ * @rq
+ *
+ */
+static int clog_presuspend(struct dm_ulog_request *rq)
+{
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (lc->touched)
+               LOG_DBG("WARNING: log still marked as 'touched' during suspend");
+
+       lc->recovery_halted = 1;
+
+       return 0;
+}
+
+/*
+ * clog_postsuspend
+ * @rq
+ *
+ */
+static int clog_postsuspend(struct dm_ulog_request *rq)
+{
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       LOG_DBG("[%s] clog_postsuspend: leaving CPG", SHORT_UUID(lc->uuid));
+       destroy_cluster_cpg(rq->uuid);
+
+       lc->state = LOG_SUSPENDED;
+       lc->recovering_region = (uint64_t)-1;
+       lc->recoverer = (uint32_t)-1;
+       lc->delay = time(NULL);
+
+       return 0;
+}
+
+/*
+ * cluster_postsuspend
+ * @rq
+ *
+ */
+int cluster_postsuspend(char *uuid, uint64_t luid)
+{
+       struct log_c *lc = get_log(uuid, luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       LOG_DBG("[%s] clog_postsuspend: finalizing", SHORT_UUID(lc->uuid));
+       lc->resume_override = 0;
+
+       /* move log to pending list */
+       dm_list_del(&lc->list);
+       dm_list_add(&log_pending_list, &lc->list);
+
+       return 0;
+}
+
+/*
+ * clog_resume
+ * @rq
+ *
+ * Does the main work of resuming.
+ */
+static int clog_resume(struct dm_ulog_request *rq)
+{
+       uint32_t i;
+       int commit_log = 0;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       lc->in_sync = 0;
+       switch (lc->resume_override) {
+       case 1000:
+               LOG_ERROR("[%s] Additional resume issued before suspend",
+                         SHORT_UUID(rq->uuid));
+#ifdef DEBUG
+               kill(getpid(), SIGUSR1);
+#endif
+               return 0;
+       case 0:
+               lc->resume_override = 1000;
+               if (lc->disk_fd == -1) {
+                       LOG_DBG("[%s] Master resume.",
+                               SHORT_UUID(lc->uuid));
+                       goto no_disk;
+               }
+
+               LOG_DBG("[%s] Master resume: reading disk log",
+                       SHORT_UUID(lc->uuid));
+               commit_log = 1;
+               break;
+       case 1:
+               LOG_ERROR("Error:: partial bit loading (just sync_bits)");
+               return -EINVAL;
+       case 2:
+               LOG_ERROR("Error:: partial bit loading (just clean_bits)");
+               return -EINVAL;
+       case 3:
+               LOG_DBG("[%s] Non-master resume: bits pre-loaded",
+                       SHORT_UUID(lc->uuid));
+               lc->resume_override = 1000;
+               goto out;
+       default:
+               LOG_ERROR("Error:: multiple loading of bits (%d)",
+                         lc->resume_override);
+               return -EINVAL;
+       }
+
+       if (lc->log_dev_failed) {
+               LOG_ERROR("Log device has failed, unable to read bits");
+               rq->error = 0;  /* We can handle this so far */
+               lc->disk_nr_regions = 0;
+       } else
+               rq->error = read_log(lc);
+
+       switch (rq->error) {
+       case 0:
+               if (lc->disk_nr_regions < lc->region_count)
+                       LOG_DBG("[%s] Mirror has grown, updating log bits",
+                               SHORT_UUID(lc->uuid));
+               else if (lc->disk_nr_regions > lc->region_count)
+                       LOG_DBG("[%s] Mirror has shrunk, updating log bits",
+                               SHORT_UUID(lc->uuid));
+               break;          
+       case -EINVAL:
+               LOG_DBG("[%s] (Re)initializing mirror log - resync issued.",
+                       SHORT_UUID(lc->uuid));
+               lc->disk_nr_regions = 0;
+               break;
+       default:
+               LOG_ERROR("Failed to read disk log");
+               lc->disk_nr_regions = 0;
+               break;
+       }
+
+no_disk:
+       /* If mirror has grown, set bits appropriately */
+       if (lc->sync == NOSYNC)
+               for (i = lc->disk_nr_regions; i < lc->region_count; i++)
+                       log_set_bit(lc, lc->clean_bits, i);
+       else
+               for (i = lc->disk_nr_regions; i < lc->region_count; i++)
+                       log_clear_bit(lc, lc->clean_bits, i);
+
+       /* Clear any old bits if device has shrunk */
+       for (i = lc->region_count; i % 32; i++)
+               log_clear_bit(lc, lc->clean_bits, i);
+
+       /* copy clean across to sync */
+       dm_bit_copy(lc->sync_bits, lc->clean_bits);
+
+       if (commit_log && (lc->disk_fd >= 0)) {
+               rq->error = write_log(lc);
+               if (rq->error)
+                       LOG_ERROR("Failed initial disk log write");
+               else
+                       LOG_DBG("Disk log initialized");
+               lc->touched = 0;
+       }
+out:
+       /*
+        * Clear any old bits if device has shrunk - necessary
+        * for non-master resume
+        */
+       for (i = lc->region_count; i % 32; i++) {
+               log_clear_bit(lc, lc->clean_bits, i);
+               log_clear_bit(lc, lc->sync_bits, i);
+       }
+
+       lc->sync_count = count_bits32(lc->sync_bits);
+
+       LOG_SPRINT(lc, "[%s] Initial sync_count = %llu",
+                  SHORT_UUID(lc->uuid), (unsigned long long)lc->sync_count);
+       lc->sync_search = 0;
+       lc->state = LOG_RESUMED;
+       lc->recovery_halted = 0;
+       
+       return rq->error;
+}
+
+/*
+ * local_resume
+ * @rq
+ *
+ * If the log is pending, we must first join the cpg and
+ * put the log in the official list.
+ *
+ */
+int local_resume(struct dm_ulog_request *rq)
+{
+       int r;
+       time_t t;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc) {
+               /* Is the log in the pending list? */
+               lc = get_pending_log(rq->uuid, rq->luid);
+               if (!lc) {
+                       LOG_ERROR("clog_resume called on log that is not official or pending");
+                       return -EINVAL;
+               }
+
+               t = time(NULL);
+               t -= lc->delay;
+               /*
+                * This should be considered a temporary fix.  It addresses
+                * a problem that exists when nodes suspend/resume in rapid
+                * succession.  While the problem is very rare, it has been
+                * seen to happen in real-world-like testing.
+                *
+                * The problem:
+                * - Node A joins cluster
+                * - Node B joins cluster
+                * - Node A prepares checkpoint
+                * - Node A gets ready to write checkpoint
+                * - Node B leaves
+                * - Node B joins
+                * - Node A finishes write of checkpoint
+                * - Node B receives checkpoint meant for previous session
+                * -- Node B can now be non-coherent
+                *
+                * This timer will solve the problem for now, but could be
+                * replaced by a generation number sent with the resume
+                * command from the kernel.  The generation number would
+                * be included in the name of the checkpoint to prevent
+                * reading stale data.
+                */
+               if ((t < 3) && (t >= 0))
+                       sleep(3 - t);
+
+               /* Join the CPG */
+               r = create_cluster_cpg(rq->uuid, rq->luid);
+               if (r) {
+                       LOG_ERROR("clog_resume:  Failed to create cluster CPG");
+                       return r;
+               }
+
+               /* move log to official list */
+               dm_list_del(&lc->list);
+               dm_list_add(&log_list, &lc->list);
+       }
+
+       return 0;
+}
+
+/*
+ * clog_get_region_size
+ * @rq
+ *
+ * Since this value doesn't change, the kernel
+ * should not need to talk to server to get this
+ * The function is here for completness
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int clog_get_region_size(struct dm_ulog_request *rq)
+{
+       uint64_t *rtn = (uint64_t *)rq->data;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc && !(lc = get_pending_log(rq->uuid, rq->luid)))
+               return -EINVAL;
+
+       *rtn = lc->region_size;
+       rq->data_size = sizeof(*rtn);
+
+       return 0;
+}
+
+/*
+ * clog_is_clean
+ * @rq
+ *
+ * Returns: 1 if clean, 0 otherwise
+ */
+static int clog_is_clean(struct dm_ulog_request *rq)
+{
+       int64_t *rtn = (int64_t *)rq->data;
+       uint64_t *region = (uint64_t *)rq->data;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       *rtn = log_test_bit(lc->clean_bits, *region);
+       rq->data_size = sizeof(*rtn);
+
+       return 0;
+}
+
+/*
+ * clog_in_sync
+ * @rq
+ *
+ * We ignore any request for non-block.  That
+ * should be handled elsewhere.  (If the request
+ * has come this far, it has already blocked.)
+ *
+ * Returns: 1 if in-sync, 0 otherwise
+ */
+static int clog_in_sync(struct dm_ulog_request *rq)
+{
+       int64_t *rtn = (int64_t *)rq->data;
+       uint64_t *region_p = (uint64_t *)rq->data;
+       uint64_t region = *region_p;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (region > lc->region_count)
+               return -EINVAL;
+
+       *rtn = log_test_bit(lc->sync_bits, region);
+
+       /*
+        * If the mirror was successfully recovered, we want to always
+        * force every machine to write to all devices - otherwise,
+        * corruption will occur.  Here's how:
+        *    Node1 suffers a failure and marks a region out-of-sync
+        *    Node2 attempts a write, gets by is_remote_recovering,
+        *          and queries the sync status of the region - finding
+        *          it out-of-sync.
+        *    Node2 thinks the write should be a nosync write, but it
+        *          hasn't suffered the drive failure that Node1 has yet.
+        *          It then issues a generic_make_request directly to
+        *          the primary image only - which is exactly the device
+        *          that has suffered the failure.
+        *    Node2 suffers a lost write - which completely bypasses the
+        *          mirror layer because it had gone through generic_m_r.
+        *    The file system will likely explode at this point due to
+        *    I/O errors.  If it wasn't the primary that failed, it is
+        *    easily possible in this case to issue writes to just one
+        *    of the remaining images - also leaving the mirror inconsistent.
+        *
+        * We let in_sync() return 1 in a cluster regardless of what is
+        * in the bitmap once recovery has successfully completed on a
+        * mirror.  This ensures the mirroring code will continue to
+        * attempt to write to all mirror images.  The worst that can
+        * happen for reads is that additional read attempts may be
+        * taken.
+        *
+        * Futher investigation may be required to determine if there are
+        * similar possible outcomes when the mirror is in the process of
+        * recovering.  In that case, lc->in_sync would not have been set
+        * yet.
+        */
+       if (!*rtn && lc->in_sync)
+               *rtn = 1;
+
+       if (*rtn)
+               LOG_DBG("[%s] Region is in-sync: %llu",
+                       SHORT_UUID(lc->uuid), (unsigned long long)region);
+       else
+               LOG_DBG("[%s] Region is not in-sync: %llu",
+                       SHORT_UUID(lc->uuid), (unsigned long long)region);
+
+       rq->data_size = sizeof(*rtn);
+
+       return 0;
+}
+
+/*
+ * clog_flush
+ * @rq
+ *
+ */
+static int clog_flush(struct dm_ulog_request *rq, int server)
+{
+       int r = 0;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+       
+       if (!lc)
+               return -EINVAL;
+
+       if (!lc->touched)
+               return 0;
+
+       /*
+        * Do the actual flushing of the log only
+        * if we are the server.
+        */
+       if (server && (lc->disk_fd >= 0)) {
+               r = rq->error = write_log(lc);
+               if (r)
+                       LOG_ERROR("[%s] Error writing to disk log",
+                                 SHORT_UUID(lc->uuid));
+               else 
+                       LOG_DBG("[%s] Disk log written", SHORT_UUID(lc->uuid));
+       }
+
+       lc->touched = 0;
+
+       return r;
+
+}
+
+/*
+ * mark_region
+ * @lc
+ * @region
+ * @who
+ *
+ * Put a mark region request in the tree for tracking.
+ *
+ * Returns: 0 on success, -EXXX on error
+ */
+static int mark_region(struct log_c *lc, uint64_t region, uint32_t who)
+{
+       int found = 0;
+       struct mark_entry *m;
+
+       dm_list_iterate_items(m, &lc->mark_list)
+               if (m->region == region) {
+                       found = 1;
+                       if (m->nodeid == who)
+                               return 0;
+               }
+
+       if (!found)
+               log_clear_bit(lc, lc->clean_bits, region);
+
+       /*
+        * Save allocation until here - if there is a failure,
+        * at least we have cleared the bit.
+        */
+       m = malloc(sizeof(*m));
+       if (!m) {
+               LOG_ERROR("Unable to allocate space for mark_entry: %llu/%u",
+                         (unsigned long long)region, who);
+               return -ENOMEM;
+       }
+
+       m->nodeid = who;
+       m->region = region;
+       dm_list_add(&lc->mark_list, &m->list);
+
+       return 0;
+}
+
+/*
+ * clog_mark_region
+ * @rq
+ *
+ * rq may contain more than one mark request.  We
+ * can determine the number from the 'data_size' field.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int clog_mark_region(struct dm_ulog_request *rq, uint32_t originator)
+{
+       int r;
+       int count;
+       uint64_t *region;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (rq->data_size % sizeof(uint64_t)) {
+               LOG_ERROR("Bad data size given for mark_region request");
+               return -EINVAL;
+       }
+
+       count = rq->data_size / sizeof(uint64_t);
+       region = (uint64_t *)&rq->data;
+
+       for (; count > 0; count--, region++) {
+               r = mark_region(lc, *region, originator);
+               if (r)
+                       return r;
+       }
+
+       rq->data_size = 0;
+
+       return 0;
+}
+
+static int clear_region(struct log_c *lc, uint64_t region, uint32_t who)
+{
+       int other_matches = 0;
+       struct mark_entry *m, *n;
+
+       dm_list_iterate_items_safe(m, n, &lc->mark_list)
+               if (m->region == region) {
+                       if (m->nodeid == who) {
+                               dm_list_del(&m->list);
+                               free(m);
+                       } else
+                               other_matches = 1;
+               }
+
+       /*
+        * Clear region if:
+        *  1) It is in-sync
+        *  2) There are no other machines that have it marked
+        */
+       if (!other_matches && log_test_bit(lc->sync_bits, region))
+               log_set_bit(lc, lc->clean_bits, region);
+
+       return 0;
+}
+
+/*
+ * clog_clear_region
+ * @rq
+ *
+ * rq may contain more than one clear request.  We
+ * can determine the number from the 'data_size' field.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int clog_clear_region(struct dm_ulog_request *rq, uint32_t originator)
+{
+       int r;
+       int count;
+       uint64_t *region;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (rq->data_size % sizeof(uint64_t)) {
+               LOG_ERROR("Bad data size given for clear_region request");
+               return -EINVAL;
+       }
+
+       count = rq->data_size / sizeof(uint64_t);
+       region = (uint64_t *)&rq->data;
+
+       for (; count > 0; count--, region++) {
+               r = clear_region(lc, *region, originator);
+               if (r)
+                       return r;
+       }
+
+       rq->data_size = 0;
+
+       return 0;
+}
+
+/*
+ * clog_get_resync_work
+ * @rq
+ *
+ */
+static int clog_get_resync_work(struct dm_ulog_request *rq, uint32_t originator)
+{
+       struct {
+               int64_t i;
+               uint64_t r;
+       } *pkg = (void *)rq->data;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       rq->data_size = sizeof(*pkg);
+       pkg->i = 0;
+
+       if (lc->sync_search >= lc->region_count) {
+               /*
+                * FIXME: handle intermittent errors during recovery
+                * by resetting sync_search... but not to many times.
+                */
+               LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                          "Recovery finished",
+                          rq->seq, SHORT_UUID(lc->uuid), originator);
+               return 0;
+       }
+
+       if (lc->recovering_region != (uint64_t)-1) {
+               if (lc->recoverer == originator) {
+                       LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                                  "Re-requesting work (%llu)",
+                                  rq->seq, SHORT_UUID(lc->uuid), originator,
+                                  (unsigned long long)lc->recovering_region);
+                       pkg->r = lc->recovering_region;
+                       pkg->i = 1;
+                       LOG_COND(log_resend_requests, "***** RE-REQUEST *****");
+               } else {
+                       LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                                  "Someone already recovering (%llu)",
+                                  rq->seq, SHORT_UUID(lc->uuid), originator,
+                                  (unsigned long long)lc->recovering_region);
+               }
+
+               return 0;
+       }
+
+       while (lc->recovery_request_list) {
+               struct recovery_request *del;
+
+               del = lc->recovery_request_list;
+               lc->recovery_request_list = del->next;
+
+               pkg->r = del->region;
+               free(del);
+
+               if (!log_test_bit(lc->sync_bits, pkg->r)) {
+                       LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                                  "Assigning priority resync work (%llu)",
+                                  rq->seq, SHORT_UUID(lc->uuid), originator,
+                                  (unsigned long long)pkg->r);
+                       pkg->i = 1;
+                       lc->recovering_region = pkg->r;
+                       lc->recoverer = originator;
+                       return 0;
+               }
+       }
+
+       pkg->r = find_next_zero_bit(lc->sync_bits, lc->sync_search);
+
+       if (pkg->r >= lc->region_count) {
+               LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                          "Resync work complete.",
+                          rq->seq, SHORT_UUID(lc->uuid), originator);
+               lc->sync_search = lc->region_count + 1;
+               return 0;
+       }
+
+       lc->sync_search = pkg->r + 1;
+
+       LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                  "Assigning resync work (%llu)",
+                  rq->seq, SHORT_UUID(lc->uuid), originator,
+                  (unsigned long long)pkg->r);
+       pkg->i = 1;
+       lc->recovering_region = pkg->r;
+       lc->recoverer = originator;
+
+       return 0;
+}
+
+/*
+ * clog_set_region_sync
+ * @rq
+ */
+static int clog_set_region_sync(struct dm_ulog_request *rq, uint32_t originator)
+{
+       struct {
+               uint64_t region;
+               int64_t in_sync;
+       } *pkg = (void *)rq->data;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       lc->recovering_region = (uint64_t)-1;
+
+       if (pkg->in_sync) {
+               if (log_test_bit(lc->sync_bits, pkg->region)) {
+                       LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                                  "Region already set (%llu)",
+                                  rq->seq, SHORT_UUID(lc->uuid), originator,
+                                  (unsigned long long)pkg->region);
+               } else {
+                       log_set_bit(lc, lc->sync_bits, pkg->region);
+                       lc->sync_count++;
+
+                       /* The rest of this section is all for debugging */
+                       LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                                  "Setting region (%llu)",
+                                  rq->seq, SHORT_UUID(lc->uuid), originator,
+                                  (unsigned long long)pkg->region);
+                       if (pkg->region == lc->skip_bit_warning)
+                               lc->skip_bit_warning = lc->region_count;
+
+                       if (pkg->region > (lc->skip_bit_warning + 5)) {
+                               LOG_SPRINT(lc, "*** Region #%llu skipped during recovery ***",
+                                         (unsigned long long)lc->skip_bit_warning);
+                               lc->skip_bit_warning = lc->region_count;
+#ifdef DEBUG
+                               kill(getpid(), SIGUSR1);
+#endif
+                       }
+
+                       if (!log_test_bit(lc->sync_bits,
+                                         (pkg->region) ? pkg->region - 1 : 0)) {
+                               LOG_SPRINT(lc, "*** Previous bit not set ***");
+                               lc->skip_bit_warning = (pkg->region) ?
+                                       pkg->region - 1 : 0;
+                       }
+               }
+       } else if (log_test_bit(lc->sync_bits, pkg->region)) {
+               lc->sync_count--;
+               log_clear_bit(lc, lc->sync_bits, pkg->region);
+               LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                          "Unsetting region (%llu)",
+                          rq->seq, SHORT_UUID(lc->uuid), originator,
+                          (unsigned long long)pkg->region);
+       }
+
+       if (lc->sync_count != count_bits32(lc->sync_bits)) {
+               unsigned long long reset = count_bits32(lc->sync_bits);
+
+               LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                          "sync_count(%llu) != bitmap count(%llu)",
+                          rq->seq, SHORT_UUID(lc->uuid), originator,
+                          (unsigned long long)lc->sync_count, reset);
+#ifdef DEBUG
+               kill(getpid(), SIGUSR1);
+#endif
+               lc->sync_count = reset;
+       }
+
+       if (lc->sync_count > lc->region_count)
+               LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                          "(lc->sync_count > lc->region_count) - this is bad",
+                          rq->seq, SHORT_UUID(lc->uuid), originator);
+
+       if (lc->sync_count == lc->region_count)
+               lc->in_sync = 1;
+
+       rq->data_size = 0;
+       return 0;
+}
+
+/*
+ * clog_get_sync_count
+ * @rq
+ */
+static int clog_get_sync_count(struct dm_ulog_request *rq, uint32_t originator)
+{
+       uint64_t *sync_count = (uint64_t *)rq->data;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       /*
+        * FIXME: Mirror requires us to be able to ask for
+        * the sync count while pending... but I don't like
+        * it because other machines may not be suspended and
+        * the stored value may not be accurate.
+        */
+       if (!lc)
+               lc = get_pending_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       *sync_count = lc->sync_count;
+
+       rq->data_size = sizeof(*sync_count);
+
+       if (lc->sync_count != count_bits32(lc->sync_bits)) {
+               unsigned long long reset = count_bits32(lc->sync_bits);
+
+               LOG_SPRINT(lc, "get_sync_count - SEQ#=%u, UUID=%s, nodeid = %u:: "
+                          "sync_count(%llu) != bitmap count(%llu)",
+                          rq->seq, SHORT_UUID(lc->uuid), originator,
+                          (unsigned long long)lc->sync_count, reset);
+#ifdef DEBUG
+               kill(getpid(), SIGUSR1);
+#endif
+               lc->sync_count = reset;
+       }
+
+       return 0;
+}
+
+static int core_status_info(struct log_c *lc __attribute__((unused)), struct dm_ulog_request *rq)
+{
+       int r;
+       char *data = (char *)rq->data;
+
+       r = sprintf(data, "1 clustered-core");
+       if (r < 0)
+               return r;
+
+       rq->data_size = r;
+
+       return 0;
+}
+
+static int disk_status_info(struct log_c *lc, struct dm_ulog_request *rq)
+{
+       int r;
+       char *data = (char *)rq->data;
+       struct stat statbuf;
+
+       if(fstat(lc->disk_fd, &statbuf)) {
+               rq->error = -errno;
+               return -errno;
+       }
+
+       r = sprintf(data, "3 clustered-disk %d:%d %c",
+                   major(statbuf.st_rdev), minor(statbuf.st_rdev),
+                   (lc->log_dev_failed) ? 'D' : 'A');
+       if (r < 0)
+               return r;
+
+       rq->data_size = r;
+
+       return 0;
+}
+
+/*
+ * clog_status_info
+ * @rq
+ *
+ */
+static int clog_status_info(struct dm_ulog_request *rq)
+{
+       int r;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               lc = get_pending_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (lc->disk_fd == -1)
+               r = core_status_info(lc, rq);
+       else
+               r = disk_status_info(lc, rq);
+
+       return r;
+}
+
+static int core_status_table(struct log_c *lc, struct dm_ulog_request *rq)
+{
+       int r;
+       char *data = (char *)rq->data;
+
+       r = sprintf(data, "clustered-core %u %s%s ",
+                   lc->region_size,
+                   (lc->sync == DEFAULTSYNC) ? "" :
+                   (lc->sync == NOSYNC) ? "nosync " : "sync ",
+                   (lc->block_on_error) ? "block_on_error" : "");
+       if (r < 0)
+               return r;
+
+       rq->data_size = r;
+       return 0;
+}
+
+static int disk_status_table(struct log_c *lc, struct dm_ulog_request *rq)
+{
+       int r;
+       char *data = (char *)rq->data;
+       struct stat statbuf;
+
+       if(fstat(lc->disk_fd, &statbuf)) {
+               rq->error = -errno;
+               return -errno;
+       }
+
+       r = sprintf(data, "clustered-disk %d:%d %u %s%s ",
+                   major(statbuf.st_rdev), minor(statbuf.st_rdev),
+                   lc->region_size,
+                   (lc->sync == DEFAULTSYNC) ? "" :
+                   (lc->sync == NOSYNC) ? "nosync " : "sync ",
+                   (lc->block_on_error) ? "block_on_error" : "");
+       if (r < 0)
+               return r;
+
+       rq->data_size = r;
+       return 0;
+}
+
+/*
+ * clog_status_table
+ * @rq
+ *
+ */
+static int clog_status_table(struct dm_ulog_request *rq)
+{
+       int r;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               lc = get_pending_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (lc->disk_fd == -1)
+               r = core_status_table(lc, rq);
+       else
+               r = disk_status_table(lc, rq);
+
+       return r;
+}
+
+/*
+ * clog_is_remote_recovering
+ * @rq
+ *
+ */
+static int clog_is_remote_recovering(struct dm_ulog_request *rq)
+{
+       uint64_t *region_p = (uint64_t *)rq->data;
+       uint64_t region = *region_p;
+       struct {
+               int64_t is_recovering;
+               uint64_t in_sync_hint;
+       } *pkg = (void *)rq->data;
+       struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+       if (!lc)
+               return -EINVAL;
+
+       if (region > lc->region_count)
+               return -EINVAL;
+
+       if (lc->recovery_halted) {
+               LOG_DBG("[%s] Recovery halted... [not remote recovering]: %llu",
+                       SHORT_UUID(lc->uuid), (unsigned long long)region);
+               pkg->is_recovering = 0;
+               pkg->in_sync_hint = lc->region_count; /* none are recovering */
+       } else {
+               pkg->is_recovering = !log_test_bit(lc->sync_bits, region);
+
+               /*
+                * Remember, 'lc->sync_search' is 1 plus the region
+                * currently being recovered.  So, we must take off 1
+                * to account for that; but only if 'sync_search > 1'.
+                */
+               pkg->in_sync_hint = lc->sync_search ? (lc->sync_search - 1) : 0;
+               LOG_DBG("[%s] Region is %s: %llu",
+                       SHORT_UUID(lc->uuid),
+                       (region == lc->recovering_region) ?
+                       "currently remote recovering" :
+                       (pkg->is_recovering) ? "pending remote recovery" :
+                       "not remote recovering", (unsigned long long)region);
+       }
+
+       if (pkg->is_recovering &&
+           (region != lc->recovering_region)) {
+               struct recovery_request *rr;
+
+               /* Already in the list? */
+               for (rr = lc->recovery_request_list; rr; rr = rr->next)
+                       if (rr->region == region)
+                               goto out;
+
+               /* Failure to allocated simply means we can't prioritize it */
+               rr = malloc(sizeof(*rr));
+               if (!rr)
+                       goto out;
+
+               LOG_DBG("[%s] Adding region to priority list: %llu",
+                       SHORT_UUID(lc->uuid), (unsigned long long)region);
+               rr->region = region;
+               rr->next = lc->recovery_request_list;
+               lc->recovery_request_list = rr;
+       }
+
+out:
+
+       rq->data_size = sizeof(*pkg);
+
+       return 0;       
+}
+
+
+/*
+ * do_request
+ * @rq: the request
+ * @server: is this request performed by the server
+ *
+ * An inability to perform this function will return an error
+ * from this function.  However, an inability to successfully
+ * perform the request will fill in the 'rq->error' field.
+ *
+ * 'rq' (or more correctly, rq->u_rq.data) should be of sufficient
+ * size to hold any returning data.  Currently, local.c uses 2kiB
+ * to hold 'rq' - leaving ~1.5kiB for return data... more than
+ * enough for all the implemented functions here.
+ *
+ * Returns: 0 on success, -EXXX on error
+ */
+int do_request(struct clog_request *rq, int server)
+{
+       int r;
+
+       if (!rq)
+               return 0;
+
+       if (rq->u_rq.error)
+               LOG_DBG("Programmer error: rq struct has error set");
+
+       switch (rq->u_rq.request_type) {
+       case DM_ULOG_CTR:
+               r = clog_ctr(&rq->u_rq);
+               break;
+       case DM_ULOG_DTR:
+               r = clog_dtr(&rq->u_rq);
+               break;
+       case DM_ULOG_PRESUSPEND:
+               r = clog_presuspend(&rq->u_rq);
+               break;
+       case DM_ULOG_POSTSUSPEND:
+               r = clog_postsuspend(&rq->u_rq);
+               break;
+       case DM_ULOG_RESUME:
+               r = clog_resume(&rq->u_rq);
+               break;
+       case DM_ULOG_GET_REGION_SIZE:
+               r = clog_get_region_size(&rq->u_rq);
+               break;
+       case DM_ULOG_IS_CLEAN:
+               r = clog_is_clean(&rq->u_rq);
+               break;
+       case DM_ULOG_IN_SYNC:
+               r = clog_in_sync(&rq->u_rq);
+               break;
+       case DM_ULOG_FLUSH:
+               r = clog_flush(&rq->u_rq, server);
+               break;
+       case DM_ULOG_MARK_REGION:
+               r = clog_mark_region(&rq->u_rq, rq->originator);
+               break;
+       case DM_ULOG_CLEAR_REGION:
+               r = clog_clear_region(&rq->u_rq, rq->originator);
+               break;
+       case DM_ULOG_GET_RESYNC_WORK:
+               r = clog_get_resync_work(&rq->u_rq, rq->originator);
+               break;
+       case DM_ULOG_SET_REGION_SYNC:
+               r = clog_set_region_sync(&rq->u_rq, rq->originator);
+               break;
+       case DM_ULOG_GET_SYNC_COUNT:
+               r = clog_get_sync_count(&rq->u_rq, rq->originator);
+               break;
+       case DM_ULOG_STATUS_INFO:
+               r = clog_status_info(&rq->u_rq);
+               break;
+       case DM_ULOG_STATUS_TABLE:
+               r = clog_status_table(&rq->u_rq);
+               break;
+       case DM_ULOG_IS_REMOTE_RECOVERING:
+               r = clog_is_remote_recovering(&rq->u_rq);
+               break;
+       default:
+               LOG_ERROR("Unknown request");
+               r = rq->u_rq.error = -EINVAL;
+               break;
+       }
+
+       if (r && !rq->u_rq.error)
+               rq->u_rq.error = r;
+       else if (r != rq->u_rq.error)
+               LOG_DBG("Warning:  error from function != rq->u_rq.error");
+
+       if (rq->u_rq.error && rq->u_rq.data_size) {
+               /* Make sure I'm handling errors correctly above */
+               LOG_DBG("Programmer error: rq->u_rq.error && rq->u_rq.data_size");
+               rq->u_rq.data_size = 0;
+       }
+
+       return 0;
+}
+
+static void print_bits(dm_bitset_t bs, int print)
+{
+       int i, size;
+       char outbuf[128];
+       unsigned char *buf = (unsigned char *)(bs + 1);
+
+       size = (*bs % 8) ? 1 : 0;
+       size += (*bs / 8);
+
+       memset(outbuf, 0, sizeof(outbuf));
+
+       for (i = 0; i < size; i++) {
+               if (!(i % 16)) {
+                       if (outbuf[0] != '\0') {
+                               if (print)
+                                       LOG_PRINT("%s", outbuf);
+                               else
+                                       LOG_DBG("%s", outbuf);
+                       }
+                       memset(outbuf, 0, sizeof(outbuf));
+                       sprintf(outbuf, "[%3d - %3d]", i, i+15);
+               }
+               sprintf(outbuf + strlen(outbuf), " %.2X", (unsigned char)buf[i]);
+       }
+       if (outbuf[0] != '\0') {
+               if (print)
+                       LOG_PRINT("%s", outbuf);
+               else
+                       LOG_DBG("%s", outbuf);
+       }
+}
+
+/* int store_bits(const char *uuid, const char *which, char **buf)*/
+int push_state(const char *uuid, uint64_t luid,
+              const char *which, char **buf, uint32_t debug_who)
+{
+       int bitset_size;
+       struct log_c *lc;
+
+       if (*buf)
+               LOG_ERROR("store_bits: *buf != NULL");
+
+       lc = get_log(uuid, luid);
+       if (!lc) {
+               LOG_ERROR("store_bits: No log found for %s", uuid);
+               return -EINVAL;
+       }
+
+       if (!strcmp(which, "recovering_region")) {
+               *buf = malloc(64); /* easily handles the 2 written numbers */
+               if (!*buf)
+                       return -ENOMEM;
+               sprintf(*buf, "%llu %u", (unsigned long long)lc->recovering_region,
+                       lc->recoverer);
+
+               LOG_SPRINT(lc, "CKPT SEND - SEQ#=X, UUID=%s, nodeid = %u:: "
+                          "recovering_region=%llu, recoverer=%u, sync_count=%llu",
+                          SHORT_UUID(lc->uuid), debug_who,
+                          (unsigned long long)lc->recovering_region,
+                          lc->recoverer,
+                          (unsigned long long)count_bits32(lc->sync_bits));
+               return 64;
+       }
+
+       /* Size in 'int's */
+       bitset_size = (*(lc->clean_bits) / DM_BITS_PER_INT) + 1;
+
+       /* Size in bytes */
+       bitset_size *= 4;
+
+       *buf = malloc(bitset_size);
+
+       if (!*buf) {
+               LOG_ERROR("store_bits: Unable to allocate memory");
+               return -ENOMEM;
+       }
+
+       if (!strncmp(which, "sync_bits", 9)) {
+               memcpy(*buf, lc->sync_bits + 1, bitset_size);
+
+               LOG_DBG("[%s] storing sync_bits (sync_count = %llu):",
+                       SHORT_UUID(uuid), (unsigned long long)
+                       count_bits32(lc->sync_bits));
+
+               print_bits(lc->sync_bits, 0);
+       } else if (!strncmp(which, "clean_bits", 9)) {
+               memcpy(*buf, lc->clean_bits + 1, bitset_size);
+
+               LOG_DBG("[%s] storing clean_bits:", SHORT_UUID(lc->uuid));
+
+               print_bits(lc->clean_bits, 0);
+       }
+
+       return bitset_size;
+}
+
+/*int load_bits(const char *uuid, const char *which, char *buf, int size)*/
+int pull_state(const char *uuid, uint64_t luid,
+              const char *which, char *buf, int size)
+{
+       int bitset_size;
+       struct log_c *lc;
+
+       if (!buf) {
+               LOG_ERROR("pull_state: buf == NULL");
+               return -EINVAL;
+       }
+
+       lc = get_log(uuid, luid);
+       if (!lc) {
+               LOG_ERROR("pull_state: No log found for %s", uuid);
+               return -EINVAL;
+       }
+
+       if (!strncmp(which, "recovering_region", 17)) {
+               sscanf(buf, "%llu %u", (unsigned long long *)&lc->recovering_region,
+                      &lc->recoverer);
+               LOG_SPRINT(lc, "CKPT INIT - SEQ#=X, UUID=%s, nodeid = X:: "
+                          "recovering_region=%llu, recoverer=%u",
+                          SHORT_UUID(lc->uuid),
+                          (unsigned long long)lc->recovering_region, lc->recoverer);
+               return 0;
+       }
+
+       /* Size in 'int's */
+       bitset_size = (*(lc->clean_bits) /DM_BITS_PER_INT) + 1;
+
+       /* Size in bytes */
+       bitset_size *= 4;
+
+       if (bitset_size != size) {
+               LOG_ERROR("pull_state(%s): bad bitset_size (%d vs %d)",
+                         which, size, bitset_size);
+               return -EINVAL;
+       }
+
+       if (!strncmp(which, "sync_bits", 9)) {
+               lc->resume_override += 1;
+               memcpy(lc->sync_bits + 1, buf, bitset_size);
+
+               LOG_DBG("[%s] loading sync_bits (sync_count = %llu):",
+                       SHORT_UUID(lc->uuid),(unsigned long long)
+                       count_bits32(lc->sync_bits));
+
+               print_bits(lc->sync_bits, 0);
+       } else if (!strncmp(which, "clean_bits", 9)) {
+               lc->resume_override += 2;
+               memcpy(lc->clean_bits + 1, buf, bitset_size);
+
+               LOG_DBG("[%s] loading clean_bits:", SHORT_UUID(lc->uuid));
+
+               print_bits(lc->sync_bits, 0);
+       }
+
+       return 0;
+}
+
+int log_get_state(struct dm_ulog_request *rq)
+{
+       struct log_c *lc;
+
+       lc = get_log(rq->uuid, rq->luid);
+       if (!lc)
+               /* FIXME Callers are ignoring this */
+               return -EINVAL;
+
+       return (int)lc->state;
+}
+
+/*
+ * log_status
+ *
+ * Returns: 1 if logs are still present, 0 otherwise
+ */
+int log_status(void)
+{
+       if (!dm_list_empty(&log_list) || !dm_list_empty(&log_pending_list))
+               return 1;
+
+       return 0;
+}
+
+void log_debug(void)
+{
+       struct log_c *lc;
+       uint64_t r;
+       int i;
+
+       LOG_ERROR("");
+       LOG_ERROR("LOG COMPONENT DEBUGGING::");
+       LOG_ERROR("Official log list:");
+       LOG_ERROR("Pending log list:");
+       dm_list_iterate_items(lc, &log_pending_list) {
+               LOG_ERROR("%s", lc->uuid);
+               LOG_ERROR("sync_bits:");
+               print_bits(lc->sync_bits, 1);
+               LOG_ERROR("clean_bits:");
+               print_bits(lc->clean_bits, 1);
+       }
+
+       dm_list_iterate_items(lc, &log_list) {
+               LOG_ERROR("%s", lc->uuid);
+               LOG_ERROR("  recoverer        : %" PRIu32, lc->recoverer);
+               LOG_ERROR("  recovering_region: %" PRIu64, lc->recovering_region);
+               LOG_ERROR("  recovery_halted  : %s", (lc->recovery_halted) ?
+                         "YES" : "NO");
+               LOG_ERROR("sync_bits:");
+               print_bits(lc->sync_bits, 1);
+               LOG_ERROR("clean_bits:");
+               print_bits(lc->clean_bits, 1);
+
+               LOG_ERROR("Validating %s::", SHORT_UUID(lc->uuid));
+               r = find_next_zero_bit(lc->sync_bits, 0);
+               LOG_ERROR("  lc->region_count = %" PRIu32, lc->region_count);
+               LOG_ERROR("  lc->sync_count = %" PRIu64, lc->sync_count);
+               LOG_ERROR("  next zero bit  = %" PRIu64, r);
+               if ((r > lc->region_count) ||
+                   ((r == lc->region_count) && (lc->sync_count > lc->region_count))) {
+                       LOG_ERROR("ADJUSTING SYNC_COUNT");
+                       lc->sync_count = lc->region_count;
+               }
+
+               LOG_ERROR("Resync request history:");
+               for (i = 0; i < RESYNC_HISTORY; i++) {
+                       lc->idx++;
+                       lc->idx = lc->idx % RESYNC_HISTORY;
+                       if (lc->resync_history[lc->idx][0] == '\0')
+                               continue;
+                       LOG_ERROR("%d:%d) %s", i, lc->idx,
+                                 lc->resync_history[lc->idx]);
+               }
+       }
+}
diff --git a/daemons/cmirrord/functions.h b/daemons/cmirrord/functions.h
new file mode 100644 (file)
index 0000000..20453f5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_CLOG_FUNCTIONS_H
+#define _LVM_CLOG_FUNCTIONS_H
+
+#include "dm-log-userspace.h"
+#include "cluster.h"
+
+#define LOG_RESUMED   1
+#define LOG_SUSPENDED 2
+
+int local_resume(struct dm_ulog_request *rq);
+int cluster_postsuspend(char *, uint64_t);
+
+int do_request(struct clog_request *rq, int server);
+int push_state(const char *uuid, uint64_t luid,
+              const char *which, char **buf, uint32_t debug_who);
+int pull_state(const char *uuid, uint64_t luid,
+              const char *which, char *buf, int size);
+
+int log_get_state(struct dm_ulog_request *rq);
+int log_status(void);
+void log_debug(void);
+
+#endif /* _LVM_CLOG_FUNCTIONS_H */
diff --git a/daemons/cmirrord/link_mon.c b/daemons/cmirrord/link_mon.c
new file mode 100644 (file)
index 0000000..80a98d0
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#include "logging.h"
+#include "link_mon.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+
+struct link_callback {
+       int fd;
+       const char *name;
+       void *data;
+       int (*callback)(void *data);
+
+       struct link_callback *next;
+};
+
+static unsigned used_pfds = 0;
+static unsigned free_pfds = 0;
+static struct pollfd *pfds = NULL;
+static struct link_callback *callbacks = NULL;
+
+int links_register(int fd, const char *name, int (*callback)(void *data), void *data)
+{
+       unsigned i;
+       struct link_callback *lc;
+
+       for (i = 0; i < used_pfds; i++) {
+               if (fd == pfds[i].fd) {
+                       LOG_ERROR("links_register: Duplicate file descriptor");
+                       return -EINVAL;
+               }
+       }
+
+       lc = malloc(sizeof(*lc));
+       if (!lc)
+               return -ENOMEM;
+
+       lc->fd = fd;
+       lc->name = name;
+       lc->data = data;
+       lc->callback = callback;
+
+       if (!free_pfds) {
+               struct pollfd *tmp;
+               tmp = realloc(pfds, sizeof(struct pollfd) * ((used_pfds*2) + 1));
+               if (!tmp) {
+                       free(lc);
+                       return -ENOMEM;
+               }
+               
+               pfds = tmp;
+               free_pfds = used_pfds + 1;
+       }
+
+       free_pfds--;
+       pfds[used_pfds].fd = fd;
+       pfds[used_pfds].events = POLLIN;
+       pfds[used_pfds].revents = 0;
+       used_pfds++;
+
+       lc->next = callbacks;
+       callbacks = lc;
+       LOG_DBG("Adding %s/%d", lc->name, lc->fd);
+       LOG_DBG(" used_pfds = %u, free_pfds = %u",
+               used_pfds, free_pfds);
+
+       return 0;
+}
+
+int links_unregister(int fd)
+{
+       unsigned i;
+       struct link_callback *p, *c;
+
+       for (i = 0; i < used_pfds; i++)
+               if (fd == pfds[i].fd) {
+                       /* entire struct is copied (overwritten) */
+                       pfds[i] = pfds[used_pfds - 1];
+                       used_pfds--;
+                       free_pfds++;
+               }
+
+       for (p = NULL, c = callbacks; c; p = c, c = c->next)
+               if (fd == c->fd) {
+                       LOG_DBG("Freeing up %s/%d", c->name, c->fd);
+                       LOG_DBG(" used_pfds = %u, free_pfds = %u",
+                               used_pfds, free_pfds);
+                       if (p)
+                               p->next = c->next;
+                       else
+                               callbacks = c->next;
+                       free(c);
+                       break;
+               }
+
+       return 0;
+}
+
+int links_monitor(void)
+{
+       unsigned i;
+       int r;
+
+       for (i = 0; i < used_pfds; i++) {
+               pfds[i].revents = 0;
+       }
+
+       r = poll(pfds, used_pfds, -1);
+       if (r <= 0)
+               return r;
+
+       r = 0;
+       /* FIXME: handle POLLHUP */
+       for (i = 0; i < used_pfds; i++)
+               if (pfds[i].revents & POLLIN) {
+                       LOG_DBG("Data ready on %d", pfds[i].fd);
+                               
+                       /* FIXME: Add this back return 1;*/
+                       r++;
+               }
+
+       return r;
+}
+
+int links_issue_callbacks(void)
+{
+       unsigned i;
+       struct link_callback *lc;
+
+       for (i = 0; i < used_pfds; i++)
+               if (pfds[i].revents & POLLIN)
+                       for (lc = callbacks; lc; lc = lc->next)
+                               if (pfds[i].fd == lc->fd) {
+                                       LOG_DBG("Issuing callback on %s/%d",
+                                               lc->name, lc->fd);
+                                       lc->callback(lc->data);
+                                       break;
+                               }
+       return 0;
+}
diff --git a/daemons/cmirrord/link_mon.h b/daemons/cmirrord/link_mon.h
new file mode 100644 (file)
index 0000000..6600f95
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_CLOG_LINK_MON_H
+#define _LVM_CLOG_LINK_MON_H
+
+int links_register(int fd, const char *name, int (*callback)(void *data), void *data);
+int links_unregister(int fd);
+int links_monitor(void);
+int links_issue_callbacks(void);
+
+#endif /* _LVM_CLOG_LINK_MON_H */
diff --git a/daemons/cmirrord/local.c b/daemons/cmirrord/local.c
new file mode 100644 (file)
index 0000000..9e076c4
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#include "logging.h"
+#include "common.h"
+#include "functions.h"
+#include "link_mon.h"
+#include "local.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/connector.h>
+#include <linux/netlink.h>
+#include <unistd.h>
+
+#ifndef CN_IDX_DM
+/* Kernel 2.6.31 is required to run this code */
+#define CN_IDX_DM                       0x7     /* Device Mapper */
+#define CN_VAL_DM_USERSPACE_LOG         0x1
+#endif
+
+static int cn_fd;  /* Connector (netlink) socket fd */
+static char recv_buf[2048];
+static char send_buf[2048];
+
+
+/* FIXME: merge this function with kernel_send_helper */
+static int kernel_ack(uint32_t seq, int error)
+{
+       int r;
+       struct nlmsghdr *nlh = (struct nlmsghdr *)send_buf;
+       struct cn_msg *msg = NLMSG_DATA(nlh);
+
+       if (error < 0) {
+               LOG_ERROR("Programmer error: error codes must be positive");
+               return -EINVAL;
+       }
+
+       memset(send_buf, 0, sizeof(send_buf));
+
+       nlh->nlmsg_seq = 0;
+       nlh->nlmsg_pid = getpid();
+       nlh->nlmsg_type = NLMSG_DONE;
+       nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg));
+       nlh->nlmsg_flags = 0;
+
+       msg->len = 0;
+       msg->id.idx = CN_IDX_DM;
+       msg->id.val = CN_VAL_DM_USERSPACE_LOG;
+       msg->seq = seq;
+       msg->ack = error;
+
+       r = send(cn_fd, nlh, NLMSG_LENGTH(sizeof(struct cn_msg)), 0);
+       /* FIXME: do better error processing */
+       if (r <= 0)
+               return -EBADE;
+
+       return 0;
+}
+
+
+/*
+ * kernel_recv
+ * @rq: the newly allocated request from kernel
+ *
+ * Read requests from the kernel and allocate space for the new request.
+ * If there is no request from the kernel, *rq is NULL.
+ *
+ * This function is not thread safe due to returned stack pointer.  In fact,
+ * the returned pointer must not be in-use when this function is called again.
+ *
+ * Returns: 0 on success, -EXXX on error
+ */
+static int kernel_recv(struct clog_request **rq)
+{
+       int r = 0;
+       ssize_t len;
+       char *foo;
+       struct cn_msg *msg;
+       struct dm_ulog_request *u_rq;
+       struct nlmsghdr *nlmsg_h;
+
+       *rq = NULL;
+       memset(recv_buf, 0, sizeof(recv_buf));
+
+       len = recv(cn_fd, recv_buf, sizeof(recv_buf), 0);
+       if (len < 0) {
+               LOG_ERROR("Failed to recv message from kernel");
+               r = -errno;
+               goto fail;
+       }
+
+       nlmsg_h = (struct nlmsghdr *)recv_buf;
+       switch (nlmsg_h->nlmsg_type) {
+       case NLMSG_ERROR:
+               LOG_ERROR("Unable to recv message from kernel: NLMSG_ERROR");
+               r = -EBADE;
+               goto fail;
+       case NLMSG_DONE:
+               msg = (struct cn_msg *)NLMSG_DATA((struct nlmsghdr *)recv_buf);
+               len -= (ssize_t)sizeof(struct nlmsghdr);
+
+               if (len < (ssize_t)sizeof(struct cn_msg)) {
+                       LOG_ERROR("Incomplete request from kernel received");
+                       r = -EBADE;
+                       goto fail;
+               }
+
+               if (msg->len > DM_ULOG_REQUEST_SIZE) {
+                       LOG_ERROR("Not enough space to receive kernel request (%d/%d)",
+                                 msg->len, DM_ULOG_REQUEST_SIZE);
+                       r = -EBADE;
+                       goto fail;
+               }
+
+               if (!msg->len)
+                       LOG_ERROR("Zero length message received");
+
+               len -= (ssize_t)sizeof(struct cn_msg);
+
+               if (len < msg->len)
+                       LOG_ERROR("len = %zd, msg->len = %" PRIu16, len, msg->len);
+
+               msg->data[msg->len] = '\0'; /* Cleaner way to ensure this? */
+               u_rq = (struct dm_ulog_request *)msg->data;
+
+               if (!u_rq->request_type) {
+                       LOG_DBG("Bad transmission, requesting resend [%u]",
+                               msg->seq);
+                       r = -EAGAIN;
+
+                       if (kernel_ack(msg->seq, EAGAIN)) {
+                               LOG_ERROR("Failed to NACK kernel transmission [%u]",
+                                         msg->seq);
+                               r = -EBADE;
+                       }
+               }
+
+               /*
+                * Now we've got sizeof(struct cn_msg) + sizeof(struct nlmsghdr)
+                * worth of space that precede the request structure from the
+                * kernel.  Since that space isn't going to be used again, we
+                * can take it for our purposes; rather than allocating a whole
+                * new structure and doing a memcpy.
+                *
+                * We should really make sure 'clog_request' doesn't grow
+                * beyond what is available to us, but we need only check it
+                * once... perhaps at compile time?
+                */
+               foo = (char *)u_rq;
+               foo -= (sizeof(struct clog_request) - sizeof(struct dm_ulog_request));
+               *rq = (struct clog_request *) foo;
+
+               /* Clear the wrapper container fields */
+               memset(*rq, 0, (size_t)((char *)u_rq - (char *)(*rq)));
+               break;
+       default:
+               LOG_ERROR("Unknown nlmsg_type");
+               r = -EBADE;
+       }
+
+fail:
+       if (r)
+               *rq = NULL;
+
+       return (r == -EAGAIN) ? 0 : r;
+}
+
+static int kernel_send_helper(void *data, uint16_t out_size)
+{
+       int r;
+       struct nlmsghdr *nlh;
+       struct cn_msg *msg;
+
+       memset(send_buf, 0, sizeof(send_buf));
+
+       nlh = (struct nlmsghdr *)send_buf;
+       nlh->nlmsg_seq = 0;  /* FIXME: Is this used? */
+       nlh->nlmsg_pid = getpid();
+       nlh->nlmsg_type = NLMSG_DONE;
+       nlh->nlmsg_len = NLMSG_LENGTH(out_size + sizeof(struct cn_msg));
+       nlh->nlmsg_flags = 0;
+
+       msg = NLMSG_DATA(nlh);
+       memcpy(msg->data, data, out_size);
+       msg->len = out_size;
+       msg->id.idx = CN_IDX_DM;
+       msg->id.val = CN_VAL_DM_USERSPACE_LOG;
+       msg->seq = 0;
+
+       r = send(cn_fd, nlh, NLMSG_LENGTH(out_size + sizeof(struct cn_msg)), 0);
+       /* FIXME: do better error processing */
+       if (r <= 0)
+               return -EBADE;
+
+       return 0;
+}
+
+/*
+ * do_local_work
+ *
+ * Any processing errors are placed in the 'rq'
+ * structure to be reported back to the kernel.
+ * It may be pointless for this function to
+ * return an int.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int do_local_work(void *data __attribute__((unused)))
+{
+       int r;
+       struct clog_request *rq;
+       struct dm_ulog_request *u_rq = NULL;
+
+       r = kernel_recv(&rq);
+       if (r)
+               return r;
+
+       if (!rq)
+               return 0;
+
+       u_rq = &rq->u_rq;
+       LOG_DBG("[%s]  Request from kernel received: [%s/%u]",
+               SHORT_UUID(u_rq->uuid), RQ_TYPE(u_rq->request_type),
+               u_rq->seq);
+       switch (u_rq->request_type) {
+       case DM_ULOG_CTR:
+       case DM_ULOG_DTR:
+       case DM_ULOG_GET_REGION_SIZE:
+       case DM_ULOG_IN_SYNC:
+       case DM_ULOG_GET_SYNC_COUNT:
+       case DM_ULOG_STATUS_INFO:
+       case DM_ULOG_STATUS_TABLE:
+       case DM_ULOG_PRESUSPEND:
+               /* We do not specify ourselves as server here */
+               r = do_request(rq, 0);
+               if (r)
+                       LOG_DBG("Returning failed request to kernel [%s]",
+                               RQ_TYPE(u_rq->request_type));
+               r = kernel_send(u_rq);
+               if (r)
+                       LOG_ERROR("Failed to respond to kernel [%s]",
+                                 RQ_TYPE(u_rq->request_type));
+                       
+               break;
+       case DM_ULOG_RESUME:
+               /*
+                * Resume is a special case that requires a local
+                * component to join the CPG, and a cluster component
+                * to handle the request.
+                */
+               r = local_resume(u_rq);
+               if (r) {
+                       LOG_DBG("Returning failed request to kernel [%s]",
+                               RQ_TYPE(u_rq->request_type));
+                       r = kernel_send(u_rq);
+                       if (r)
+                               LOG_ERROR("Failed to respond to kernel [%s]",
+                                         RQ_TYPE(u_rq->request_type));
+                       break;
+               }
+               /* ELSE, fall through */
+       case DM_ULOG_IS_CLEAN:
+       case DM_ULOG_FLUSH:
+       case DM_ULOG_MARK_REGION:
+       case DM_ULOG_GET_RESYNC_WORK:
+       case DM_ULOG_SET_REGION_SYNC:
+       case DM_ULOG_IS_REMOTE_RECOVERING:
+       case DM_ULOG_POSTSUSPEND:
+               r = cluster_send(rq);
+               if (r) {
+                       u_rq->data_size = 0;
+                       u_rq->error = r;
+                       kernel_send(u_rq);
+               }
+
+               break;
+       case DM_ULOG_CLEAR_REGION:
+               r = kernel_ack(u_rq->seq, 0);
+
+               r = cluster_send(rq);
+               if (r) {
+                       /*
+                        * FIXME: store error for delivery on flush
+                        *        This would allow us to optimize MARK_REGION
+                        *        too.
+                        */
+               }
+
+               break;
+       default:
+               LOG_ERROR("Invalid log request received (%u), ignoring.",
+                         u_rq->request_type);
+
+               return 0;
+       }
+
+       if (r && !u_rq->error)
+               u_rq->error = r;
+
+       return r;
+}
+
+/*
+ * kernel_send
+ * @u_rq: result to pass back to kernel
+ *
+ * This function returns the u_rq structure
+ * (containing the results) to the kernel.
+ * It then frees the structure.
+ *
+ * WARNING: should the structure be freed if
+ * there is an error?  I vote 'yes'.  If the
+ * kernel doesn't get the response, it should
+ * resend the request.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+int kernel_send(struct dm_ulog_request *u_rq)
+{
+       int r;
+       uint16_t size;
+
+       if (!u_rq)
+               return -EINVAL;
+
+       size = (uint16_t)(sizeof(struct dm_ulog_request) + u_rq->data_size);
+
+       if (!u_rq->data_size && !u_rq->error) {
+               /* An ACK is all that is needed */
+
+               /* FIXME: add ACK code */
+       } else if (size > DM_ULOG_REQUEST_SIZE) {
+               /*
+                * If we gotten here, we've already overrun
+                * our allotted space somewhere.
+                *
+                * We must do something, because the kernel
+                * is waiting for a response.
+                */
+               LOG_ERROR("Not enough space to respond to server");
+               u_rq->error = -ENOSPC;
+               size = sizeof(struct dm_ulog_request);
+       }
+
+       r = kernel_send_helper(u_rq, size);
+       if (r)
+               LOG_ERROR("Failed to send msg to kernel.");
+
+       return r;
+}
+
+/*
+ * init_local
+ *
+ * Initialize kernel communication socket (netlink)
+ *
+ * Returns: 0 on success, values from common.h on failure
+ */
+int init_local(void)
+{
+       int r = 0;
+       unsigned opt;
+       struct sockaddr_nl addr;
+
+       cn_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+       if (cn_fd < 0)
+               return EXIT_KERNEL_SOCKET;
+
+       /* memset to fix valgrind complaint */
+       memset(&addr, 0, sizeof(struct sockaddr_nl));
+
+       addr.nl_family = AF_NETLINK;
+       addr.nl_groups = CN_IDX_DM;
+       addr.nl_pid = 0;
+
+       r = bind(cn_fd, (struct sockaddr *) &addr, sizeof(addr));
+       if (r < 0) {
+               close(cn_fd);
+               return EXIT_KERNEL_BIND;
+       }
+
+       opt = addr.nl_groups;
+       r = setsockopt(cn_fd, 270, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
+       if (r) {
+               close(cn_fd);
+               return EXIT_KERNEL_SETSOCKOPT;
+       }
+
+       /*
+       r = fcntl(cn_fd, F_SETFL, FNDELAY);
+       */
+
+       links_register(cn_fd, "local", do_local_work, NULL);
+
+       return 0;
+}
+
+/*
+ * cleanup_local
+ *
+ * Clean up before exiting
+ */
+void cleanup_local(void)
+{
+       links_unregister(cn_fd);
+       close(cn_fd);
+}
diff --git a/daemons/cmirrord/local.h b/daemons/cmirrord/local.h
new file mode 100644 (file)
index 0000000..91298be
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_CLOG_LOCAL_H
+#define _LVM_CLOG_LOCAL_H
+
+int init_local(void);
+void cleanup_local(void);
+
+int kernel_send(struct dm_ulog_request *rq);
+
+#endif /* _LVM_CLOG_LOCAL_H */
diff --git a/daemons/cmirrord/logging.c b/daemons/cmirrord/logging.c
new file mode 100644 (file)
index 0000000..fef114a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#include "logging.h"
+
+const char *__rq_types_off_by_one[] = {
+       "DM_ULOG_CTR",
+       "DM_ULOG_DTR",
+       "DM_ULOG_PRESUSPEND",
+       "DM_ULOG_POSTSUSPEND",
+       "DM_ULOG_RESUME",
+       "DM_ULOG_GET_REGION_SIZE",
+       "DM_ULOG_IS_CLEAN",
+       "DM_ULOG_IN_SYNC",
+       "DM_ULOG_FLUSH",
+       "DM_ULOG_MARK_REGION",
+       "DM_ULOG_CLEAR_REGION",
+       "DM_ULOG_GET_RESYNC_WORK",
+       "DM_ULOG_SET_REGION_SYNC",
+       "DM_ULOG_GET_SYNC_COUNT",
+       "DM_ULOG_STATUS_INFO",
+       "DM_ULOG_STATUS_TABLE",
+       "DM_ULOG_IS_REMOTE_RECOVERING",
+       NULL
+};
+
+int log_tabbing = 0;
+int log_is_open = 0;
+
+/*
+ * Variables for various conditional logging
+ */
+#ifdef MEMB
+int log_membership_change = 1;
+#else
+int log_membership_change = 0;
+#endif
+
+#ifdef CKPT
+int log_checkpoint = 1;
+#else
+int log_checkpoint = 0;
+#endif
+
+#ifdef RESEND
+int log_resend_requests = 1;
+#else
+int log_resend_requests = 0;
+#endif
diff --git a/daemons/cmirrord/logging.h b/daemons/cmirrord/logging.h
new file mode 100644 (file)
index 0000000..40dd462
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_CLOG_LOGGING_H
+#define _LVM_CLOG_LOGGING_H
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <syslog.h>
+
+/* SHORT_UUID - print last 8 chars of a string */
+#define SHORT_UUID(x) (strlen(x) > 8) ? ((x) + (strlen(x) - 8)) : (x)
+
+extern const char *__rq_types_off_by_one[];
+#define RQ_TYPE(x) __rq_types_off_by_one[(x) - 1]
+
+extern int log_tabbing;
+extern int log_is_open;
+extern int log_membership_change;
+extern int log_checkpoint;
+extern int log_resend_requests;
+
+#define LOG_OPEN(ident, option, facility) do { \
+               openlog(ident, option, facility); \
+               log_is_open = 1;                  \
+       } while (0)
+
+#define LOG_CLOSE(void) do { \
+               log_is_open = 0; \
+               closelog();      \
+       } while (0)
+
+#define LOG_OUTPUT(level, f, arg...) do {                              \
+               int __i;                                                \
+               char __buffer[16];                                      \
+               FILE *fp = (level > LOG_NOTICE) ? stderr : stdout;      \
+               if (log_is_open) {                                      \
+                       for (__i = 0; (__i < log_tabbing) && (__i < 15); __i++) \
+                               __buffer[__i] = '\t';                   \
+                       __buffer[__i] = '\0';                           \
+                       syslog(level, "%s" f "\n", __buffer, ## arg);   \
+               } else {                                                \
+                       for (__i = 0; __i < log_tabbing; __i++)         \
+                               fprintf(fp, "\t");                      \
+                       fprintf(fp, f "\n", ## arg);                    \
+               }                                                       \
+       } while (0)
+
+
+#ifdef DEBUG
+#define LOG_DBG(f, arg...) LOG_OUTPUT(LOG_DEBUG, f, ## arg)
+#else /* DEBUG */
+#define LOG_DBG(f, arg...)
+#endif /* DEBUG */
+
+#define LOG_COND(__X, f, arg...) do {\
+               if (__X) {           \
+                       LOG_OUTPUT(LOG_NOTICE, f, ## arg); \
+               } \
+       } while (0)
+#define LOG_PRINT(f, arg...) LOG_OUTPUT(LOG_NOTICE, f, ## arg)
+#define LOG_ERROR(f, arg...) LOG_OUTPUT(LOG_ERR, f, ## arg)
+
+#endif /* _LVM_CLOG_LOGGING_H */
diff --git a/daemons/dmeventd/.exported_symbols b/daemons/dmeventd/.exported_symbols
new file mode 100644 (file)
index 0000000..25690c8
--- /dev/null
@@ -0,0 +1,3 @@
+init_fifos
+fini_fifos
+daemon_talk
diff --git a/daemons/dmeventd/Makefile.in b/daemons/dmeventd/Makefile.in
new file mode 100644 (file)
index 0000000..e99e089
--- /dev/null
@@ -0,0 +1,108 @@
+#
+# Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the device-mapper userspace tools.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU Lesser General Public License v.2.1.
+#
+# 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = libdevmapper-event.c
+SOURCES2 = dmeventd.c
+
+TARGETS = dmeventd
+
+.PHONY: install_lib_dynamic install_lib_static install_include \
+       install_pkgconfig install_dmeventd_dynamic install_dmeventd_static \
+       install_lib install_dmeventd
+
+INSTALL_DMEVENTD_TARGETS = install_dmeventd_dynamic
+INSTALL_LIB_TARGETS = install_lib_dynamic
+
+LIB_NAME = libdevmapper-event
+ifeq ("@STATIC_LINK@", "yes")
+  LIB_STATIC = $(LIB_NAME).a
+  TARGETS += $(LIB_STATIC) dmeventd.static
+  INSTALL_DMEVENTD_TARGETS += install_dmeventd_static
+  INSTALL_LIB_TARGETS += install_lib_static
+endif
+
+LIB_VERSION = $(LIB_VERSION_DM)
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+
+CLEAN_TARGETS = dmeventd.static $(LIB_NAME).a
+
+ifneq ($(MAKECMDGOALS),device-mapper)
+  SUBDIRS+=plugins
+endif
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+CFLOW_TARGET = dmeventd
+
+EXPORTED_HEADER = $(srcdir)/libdevmapper-event.h
+EXPORTED_FN_PREFIX = dm_event
+
+include $(top_builddir)/make.tmpl
+
+all: device-mapper
+device-mapper: $(TARGETS)
+
+LIBS += -ldevmapper
+LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
+
+dmeventd: $(LIB_SHARED) dmeventd.o
+       $(CC) $(CFLAGS) $(LDFLAGS) -L. -o $@ dmeventd.o \
+       $(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
+
+dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
+       dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS)
+
+ifeq ("@PKGCONFIG@", "yes")
+  INSTALL_LIB_TARGETS += install_pkgconfig
+endif
+
+ifneq ("$(CFLOW_CMD)", "")
+CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
+-include $(top_builddir)/libdm/libdevmapper.cflow
+-include $(top_builddir)/lib/liblvm-internal.cflow
+-include $(top_builddir)/lib/liblvm2cmd.cflow
+-include $(top_builddir)/daemons/dmeventd/$(LIB_NAME).cflow
+-include $(top_builddir)/daemons/dmeventd/plugins/mirror/$(LIB_NAME)-lvm2mirror.cflow
+endif
+
+install_include: $(srcdir)/libdevmapper-event.h
+       $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_pkgconfig: libdevmapper-event.pc
+       $(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper-event.pc
+
+install_lib_dynamic: install_lib_shared
+
+install_lib_static: $(LIB_STATIC)
+       $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
+
+install_lib: $(INSTALL_LIB_TARGETS)
+
+install_dmeventd_dynamic: dmeventd
+       $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
+
+install_dmeventd_static: dmeventd.static
+       $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+
+install_dmeventd: $(INSTALL_DMEVENTD_TARGETS)
+
+install: install_include install_lib install_dmeventd
+
+install_device-mapper: install_include install_lib install_dmeventd
+
+DISTCLEAN_TARGETS += libdevmapper-event.pc .exported_symbols_generated
diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c
new file mode 100644 (file)
index 0000000..2b454f9
--- /dev/null
@@ -0,0 +1,1883 @@
+/*
+ * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * dmeventd - dm event daemon to monitor active mapped devices
+ */
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+#include "libdevmapper.h"
+#include "libdevmapper-event.h"
+#include "dmeventd.h"
+//#include "libmultilog.h"
+#include "dm-logging.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <signal.h>
+#include <arpa/inet.h>         /* for htonl, ntohl */
+
+#ifdef linux
+#  include <malloc.h>
+
+#  define OOM_ADJ_FILE "/proc/self/oom_adj"
+
+/* From linux/oom.h */
+#  define OOM_DISABLE (-17)
+#  define OOM_ADJUST_MIN (-16)
+
+#endif
+
+/* FIXME We use syslog for now, because multilog is not yet implemented */
+#include <syslog.h>
+
+static volatile sig_atomic_t _exit_now = 0;    /* set to '1' when signal is given to exit */
+static volatile sig_atomic_t _thread_registries_empty = 1;     /* registries are empty initially */
+
+/* List (un)link macros. */
+#define        LINK(x, head)           dm_list_add(head, &(x)->list)
+#define        LINK_DSO(dso)           LINK(dso, &_dso_registry)
+#define        LINK_THREAD(thread)     LINK(thread, &_thread_registry)
+
+#define        UNLINK(x)               dm_list_del(&(x)->list)
+#define        UNLINK_DSO(x)           UNLINK(x)
+#define        UNLINK_THREAD(x)        UNLINK(x)
+
+#define DAEMON_NAME "dmeventd"
+
+/*
+  Global mutex for thread list access. Has to be held when:
+  - iterating thread list
+  - adding or removing elements from thread list
+  - changing or reading thread_status's fields:
+    processing, status, events
+  Use _lock_mutex() and _unlock_mutex() to hold/release it
+*/
+static pthread_mutex_t _global_mutex;
+
+/*
+  There are three states a thread can attain (see struct
+  thread_status, field int status):
+
+  - DM_THREAD_RUNNING: thread has started up and is either working or
+  waiting for events... transitions to either SHUTDOWN or DONE
+  - DM_THREAD_SHUTDOWN: thread is still doing something, but it is
+  supposed to terminate (and transition to DONE) as soon as it
+  finishes whatever it was doing at the point of flipping state to
+  SHUTDOWN... the thread is still on the thread list
+  - DM_THREAD_DONE: thread has terminated and has been moved over to
+  unused thread list, cleanup pending
+ */
+#define DM_THREAD_RUNNING  0
+#define DM_THREAD_SHUTDOWN 1
+#define DM_THREAD_DONE     2
+
+#define THREAD_STACK_SIZE (300*1024)
+
+#define DEBUGLOG(fmt, args...) _debuglog(fmt, ## args)
+
+int dmeventd_debug = 0;
+static int _foreground = 0;
+static int _restart = 0;
+static char **_initial_registrations = 0;
+
+/* Data kept about a DSO. */
+struct dso_data {
+       struct dm_list list;
+
+       char *dso_name;         /* DSO name (eg, "evms", "dmraid", "lvm2"). */
+
+       void *dso_handle;       /* Opaque handle as returned from dlopen(). */
+       unsigned int ref_count; /* Library reference count. */
+
+       /*
+        * Event processing.
+        *
+        * The DSO can do whatever appropriate steps if an event
+        * happens such as changing the mapping in case a mirror
+        * fails, update the application metadata etc.
+        *
+        * This function gets a dm_task that is a result of
+        * DM_DEVICE_WAITEVENT ioctl (results equivalent to
+        * DM_DEVICE_STATUS). It should not destroy it.
+        * The caller must dispose of the task.
+        */
+       void (*process_event)(struct dm_task *dmt, enum dm_event_mask event, void **user);
+
+       /*
+        * Device registration.
+        *
+        * When an application registers a device for an event, the DSO
+        * can carry out appropriate steps so that a later call to
+        * the process_event() function is sane (eg, read metadata
+        * and activate a mapping).
+        */
+       int (*register_device)(const char *device, const char *uuid, int major,
+                              int minor, void **user);
+
+       /*
+        * Device unregistration.
+        *
+        * In case all devices of a mapping (eg, RAID10) are unregistered
+        * for events, the DSO can recognize this and carry out appropriate
+        * steps (eg, deactivate mapping, metadata update).
+        */
+       int (*unregister_device)(const char *device, const char *uuid,
+                                int major, int minor, void **user);
+};
+static DM_LIST_INIT(_dso_registry);
+
+/* Structure to keep parsed register variables from client message. */
+struct message_data {
+       char *id;
+       char *dso_name;         /* Name of DSO. */
+       char *device_uuid;      /* Mapped device path. */
+       union {
+               char *str;      /* Events string as fetched from message. */
+               enum dm_event_mask field;       /* Events bitfield. */
+       } events;
+       union {
+               char *str;
+               uint32_t secs;
+       } timeout;
+       struct dm_event_daemon_message *msg;    /* Pointer to message buffer. */
+};
+
+/*
+ * Housekeeping of thread+device states.
+ *
+ * One thread per mapped device which can block on it until an event
+ * occurs and the event processing function of the DSO gets called.
+ */
+struct thread_status {
+       struct dm_list list;
+
+       pthread_t thread;
+
+       struct dso_data *dso_data;      /* DSO this thread accesses. */
+
+       struct {
+               char *uuid;
+               char *name;
+               int major, minor;
+       } device;
+       uint32_t event_nr;      /* event number */
+       int processing;         /* Set when event is being processed */
+
+       int status;             /* see DM_THREAD_{RUNNING,SHUTDOWN,DONE}
+                                  constants above */
+       enum dm_event_mask events;      /* bitfield for event filter. */
+       enum dm_event_mask current_events;      /* bitfield for occured events. */
+       struct dm_task *current_task;
+       time_t next_time;
+       uint32_t timeout;
+       struct dm_list timeout_list;
+       void *dso_private; /* dso per-thread status variable */
+};
+static DM_LIST_INIT(_thread_registry);
+static DM_LIST_INIT(_thread_registry_unused);
+
+static int _timeout_running;
+static DM_LIST_INIT(_timeout_registry);
+static pthread_mutex_t _timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER;
+
+static void _debuglog(const char *fmt, ...)
+{
+        time_t P;
+        va_list ap;
+        if (!_foreground)
+                return;
+        va_start(ap,fmt);
+
+        time(&P);
+        fprintf(stderr, "dmeventd[%p]: %.15s ", (void *) pthread_self(), ctime(&P)+4 );
+        vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+
+        va_end(ap);
+}
+
+/* Allocate/free the status structure for a monitoring thread. */
+static struct thread_status *_alloc_thread_status(struct message_data *data,
+                                                 struct dso_data *dso_data)
+{
+       struct thread_status *ret = (typeof(ret)) dm_zalloc(sizeof(*ret));
+
+       if (!ret)
+               return NULL;
+
+       if (!(ret->device.uuid = dm_strdup(data->device_uuid))) {
+               dm_free(ret);
+               return NULL;
+       }
+
+       ret->current_task = NULL;
+       ret->device.name = NULL;
+       ret->device.major = ret->device.minor = 0;
+       ret->dso_data = dso_data;
+       ret->events = data->events.field;
+       ret->timeout = data->timeout.secs;
+       dm_list_init(&ret->timeout_list);
+
+       return ret;
+}
+
+static void _lib_put(struct dso_data *data);
+static void _free_thread_status(struct thread_status *thread)
+{
+       _lib_put(thread->dso_data);
+       if (thread->current_task)
+               dm_task_destroy(thread->current_task);
+       dm_free(thread->device.uuid);
+       dm_free(thread->device.name);
+       dm_free(thread);
+}
+
+/* Allocate/free DSO data. */
+static struct dso_data *_alloc_dso_data(struct message_data *data)
+{
+       struct dso_data *ret = (typeof(ret)) dm_zalloc(sizeof(*ret));
+
+       if (!ret)
+               return NULL;
+
+       if (!(ret->dso_name = dm_strdup(data->dso_name))) {
+               dm_free(ret);
+               return NULL;
+       }
+
+       return ret;
+}
+
+/* Create a device monitoring thread. */
+static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void *arg)
+{
+       pthread_attr_t attr;
+       pthread_attr_init(&attr);
+       /*
+        * We use a smaller stack since it gets preallocated in its entirety
+        */
+       pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE);
+       return pthread_create(t, &attr, fun, arg);
+}
+
+static void _free_dso_data(struct dso_data *data)
+{
+       dm_free(data->dso_name);
+       dm_free(data);
+}
+
+/*
+ * Fetch a string off src and duplicate it into *ptr.
+ * Pay attention to zero-length strings.
+ */
+/* FIXME? move to libdevmapper to share with the client lib (need to
+   make delimiter a parameter then) */
+static int _fetch_string(char **ptr, char **src, const int delimiter)
+{
+       int ret = 0;
+       char *p;
+       size_t len;
+
+       if ((p = strchr(*src, delimiter)))
+               *p = 0;
+
+       if ((*ptr = dm_strdup(*src))) {
+               if ((len = strlen(*ptr)))
+                       *src += len;
+               else {
+                       dm_free(*ptr);
+                       *ptr = NULL;
+               }
+
+               (*src)++;
+               ret = 1;
+       }
+
+       if (p)
+               *p = delimiter;
+
+       return ret;
+}
+
+/* Free message memory. */
+static void _free_message(struct message_data *message_data)
+{
+       dm_free(message_data->id);
+       dm_free(message_data->dso_name);
+
+       dm_free(message_data->device_uuid);
+
+}
+
+/* Parse a register message from the client. */
+static int _parse_message(struct message_data *message_data)
+{
+       int ret = 0;
+       char *p = message_data->msg->data;
+       struct dm_event_daemon_message *msg = message_data->msg;
+
+       if (!msg->data)
+               return 0;
+
+       /*
+        * Retrieve application identifier, mapped device
+        * path and events # string from message.
+        */
+       if (_fetch_string(&message_data->id, &p, ' ') &&
+           _fetch_string(&message_data->dso_name, &p, ' ') &&
+           _fetch_string(&message_data->device_uuid, &p, ' ') &&
+           _fetch_string(&message_data->events.str, &p, ' ') &&
+           _fetch_string(&message_data->timeout.str, &p, ' ')) {
+               if (message_data->events.str) {
+                       enum dm_event_mask i = atoi(message_data->events.str);
+
+                       /*
+                        * Free string representaion of events.
+                        * Not needed an more.
+                        */
+                       dm_free(message_data->events.str);
+                       message_data->events.field = i;
+               }
+               if (message_data->timeout.str) {
+                       uint32_t secs = atoi(message_data->timeout.str);
+                       dm_free(message_data->timeout.str);
+                       message_data->timeout.secs = secs ? secs :
+                           DM_EVENT_DEFAULT_TIMEOUT;
+               }
+
+               ret = 1;
+       }
+
+       dm_free(msg->data);
+       msg->data = NULL;
+       msg->size = 0;
+       return ret;
+};
+
+/* Global mutex to lock access to lists et al. See _global_mutex
+   above. */
+static int _lock_mutex(void)
+{
+       return pthread_mutex_lock(&_global_mutex);
+}
+
+static int _unlock_mutex(void)
+{
+       return pthread_mutex_unlock(&_global_mutex);
+}
+
+/* Check, if a device exists. */
+static int _fill_device_data(struct thread_status *ts)
+{
+       struct dm_task *dmt;
+       struct dm_info dmi;
+
+       if (!ts->device.uuid)
+               return 0;
+
+       ts->device.name = NULL;
+       ts->device.major = ts->device.minor = 0;
+
+       dmt = dm_task_create(DM_DEVICE_INFO);
+       if (!dmt)
+               return 0;
+
+       dm_task_set_uuid(dmt, ts->device.uuid);
+       if (!dm_task_run(dmt))
+               goto fail;
+
+       ts->device.name = dm_strdup(dm_task_get_name(dmt));
+       if (!ts->device.name)
+               goto fail;
+
+       if (!dm_task_get_info(dmt, &dmi))
+               goto fail;
+
+       ts->device.major = dmi.major;
+       ts->device.minor = dmi.minor;
+
+       dm_task_destroy(dmt);
+       return 1;
+
+      fail:
+       dm_task_destroy(dmt);
+       dm_free(ts->device.name);
+       return 0;
+}
+
+/*
+ * Find an existing thread for a device.
+ *
+ * Mutex must be held when calling this.
+ */
+static struct thread_status *_lookup_thread_status(struct message_data *data)
+{
+       struct thread_status *thread;
+
+       dm_list_iterate_items(thread, &_thread_registry)
+           if (!strcmp(data->device_uuid, thread->device.uuid))
+               return thread;
+
+       return NULL;
+}
+
+static int _get_status(struct message_data *message_data)
+{
+       struct dm_event_daemon_message *msg = message_data->msg;
+       struct thread_status *thread;
+       int i = 0, j = 0;
+       int ret = -1;
+       int count = dm_list_size(&_thread_registry);
+       int size = 0, current = 0;
+       char *buffers[count];
+       char *message;
+
+       dm_free(msg->data);
+
+       for (i = 0; i < count; ++i)
+               buffers[i] = NULL;
+
+       i = 0;
+       _lock_mutex();
+       dm_list_iterate_items(thread, &_thread_registry) {
+               if ((current = dm_asprintf(buffers + i, "0:%d %s %s %u %" PRIu32 ";",
+                                          i, thread->dso_data->dso_name,
+                                          thread->device.uuid, thread->events,
+                                          thread->timeout)) < 0) {
+                       _unlock_mutex();
+                       goto out;
+               }
+               ++ i;
+               size += current;
+       }
+       _unlock_mutex();
+
+       msg->size = size + strlen(message_data->id) + 1;
+       msg->data = dm_malloc(msg->size);
+       if (!msg->data)
+               goto out;
+       *msg->data = 0;
+
+       message = msg->data;
+       strcpy(message, message_data->id);
+       message += strlen(message_data->id);
+       *message = ' ';
+       message ++;
+       for (j = 0; j < i; ++j) {
+               strcpy(message, buffers[j]);
+               message += strlen(buffers[j]);
+       }
+
+       ret = 0;
+ out:
+       for (j = 0; j < i; ++j)
+               dm_free(buffers[j]);
+       return ret;
+
+}
+
+/* Cleanup at exit. */
+static void _exit_dm_lib(void)
+{
+       dm_lib_release();
+       dm_lib_exit();
+}
+
+static void _exit_timeout(void *unused __attribute__((unused)))
+{
+       _timeout_running = 0;
+       pthread_mutex_unlock(&_timeout_mutex);
+}
+
+/* Wake up monitor threads every so often. */
+static void *_timeout_thread(void *unused __attribute__((unused)))
+{
+       struct timespec timeout;
+       time_t curr_time;
+
+       timeout.tv_nsec = 0;
+       pthread_cleanup_push(_exit_timeout, NULL);
+       pthread_mutex_lock(&_timeout_mutex);
+
+       while (!dm_list_empty(&_timeout_registry)) {
+               struct thread_status *thread;
+
+               timeout.tv_sec = 0;
+               curr_time = time(NULL);
+
+               dm_list_iterate_items_gen(thread, &_timeout_registry, timeout_list) {
+                       if (thread->next_time <= curr_time) {
+                               thread->next_time = curr_time + thread->timeout;
+                               pthread_kill(thread->thread, SIGALRM);
+                       }
+
+                       if (thread->next_time < timeout.tv_sec || !timeout.tv_sec)
+                               timeout.tv_sec = thread->next_time;
+               }
+
+               pthread_cond_timedwait(&_timeout_cond, &_timeout_mutex,
+                                      &timeout);
+       }
+
+       pthread_cleanup_pop(1);
+
+       return NULL;
+}
+
+static int _register_for_timeout(struct thread_status *thread)
+{
+       int ret = 0;
+
+       pthread_mutex_lock(&_timeout_mutex);
+
+       thread->next_time = time(NULL) + thread->timeout;
+
+       if (dm_list_empty(&thread->timeout_list)) {
+               dm_list_add(&_timeout_registry, &thread->timeout_list);
+               if (_timeout_running)
+                       pthread_cond_signal(&_timeout_cond);
+       }
+
+       if (!_timeout_running) {
+               pthread_t timeout_id;
+
+               if (!(ret = -_pthread_create_smallstack(&timeout_id, _timeout_thread, NULL)))
+                       _timeout_running = 1;
+       }
+
+       pthread_mutex_unlock(&_timeout_mutex);
+
+       return ret;
+}
+
+static void _unregister_for_timeout(struct thread_status *thread)
+{
+       pthread_mutex_lock(&_timeout_mutex);
+       if (!dm_list_empty(&thread->timeout_list)) {
+               dm_list_del(&thread->timeout_list);
+               dm_list_init(&thread->timeout_list);
+       }
+       pthread_mutex_unlock(&_timeout_mutex);
+}
+
+static void _no_intr_log(int level, const char *file, int line,
+                       const char *f, ...)
+{
+       va_list ap;
+
+       if (errno == EINTR)
+               return;
+       if (level > _LOG_WARN)
+               return;
+
+       va_start(ap, f);
+
+       if (level < _LOG_WARN)
+               vfprintf(stderr, f, ap);
+       else
+               vprintf(f, ap);
+
+       va_end(ap);
+
+       if (level < _LOG_WARN)
+               fprintf(stderr, "\n");
+       else
+               fprintf(stdout, "\n");
+}
+
+static sigset_t _unblock_sigalrm(void)
+{
+       sigset_t set, old;
+
+       sigemptyset(&set);
+       sigaddset(&set, SIGALRM);
+       pthread_sigmask(SIG_UNBLOCK, &set, &old);
+       return old;
+}
+
+#define DM_WAIT_RETRY 0
+#define DM_WAIT_INTR 1
+#define DM_WAIT_FATAL 2
+
+/* Wait on a device until an event occurs. */
+static int _event_wait(struct thread_status *thread, struct dm_task **task)
+{
+       sigset_t set;
+       int ret = DM_WAIT_RETRY;
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       *task = 0;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
+               return DM_WAIT_RETRY;
+
+       thread->current_task = dmt;
+
+       if (!dm_task_set_uuid(dmt, thread->device.uuid) ||
+           !dm_task_set_event_nr(dmt, thread->event_nr))
+               goto out;
+
+       /*
+        * This is so that you can break out of waiting on an event,
+        * either for a timeout event, or to cancel the thread.
+        */
+       set = _unblock_sigalrm();
+       dm_log_init(_no_intr_log);
+       errno = 0;
+       if (dm_task_run(dmt)) {
+               thread->current_events |= DM_EVENT_DEVICE_ERROR;
+               ret = DM_WAIT_INTR;
+
+               if ((ret = dm_task_get_info(dmt, &info)))
+                       thread->event_nr = info.event_nr;
+       } else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
+               thread->current_events |= DM_EVENT_TIMEOUT;
+               ret = DM_WAIT_INTR;
+       } else if (thread->status == DM_THREAD_SHUTDOWN && errno == EINTR) {
+               ret = DM_WAIT_FATAL;
+       } else {
+               syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
+                      errno, strerror(errno));
+               if (errno == ENXIO) {
+                       syslog(LOG_ERR, "%s disappeared, detaching",
+                              thread->device.name);
+                       ret = DM_WAIT_FATAL;
+               }
+       }
+
+       pthread_sigmask(SIG_SETMASK, &set, NULL);
+       dm_log_init(NULL);
+
+      out:
+       if (ret == DM_WAIT_FATAL || ret == DM_WAIT_RETRY) {
+               dm_task_destroy(dmt);
+               thread->current_task = NULL;
+       } else
+               *task = dmt;
+
+       return ret;
+}
+
+/* Register a device with the DSO. */
+static int _do_register_device(struct thread_status *thread)
+{
+       return thread->dso_data->register_device(thread->device.name,
+                                                thread->device.uuid,
+                                                thread->device.major,
+                                                thread->device.minor,
+                                                &(thread->dso_private));
+}
+
+/* Unregister a device with the DSO. */
+static int _do_unregister_device(struct thread_status *thread)
+{
+       return thread->dso_data->unregister_device(thread->device.name,
+                                                  thread->device.uuid,
+                                                  thread->device.major,
+                                                  thread->device.minor,
+                                                  &(thread->dso_private));
+}
+
+/* Process an event in the DSO. */
+static void _do_process_event(struct thread_status *thread, struct dm_task *task)
+{
+       thread->dso_data->process_event(task, thread->current_events, &(thread->dso_private));
+}
+
+/* Thread cleanup handler to unregister device. */
+static void _monitor_unregister(void *arg)
+{
+       struct thread_status *thread = arg, *thread_iter;
+
+       if (!_do_unregister_device(thread))
+               syslog(LOG_ERR, "%s: %s unregister failed\n", __func__,
+                      thread->device.name);
+       if (thread->current_task)
+               dm_task_destroy(thread->current_task);
+       thread->current_task = NULL;
+
+       _lock_mutex();
+       if (thread->events & DM_EVENT_TIMEOUT) {
+               /* _unregister_for_timeout locks another mutex, we
+                  don't want to deadlock so we release our mutex for
+                  a bit */
+               _unlock_mutex();
+               _unregister_for_timeout(thread);
+               _lock_mutex();
+       }
+       /* we may have been relinked to unused registry since we were
+          called, so check that */
+       dm_list_iterate_items(thread_iter, &_thread_registry_unused)
+               if (thread_iter == thread) {
+                       thread->status = DM_THREAD_DONE;
+                       _unlock_mutex();
+                       return;
+               }
+       thread->status = DM_THREAD_DONE;
+       UNLINK_THREAD(thread);
+       LINK(thread, &_thread_registry_unused);
+       _unlock_mutex();
+}
+
+static struct dm_task *_get_device_status(struct thread_status *ts)
+{
+       struct dm_task *dmt = dm_task_create(DM_DEVICE_STATUS);
+
+       if (!dmt)
+               return NULL;
+
+       dm_task_set_uuid(dmt, ts->device.uuid);
+
+       if (!dm_task_run(dmt)) {
+               dm_task_destroy(dmt);
+               return NULL;
+       }
+
+       return dmt;
+}
+
+/* Device monitoring thread. */
+static void *_monitor_thread(void *arg)
+{
+       struct thread_status *thread = arg;
+       int wait_error = 0;
+       struct dm_task *task;
+
+       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+       pthread_cleanup_push(_monitor_unregister, thread);
+
+       /* Wait for do_process_request() to finish its task. */
+       _lock_mutex();
+       thread->status = DM_THREAD_RUNNING;
+       _unlock_mutex();
+
+       /* Loop forever awaiting/analyzing device events. */
+       while (1) {
+               thread->current_events = 0;
+
+               wait_error = _event_wait(thread, &task);
+               if (wait_error == DM_WAIT_RETRY)
+                       continue;
+
+               if (wait_error == DM_WAIT_FATAL)
+                       break;
+
+               /* Timeout occurred, task is not filled properly.
+                * We get device status here for processing it in DSO.
+                */
+               if (wait_error == DM_WAIT_INTR &&
+                   thread->current_events & DM_EVENT_TIMEOUT) {
+                       dm_task_destroy(task);
+                       task = _get_device_status(thread);
+                       /* FIXME: syslog fail here ? */
+                       if (!(thread->current_task = task))
+                               continue;
+               }
+
+               /*
+                * We know that wait succeeded and stored a
+                * pointer to dm_task with device status into task.
+                */
+
+               /*
+                * Check against filter.
+                *
+                * If there's current events delivered from _event_wait() AND
+                * the device got registered for those events AND
+                * those events haven't been processed yet, call
+                * the DSO's process_event() handler.
+                */
+               _lock_mutex();
+               if (thread->status == DM_THREAD_SHUTDOWN) {
+                       _unlock_mutex();
+                       break;
+               }
+               _unlock_mutex();
+
+               if (thread->events & thread->current_events) {
+                       _lock_mutex();
+                       thread->processing = 1;
+                       _unlock_mutex();
+
+                       _do_process_event(thread, task);
+                       dm_task_destroy(task);
+                       thread->current_task = NULL;
+
+                       _lock_mutex();
+                       thread->processing = 0;
+                       _unlock_mutex();
+               } else {
+                       dm_task_destroy(task);
+                       thread->current_task = NULL;
+               }
+       }
+
+       pthread_cleanup_pop(1);
+
+       return NULL;
+}
+
+/* Create a device monitoring thread. */
+static int _create_thread(struct thread_status *thread)
+{
+       return _pthread_create_smallstack(&thread->thread, _monitor_thread, thread);
+}
+
+static int _terminate_thread(struct thread_status *thread)
+{
+       return pthread_kill(thread->thread, SIGALRM);
+}
+
+/* DSO reference counting. Call with _global_mutex locked! */
+static void _lib_get(struct dso_data *data)
+{
+       data->ref_count++;
+}
+
+static void _lib_put(struct dso_data *data)
+{
+       if (!--data->ref_count) {
+               dlclose(data->dso_handle);
+               UNLINK_DSO(data);
+               _free_dso_data(data);
+       }
+}
+
+/* Find DSO data. */
+static struct dso_data *_lookup_dso(struct message_data *data)
+{
+       struct dso_data *dso_data, *ret = NULL;
+
+       dm_list_iterate_items(dso_data, &_dso_registry)
+           if (!strcmp(data->dso_name, dso_data->dso_name)) {
+               _lib_get(dso_data);
+               ret = dso_data;
+               break;
+       }
+
+       return ret;
+}
+
+/* Lookup DSO symbols we need. */
+static int _lookup_symbol(void *dl, void **symbol, const char *name)
+{
+       if ((*symbol = dlsym(dl, name)))
+               return 1;
+
+       return 0;
+}
+
+static int lookup_symbols(void *dl, struct dso_data *data)
+{
+       return _lookup_symbol(dl, (void *) &data->process_event,
+                            "process_event") &&
+           _lookup_symbol(dl, (void *) &data->register_device,
+                         "register_device") &&
+           _lookup_symbol(dl, (void *) &data->unregister_device,
+                         "unregister_device");
+}
+
+/* Load an application specific DSO. */
+static struct dso_data *_load_dso(struct message_data *data)
+{
+       void *dl;
+       struct dso_data *ret = NULL;
+
+       if (!(dl = dlopen(data->dso_name, RTLD_NOW))) {
+               const char *dlerr = dlerror();
+               syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name,
+                      dlerr);
+               data->msg->size =
+                   dm_asprintf(&(data->msg->data), "%s %s dlopen failed: %s",
+                               data->id, data->dso_name, dlerr);
+               return NULL;
+       }
+
+       if (!(ret = _alloc_dso_data(data))) {
+               dlclose(dl);
+               return NULL;
+       }
+
+       if (!(lookup_symbols(dl, ret))) {
+               _free_dso_data(ret);
+               dlclose(dl);
+               return NULL;
+       }
+
+       /*
+        * Keep handle to close the library once
+        * we've got no references to it any more.
+        */
+       ret->dso_handle = dl;
+       _lib_get(ret);
+
+       _lock_mutex();
+       LINK_DSO(ret);
+       _unlock_mutex();
+
+       return ret;
+}
+
+/* Return success on daemon active check. */
+static int _active(struct message_data *message_data)
+{
+       return 0;
+}
+
+/*
+ * Register for an event.
+ *
+ * Only one caller at a time here, because we use
+ * a FIFO and lock it against multiple accesses.
+ */
+static int _register_for_event(struct message_data *message_data)
+{
+       int ret = 0;
+       struct thread_status *thread, *thread_new = NULL;
+       struct dso_data *dso_data;
+
+       if (!(dso_data = _lookup_dso(message_data)) &&
+           !(dso_data = _load_dso(message_data))) {
+               stack;
+#ifdef ELIBACC
+               ret = -ELIBACC;
+#else
+               ret = -ENODEV;
+#endif
+               goto out;
+       }
+
+       /* Preallocate thread status struct to avoid deadlock. */
+       if (!(thread_new = _alloc_thread_status(message_data, dso_data))) {
+               stack;
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (!_fill_device_data(thread_new)) {
+               stack;
+               ret = -ENODEV;
+               goto out;
+       }
+
+       _lock_mutex();
+
+       /* If creation of timeout thread fails (as it may), we fail
+          here completely. The client is responsible for either
+          retrying later or trying to register without timeout
+          events. However, if timeout thread cannot be started, it
+          usually means we are so starved on resources that we are
+          almost as good as dead already... */
+       if (thread_new->events & DM_EVENT_TIMEOUT) {
+               ret = -_register_for_timeout(thread_new);
+               if (ret) {
+                   _unlock_mutex();
+                   goto out;
+               }
+       }
+
+       if (!(thread = _lookup_thread_status(message_data))) {
+               _unlock_mutex();
+
+               if (!(ret = _do_register_device(thread_new)))
+                       goto out;
+
+               thread = thread_new;
+               thread_new = NULL;
+
+               /* Try to create the monitoring thread for this device. */
+               _lock_mutex();
+               if ((ret = -_create_thread(thread))) {
+                       _unlock_mutex();
+                       _do_unregister_device(thread);
+                       _free_thread_status(thread);
+                       goto out;
+               } else
+                       LINK_THREAD(thread);
+       }
+
+       /* Or event # into events bitfield. */
+       thread->events |= message_data->events.field;
+
+       _unlock_mutex();
+
+      out:
+       /*
+        * Deallocate thread status after releasing
+        * the lock in case we haven't used it.
+        */
+       if (thread_new)
+               _free_thread_status(thread_new);
+
+       return ret;
+}
+
+/*
+ * Unregister for an event.
+ *
+ * Only one caller at a time here as with register_for_event().
+ */
+static int _unregister_for_event(struct message_data *message_data)
+{
+       int ret = 0;
+       struct thread_status *thread;
+
+       /*
+        * Clear event in bitfield and deactivate
+        * monitoring thread in case bitfield is 0.
+        */
+       _lock_mutex();
+
+       if (!(thread = _lookup_thread_status(message_data))) {
+               _unlock_mutex();
+               ret = -ENODEV;
+               goto out;
+       }
+
+       if (thread->status == DM_THREAD_DONE) {
+               /* the thread has terminated while we were not
+                  watching */
+               _unlock_mutex();
+               return 0;
+       }
+
+       thread->events &= ~message_data->events.field;
+
+       if (!(thread->events & DM_EVENT_TIMEOUT))
+               _unregister_for_timeout(thread);
+       /*
+        * In case there's no events to monitor on this device ->
+        * unlink and terminate its monitoring thread.
+        */
+       if (!thread->events) {
+               UNLINK_THREAD(thread);
+               LINK(thread, &_thread_registry_unused);
+       }
+       _unlock_mutex();
+
+      out:
+       return ret;
+}
+
+/*
+ * Get registered device.
+ *
+ * Only one caller at a time here as with register_for_event().
+ */
+static int _registered_device(struct message_data *message_data,
+                            struct thread_status *thread)
+{
+       struct dm_event_daemon_message *msg = message_data->msg;
+
+       const char *fmt = "%s %s %s %u";
+       const char *id = message_data->id;
+       const char *dso = thread->dso_data->dso_name;
+       const char *dev = thread->device.uuid;
+       unsigned events = ((thread->status == DM_THREAD_RUNNING)
+                          && (thread->events)) ? thread->events : thread->
+           events | DM_EVENT_REGISTRATION_PENDING;
+
+       dm_free(msg->data);
+
+       msg->size = dm_asprintf(&(msg->data), fmt, id, dso, dev, events);
+
+       _unlock_mutex();
+
+       return 0;
+}
+
+static int _want_registered_device(char *dso_name, char *device_uuid,
+                                 struct thread_status *thread)
+{
+       /* If DSO names and device paths are equal. */
+       if (dso_name && device_uuid)
+               return !strcmp(dso_name, thread->dso_data->dso_name) &&
+                   !strcmp(device_uuid, thread->device.uuid) &&
+                       (thread->status == DM_THREAD_RUNNING ||
+                        (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
+       /* If DSO names are equal. */
+       if (dso_name)
+               return !strcmp(dso_name, thread->dso_data->dso_name) &&
+                       (thread->status == DM_THREAD_RUNNING ||
+                        (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
+       /* If device paths are equal. */
+       if (device_uuid)
+               return !strcmp(device_uuid, thread->device.uuid) &&
+                       (thread->status == DM_THREAD_RUNNING ||
+                        (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
+       return 1;
+}
+
+static int _get_registered_dev(struct message_data *message_data, int next)
+{
+       struct thread_status *thread, *hit = NULL;
+
+       _lock_mutex();
+
+       /* Iterate list of threads checking if we want a particular one. */
+       dm_list_iterate_items(thread, &_thread_registry)
+               if (_want_registered_device(message_data->dso_name,
+                                           message_data->device_uuid,
+                                           thread)) {
+                       hit = thread;
+                       break;
+               }
+
+       /*
+        * If we got a registered device and want the next one ->
+        * fetch next conforming element off the list.
+        */
+       if (hit && !next) {
+               _unlock_mutex();
+               return _registered_device(message_data, hit);
+       }
+
+       if (!hit)
+               goto out;
+
+       thread = hit;
+
+       while (1) {
+               if (dm_list_end(&_thread_registry, &thread->list))
+                       goto out;
+
+               thread = dm_list_item(thread->list.n, struct thread_status);
+               if (_want_registered_device(message_data->dso_name, NULL, thread)) {
+                       hit = thread;
+                       break;
+               }
+       }
+
+       _unlock_mutex();
+       return _registered_device(message_data, hit);
+
+      out:
+       _unlock_mutex();
+       
+       return -ENOENT;
+}
+
+static int _get_registered_device(struct message_data *message_data)
+{
+       return _get_registered_dev(message_data, 0);
+}
+
+static int _get_next_registered_device(struct message_data *message_data)
+{
+       return _get_registered_dev(message_data, 1);
+}
+
+static int _set_timeout(struct message_data *message_data)
+{
+       struct thread_status *thread;
+
+       _lock_mutex();
+       if ((thread = _lookup_thread_status(message_data)))
+               thread->timeout = message_data->timeout.secs;
+       _unlock_mutex();
+
+       return thread ? 0 : -ENODEV;
+}
+
+static int _get_timeout(struct message_data *message_data)
+{
+       struct thread_status *thread;
+       struct dm_event_daemon_message *msg = message_data->msg;
+
+       dm_free(msg->data);
+
+       _lock_mutex();
+       if ((thread = _lookup_thread_status(message_data))) {
+               msg->size =
+                   dm_asprintf(&(msg->data), "%s %" PRIu32, message_data->id,
+                               thread->timeout);
+       } else {
+               msg->data = NULL;
+               msg->size = 0;
+       }
+       _unlock_mutex();
+
+       return thread ? 0 : -ENODEV;
+}
+
+/* Initialize a fifos structure with path names. */
+static void _init_fifos(struct dm_event_fifos *fifos)
+{
+       memset(fifos, 0, sizeof(*fifos));
+
+       fifos->client_path = DM_EVENT_FIFO_CLIENT;
+       fifos->server_path = DM_EVENT_FIFO_SERVER;
+}
+
+/* Open fifos used for client communication. */
+static int _open_fifos(struct dm_event_fifos *fifos)
+{
+       int orig_errno;
+
+       /* Create client fifo. */
+       (void) dm_prepare_selinux_context(fifos->client_path, S_IFIFO);
+       if ((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) {
+               syslog(LOG_ERR, "%s: Failed to create client fifo.\n", __func__);
+               orig_errno = errno;
+               (void) dm_prepare_selinux_context(NULL, 0);
+               stack;
+               return -orig_errno;
+       }
+
+       /* Create server fifo. */
+       (void) dm_prepare_selinux_context(fifos->server_path, S_IFIFO);
+       if ((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST) {
+               syslog(LOG_ERR, "%s: Failed to create server fifo.\n", __func__);
+               orig_errno = errno;
+               (void) dm_prepare_selinux_context(NULL, 0);
+               stack;
+               return -orig_errno;
+       }
+
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       struct stat st;
+
+       /* Warn about wrong permissions if applicable */
+       if ((!stat(fifos->client_path, &st)) && (st.st_mode & 0777) != 0600)
+               syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+                      fifos->client_path);
+
+       if ((!stat(fifos->server_path, &st)) && (st.st_mode & 0777) != 0600)
+               syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+                      fifos->server_path);
+
+       /* If they were already there, make sure permissions are ok. */
+       if (chmod(fifos->client_path, 0600)) {
+               syslog(LOG_ERR, "Unable to set correct file permissions on %s",
+                      fifos->client_path);
+               return -errno;
+       }
+
+       if (chmod(fifos->server_path, 0600)) {
+               syslog(LOG_ERR, "Unable to set correct file permissions on %s",
+                      fifos->server_path);
+               return -errno;
+       }
+
+       /* Need to open read+write or we will block or fail */
+       if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
+               stack;
+               return -errno;
+       }
+
+       /* Need to open read+write for select() to work. */
+       if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) {
+               stack;
+               close(fifos->server);
+               return -errno;
+       }
+
+       return 0;
+}
+
+/*
+ * Read message from client making sure that data is available
+ * and a complete message is read.  Must not block indefinitely.
+ */
+static int _client_read(struct dm_event_fifos *fifos,
+                      struct dm_event_daemon_message *msg)
+{
+       struct timeval t;
+       unsigned bytes = 0;
+       int ret = 0;
+       fd_set fds;
+       size_t size = 2 * sizeof(uint32_t);     /* status + size */
+       uint32_t *header = alloca(size);
+       char *buf = (char *)header;
+
+       msg->data = NULL;
+
+       errno = 0;
+       while (bytes < size && errno != EOF) {
+               /* Watch client read FIFO for input. */
+               FD_ZERO(&fds);
+               FD_SET(fifos->client, &fds);
+               t.tv_sec = 1;
+               t.tv_usec = 0;
+               ret = select(fifos->client + 1, &fds, NULL, NULL, &t);
+
+               if (!ret && !bytes)     /* nothing to read */
+                       return 0;
+
+               if (!ret)       /* trying to finish read */
+                       continue;
+
+               if (ret < 0)    /* error */
+                       return 0;
+
+               ret = read(fifos->client, buf + bytes, size - bytes);
+               bytes += ret > 0 ? ret : 0;
+               if (header && (bytes == 2 * sizeof(uint32_t))) {
+                       msg->cmd = ntohl(header[0]);
+                       msg->size = ntohl(header[1]);
+                       buf = msg->data = dm_malloc(msg->size);
+                       size = msg->size;
+                       bytes = 0;
+                       header = 0;
+               }
+       }
+
+       if (bytes != size) {
+               dm_free(msg->data);
+               msg->data = NULL;
+               msg->size = 0;
+       }
+
+       return bytes == size;
+}
+
+/*
+ * Write a message to the client making sure that it is ready to write.
+ */
+static int _client_write(struct dm_event_fifos *fifos,
+                       struct dm_event_daemon_message *msg)
+{
+       unsigned bytes = 0;
+       int ret = 0;
+       fd_set fds;
+
+       size_t size = 2 * sizeof(uint32_t) + msg->size;
+       uint32_t *header = alloca(size);
+       char *buf = (char *)header;
+
+       header[0] = htonl(msg->cmd);
+       header[1] = htonl(msg->size);
+       if (msg->data)
+               memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
+
+       errno = 0;
+       while (bytes < size && errno != EIO) {
+               do {
+                       /* Watch client write FIFO to be ready for output. */
+                       FD_ZERO(&fds);
+                       FD_SET(fifos->server, &fds);
+               } while (select(fifos->server + 1, NULL, &fds, NULL, NULL) !=
+                        1);
+
+               ret = write(fifos->server, buf + bytes, size - bytes);
+               bytes += ret > 0 ? ret : 0;
+       }
+
+       return bytes == size;
+}
+
+/*
+ * Handle a client request.
+ *
+ * We put the request handling functions into
+ * a list because of the growing number.
+ */
+static int _handle_request(struct dm_event_daemon_message *msg,
+                         struct message_data *message_data)
+{
+       static struct {
+               unsigned int cmd;
+               int (*f)(struct message_data *);
+       } requests[] = {
+               { DM_EVENT_CMD_REGISTER_FOR_EVENT, _register_for_event},
+               { DM_EVENT_CMD_UNREGISTER_FOR_EVENT, _unregister_for_event},
+               { DM_EVENT_CMD_GET_REGISTERED_DEVICE, _get_registered_device},
+               { DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
+                       _get_next_registered_device},
+               { DM_EVENT_CMD_SET_TIMEOUT, _set_timeout},
+               { DM_EVENT_CMD_GET_TIMEOUT, _get_timeout},
+               { DM_EVENT_CMD_ACTIVE, _active},
+               { DM_EVENT_CMD_GET_STATUS, _get_status},
+       }, *req;
+
+       for (req = requests; req < requests + sizeof(requests); req++)
+               if (req->cmd == msg->cmd)
+                       return req->f(message_data);
+
+       return -EINVAL;
+}
+
+/* Process a request passed from the communication thread. */
+static int _do_process_request(struct dm_event_daemon_message *msg)
+{
+       int ret;
+       char *answer;
+       static struct message_data message_data;
+
+       /* Parse the message. */
+       memset(&message_data, 0, sizeof(message_data));
+       message_data.msg = msg;
+       if (msg->cmd == DM_EVENT_CMD_HELLO || msg->cmd == DM_EVENT_CMD_DIE)  {
+               ret = 0;
+               answer = msg->data;
+               if (answer) {
+                       msg->size = dm_asprintf(&(msg->data), "%s %s", answer,
+                                               msg->cmd == DM_EVENT_CMD_DIE ? "DYING" : "HELLO");
+                       dm_free(answer);
+               } else {
+                       msg->size = 0;
+                       msg->data = NULL;
+               }
+       } else if (msg->cmd != DM_EVENT_CMD_ACTIVE && !_parse_message(&message_data)) {
+               stack;
+               ret = -EINVAL;
+       } else
+               ret = _handle_request(msg, &message_data);
+
+       msg->cmd = ret;
+       if (!msg->data)
+               msg->size = dm_asprintf(&(msg->data), "%s %s", message_data.id, strerror(-ret));
+
+       _free_message(&message_data);
+
+       return ret;
+}
+
+/* Only one caller at a time. */
+static void _process_request(struct dm_event_fifos *fifos)
+{
+       int die = 0;
+       struct dm_event_daemon_message msg;
+
+       memset(&msg, 0, sizeof(msg));
+
+       /*
+        * Read the request from the client (client_read, client_write
+        * give true on success and false on failure).
+        */
+       if (!_client_read(fifos, &msg))
+               return;
+
+       if (msg.cmd == DM_EVENT_CMD_DIE)
+               die = 1;
+
+       /* _do_process_request fills in msg (if memory allows for
+          data, otherwise just cmd and size = 0) */
+       _do_process_request(&msg);
+
+       if (!_client_write(fifos, &msg))
+               stack;
+
+       if (die) raise(9);
+
+       dm_free(msg.data);
+}
+
+static void _process_initial_registrations(void)
+{
+       int i = 0;
+       char *reg;
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+       while ((reg = _initial_registrations[i])) {
+               msg.cmd = DM_EVENT_CMD_REGISTER_FOR_EVENT;
+               msg.size = strlen(reg);
+               msg.data = reg;
+               _do_process_request(&msg);
+               ++ i;
+       }
+}
+
+static void _cleanup_unused_threads(void)
+{
+       int ret;
+       struct dm_list *l;
+       struct thread_status *thread;
+
+       _lock_mutex();
+       while ((l = dm_list_first(&_thread_registry_unused))) {
+               thread = dm_list_item(l, struct thread_status);
+               if (thread->processing)
+                       break;  /* cleanup on the next round */
+
+               if (thread->status == DM_THREAD_RUNNING) {
+                       thread->status = DM_THREAD_SHUTDOWN;
+                       break;
+               }
+
+               if (thread->status == DM_THREAD_SHUTDOWN) {
+                       if (!thread->events) {
+                               /* turn codes negative -- should we be returning this? */
+                               ret = _terminate_thread(thread);
+
+                               if (ret == ESRCH) {
+                                       thread->status = DM_THREAD_DONE;
+                               } else if (ret) {
+                                       syslog(LOG_ERR,
+                                              "Unable to terminate thread: %s\n",
+                                              strerror(-ret));
+                                       stack;
+                               }
+                               break;
+                       }
+
+                       dm_list_del(l);
+                       syslog(LOG_ERR,
+                              "thread can't be on unused list unless !thread->events");
+                       thread->status = DM_THREAD_RUNNING;
+                       LINK_THREAD(thread);
+
+                       continue;
+               }
+
+               if (thread->status == DM_THREAD_DONE) {
+                       dm_list_del(l);
+                       pthread_join(thread->thread, NULL);
+                       _free_thread_status(thread);
+               }
+       }
+
+       _unlock_mutex();
+}
+
+static void _sig_alarm(int signum __attribute__((unused)))
+{
+       pthread_testcancel();
+}
+
+/* Init thread signal handling. */
+static void _init_thread_signals(void)
+{
+       sigset_t my_sigset;
+       struct sigaction act;
+
+       memset(&act, 0, sizeof(act));
+       act.sa_handler = _sig_alarm;
+       sigaction(SIGALRM, &act, NULL);
+       sigfillset(&my_sigset);
+
+       /* These are used for exiting */
+       sigdelset(&my_sigset, SIGTERM);
+       sigdelset(&my_sigset, SIGINT);
+       sigdelset(&my_sigset, SIGHUP);
+       sigdelset(&my_sigset, SIGQUIT);
+
+       pthread_sigmask(SIG_BLOCK, &my_sigset, NULL);
+}
+
+/*
+ * exit_handler
+ * @sig
+ *
+ * Set the global variable which the process should
+ * be watching to determine when to exit.
+ */
+static void _exit_handler(int sig __attribute__((unused)))
+{
+       /*
+        * We exit when '_exit_now' is set.
+        * That is, when a signal has been received.
+        *
+        * We can not simply set '_exit_now' unless all
+        * threads are done processing.
+        */
+       if (!_thread_registries_empty) {
+               syslog(LOG_ERR, "There are still devices being monitored.");
+               syslog(LOG_ERR, "Refusing to exit.");
+       } else
+               _exit_now = 1;
+
+}
+
+#ifdef linux
+/*
+ * Protection against OOM killer if kernel supports it
+ */
+static int _set_oom_adj(int val)
+{
+       FILE *fp;
+
+       struct stat st;
+
+       if (stat(OOM_ADJ_FILE, &st) == -1) {
+               if (errno == ENOENT)
+                       DEBUGLOG(OOM_ADJ_FILE " not found");
+               else
+                       perror(OOM_ADJ_FILE ": stat failed");
+               return 1;
+       }
+
+       if (!(fp = fopen(OOM_ADJ_FILE, "w"))) {
+               perror(OOM_ADJ_FILE ": fopen failed");
+               return 0;
+       }
+
+       fprintf(fp, "%i", val);
+       if (dm_fclose(fp))
+               perror(OOM_ADJ_FILE ": fclose failed");
+
+       return 1;
+}
+#endif
+
+static void remove_lockfile(void)
+{
+       if (unlink(DMEVENTD_PIDFILE))
+               perror(DMEVENTD_PIDFILE ": unlink failed");
+}
+
+static void _daemonize(void)
+{
+       int child_status;
+       int fd;
+       pid_t pid;
+       struct rlimit rlim;
+       struct timeval tval;
+       sigset_t my_sigset;
+
+       sigemptyset(&my_sigset);
+       if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) {
+               fprintf(stderr, "Unable to restore signals.\n");
+               exit(EXIT_FAILURE);
+       }
+       signal(SIGTERM, &_exit_handler);
+
+       switch (pid = fork()) {
+       case -1:
+               perror("fork failed:");
+               exit(EXIT_FAILURE);
+
+       case 0:         /* Child */
+               break;
+
+       default:
+               /* Wait for response from child */
+               while (!waitpid(pid, &child_status, WNOHANG) && !_exit_now) {
+                       tval.tv_sec = 0;
+                       tval.tv_usec = 250000;  /* .25 sec */
+                       select(0, NULL, NULL, NULL, &tval);
+               }
+
+               if (_exit_now)  /* Child has signaled it is ok - we can exit now */
+                       exit(EXIT_SUCCESS);
+
+               /* Problem with child.  Determine what it is by exit code */
+               switch (WEXITSTATUS(child_status)) {
+               case EXIT_DESC_CLOSE_FAILURE:
+               case EXIT_DESC_OPEN_FAILURE:
+               case EXIT_FIFO_FAILURE:
+               case EXIT_CHDIR_FAILURE:
+               default:
+                       fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status));
+                       break;
+               }
+
+               exit(WEXITSTATUS(child_status));
+       }
+
+       if (chdir("/"))
+               exit(EXIT_CHDIR_FAILURE);
+
+       if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+               fd = 256;       /* just have to guess */
+       else
+               fd = rlim.rlim_cur;
+
+       for (--fd; fd >= 0; fd--)
+               close(fd);
+
+       if ((open("/dev/null", O_RDONLY) < 0) ||
+           (open("/dev/null", O_WRONLY) < 0) ||
+           (open("/dev/null", O_WRONLY) < 0))
+               exit(EXIT_DESC_OPEN_FAILURE);
+
+       setsid();
+}
+
+static void restart(void)
+{
+       struct dm_event_fifos fifos;
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+       int i, count = 0;
+       char *message;
+       int length;
+
+       /* Get the list of registrations from the running daemon. */
+
+       if (!init_fifos(&fifos)) {
+               fprintf(stderr, "Could not initiate communication with existing dmeventd.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)) {
+               fprintf(stderr, "Could not communicate with existing dmeventd.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_STATUS, "-", "-", 0, 0)) {
+               exit(EXIT_FAILURE);
+       }
+
+       message = msg.data;
+       message = strchr(message, ' ');
+       ++ message;
+       length = strlen(msg.data);
+       for (i = 0; i < length; ++i) {
+               if (msg.data[i] == ';') {
+                       msg.data[i] = 0;
+                       ++count;
+               }
+       }
+
+       _initial_registrations = dm_malloc(sizeof(char*) * (count + 1));
+       for (i = 0; i < count; ++i) {
+               _initial_registrations[i] = dm_strdup(message);
+               message += strlen(message) + 1;
+       }
+       _initial_registrations[count] = 0;
+
+       if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) {
+               fprintf(stderr, "Old dmeventd refused to die.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       fini_fifos(&fifos);
+}
+
+static void usage(char *prog, FILE *file)
+{
+       fprintf(file, "Usage:\n"
+               "%s [-V] [-h] [-d] [-d] [-d] [-f]\n\n"
+               "   -V       Show version of dmeventd\n"
+               "   -h       Show this help information\n"
+               "   -d       Log debug messages to syslog (-d, -dd, -ddd)\n"
+               "   -f       Don't fork, run in the foreground\n\n", prog);
+}
+
+int main(int argc, char *argv[])
+{
+       signed char opt;
+       struct dm_event_fifos fifos;
+       //struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON};
+
+       opterr = 0;
+       optind = 0;
+
+       while ((opt = getopt(argc, argv, "?fhVdR")) != EOF) {
+               switch (opt) {
+               case 'h':
+                       usage(argv[0], stdout);
+                       exit(0);
+               case '?':
+                       usage(argv[0], stderr);
+                       exit(0);
+               case 'R':
+                       _restart++;
+                       break;
+               case 'f':
+                       _foreground++;
+                       break;
+               case 'd':
+                       dmeventd_debug++;
+                       break;
+               case 'V':
+                       printf("dmeventd version: %s\n", DM_LIB_VERSION);
+                       exit(1);
+                       break;
+               }
+       }
+
+       /*
+        * Switch to C locale to avoid reading large locale-archive file
+        * used by some glibc (on some distributions it takes over 100MB).
+        * Daemon currently needs to use mlockall().
+        */
+       if (setenv("LANG", "C", 1))
+               perror("Cannot set LANG to C");
+
+       if (_restart)
+               restart();
+
+       if (!_foreground)
+               _daemonize();
+
+       openlog("dmeventd", LOG_PID, LOG_DAEMON);
+
+       (void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
+       if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
+               exit(EXIT_FAILURE);
+
+       atexit(remove_lockfile);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       /* Set the rest of the signals to cause '_exit_now' to be set */
+       signal(SIGINT, &_exit_handler);
+       signal(SIGHUP, &_exit_handler);
+       signal(SIGQUIT, &_exit_handler);
+
+#ifdef linux
+       if (!_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN))
+               syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer");
+#endif
+
+       _init_thread_signals();
+
+       //multilog_clear_logging();
+       //multilog_add_type(std_syslog, &logdata);
+       //multilog_init_verbose(std_syslog, _LOG_DEBUG);
+       //multilog_async(1);
+
+       _init_fifos(&fifos);
+
+       pthread_mutex_init(&_global_mutex, NULL);
+
+       if (_open_fifos(&fifos))
+               exit(EXIT_FIFO_FAILURE);
+
+       /* Signal parent, letting them know we are ready to go. */
+       if (!_foreground)
+               kill(getppid(), SIGTERM);
+       syslog(LOG_NOTICE, "dmeventd ready for processing.");
+
+       if (_initial_registrations)
+               _process_initial_registrations();
+
+       while (!_exit_now) {
+               _process_request(&fifos);
+               _cleanup_unused_threads();
+               if (!dm_list_empty(&_thread_registry)
+                   || !dm_list_empty(&_thread_registry_unused))
+                       _thread_registries_empty = 0;
+               else
+                       _thread_registries_empty = 1;
+       }
+
+       _exit_dm_lib();
+
+       pthread_mutex_destroy(&_global_mutex);
+
+       syslog(LOG_NOTICE, "dmeventd shutting down.");
+       closelog();
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h
new file mode 100644 (file)
index 0000000..254758e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef __DMEVENTD_DOT_H__
+#define __DMEVENTD_DOT_H__
+
+/* FIXME This stuff must be configurable. */
+
+#define        DM_EVENT_DAEMON         "/sbin/dmeventd"
+#define DM_EVENT_LOCKFILE      "/var/lock/dmeventd"
+#define        DM_EVENT_FIFO_CLIENT    "/var/run/dmeventd-client"
+#define        DM_EVENT_FIFO_SERVER    "/var/run/dmeventd-server"
+#define DM_EVENT_PIDFILE       "/var/run/dmeventd.pid"
+
+#define DM_EVENT_DEFAULT_TIMEOUT 10
+
+/* Commands for the daemon passed in the message below. */
+enum dm_event_command {
+       DM_EVENT_CMD_ACTIVE = 1,
+       DM_EVENT_CMD_REGISTER_FOR_EVENT,
+       DM_EVENT_CMD_UNREGISTER_FOR_EVENT,
+       DM_EVENT_CMD_GET_REGISTERED_DEVICE,
+       DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
+       DM_EVENT_CMD_SET_TIMEOUT,
+       DM_EVENT_CMD_GET_TIMEOUT,
+       DM_EVENT_CMD_HELLO,
+       DM_EVENT_CMD_DIE,
+       DM_EVENT_CMD_GET_STATUS,
+};
+
+/* Message passed between client and daemon. */
+struct dm_event_daemon_message {
+       uint32_t cmd;
+       uint32_t size;
+       char *data;
+};
+
+/* FIXME Is this meant to be exported?  I can't see where the
+   interface uses it. */
+/* Fifos for client/daemon communication. */
+struct dm_event_fifos {
+       int client;
+       int server;
+       const char *client_path;
+       const char *server_path;
+};
+
+/*      EXIT_SUCCESS             0 -- stdlib.h */
+/*      EXIT_FAILURE             1 -- stdlib.h */
+/*      EXIT_LOCKFILE_INUSE      2 -- obsoleted */
+#define EXIT_DESC_CLOSE_FAILURE  3
+#define EXIT_DESC_OPEN_FAILURE   4
+/*      EXIT_OPEN_PID_FAILURE    5 -- obsoleted */
+#define EXIT_FIFO_FAILURE        6
+#define EXIT_CHDIR_FAILURE       7
+
+/* Implemented in libdevmapper-event.c, but not part of public API. */
+int daemon_talk(struct dm_event_fifos *fifos,
+               struct dm_event_daemon_message *msg, int cmd,
+               const char *dso_name, const char *dev_name,
+               enum dm_event_mask evmask, uint32_t timeout);
+int init_fifos(struct dm_event_fifos *fifos);
+void fini_fifos(struct dm_event_fifos *fifos);
+
+#endif /* __DMEVENTD_DOT_H__ */
diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c
new file mode 100644 (file)
index 0000000..bc8ad99
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "libdevmapper-event.h"
+//#include "libmultilog.h"
+#include "dmeventd.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>         /* for htonl, ntohl */
+
+static int _sequence_nr = 0;
+
+struct dm_event_handler {
+       char *dso;
+
+       char *dmeventd_path;
+
+       char *dev_name;
+
+       char *uuid;
+       int major;
+       int minor;
+       uint32_t timeout;
+
+       enum dm_event_mask mask;
+};
+
+static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh)
+{
+       dm_free(dmevh->dev_name);
+       dm_free(dmevh->uuid);
+       dmevh->dev_name = dmevh->uuid = NULL;
+       dmevh->major = dmevh->minor = 0;
+}
+
+struct dm_event_handler *dm_event_handler_create(void)
+{
+       struct dm_event_handler *dmevh = NULL;
+
+       if (!(dmevh = dm_malloc(sizeof(*dmevh))))
+               return NULL;
+
+       dmevh->dmeventd_path = NULL;
+       dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL;
+       dmevh->major = dmevh->minor = 0;
+       dmevh->mask = 0;
+       dmevh->timeout = 0;
+
+       return dmevh;
+}
+
+void dm_event_handler_destroy(struct dm_event_handler *dmevh)
+{
+       _dm_event_handler_clear_dev_info(dmevh);
+       dm_free(dmevh->dso);
+       dm_free(dmevh->dmeventd_path);
+       dm_free(dmevh);
+}
+
+int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path)
+{
+       if (!dmeventd_path) /* noop */
+               return 0;
+
+       dm_free(dmevh->dmeventd_path);
+
+       dmevh->dmeventd_path = dm_strdup(dmeventd_path);
+       if (!dmevh->dmeventd_path)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path)
+{
+       if (!path) /* noop */
+               return 0;
+       dm_free(dmevh->dso);
+
+       dmevh->dso = dm_strdup(path);
+       if (!dmevh->dso)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name)
+{
+       if (!dev_name)
+               return 0;
+
+       _dm_event_handler_clear_dev_info(dmevh);
+
+       dmevh->dev_name = dm_strdup(dev_name);
+       if (!dmevh->dev_name)
+               return -ENOMEM;
+       return 0;
+}
+
+int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid)
+{
+       if (!uuid)
+               return 0;
+
+       _dm_event_handler_clear_dev_info(dmevh);
+
+       dmevh->uuid = dm_strdup(uuid);
+       if (!dmevh->uuid)
+               return -ENOMEM;
+       return 0;
+}
+
+void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major)
+{
+       int minor = dmevh->minor;
+
+       _dm_event_handler_clear_dev_info(dmevh);
+
+       dmevh->major = major;
+       dmevh->minor = minor;
+}
+
+void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor)
+{
+       int major = dmevh->major;
+
+       _dm_event_handler_clear_dev_info(dmevh);
+
+       dmevh->major = major;
+       dmevh->minor = minor;
+}
+
+void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
+                                    enum dm_event_mask evmask)
+{
+       dmevh->mask = evmask;
+}
+
+void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
+{
+       dmevh->timeout = timeout;
+}
+
+const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
+{
+       return dmevh->dso;
+}
+
+const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
+{
+       return dmevh->dev_name;
+}
+
+const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
+{
+       return dmevh->uuid;
+}
+
+int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
+{
+       return dmevh->major;
+}
+
+int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
+{
+       return dmevh->minor;
+}
+
+int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
+{
+       return dmevh->timeout;
+}
+
+enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
+{
+       return dmevh->mask;
+}
+
+static int _check_message_id(struct dm_event_daemon_message *msg)
+{
+       int pid, seq_nr;
+
+       if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
+           (pid != getpid()) || (seq_nr != _sequence_nr)) {
+               log_error("Ignoring out-of-sequence reply from dmeventd. "
+                         "Expected %d:%d but received %s", getpid(),
+                         _sequence_nr, msg->data);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * daemon_read
+ * @fifos
+ * @msg
+ *
+ * Read message from daemon.
+ *
+ * Returns: 0 on failure, 1 on success
+ */
+static int _daemon_read(struct dm_event_fifos *fifos,
+                       struct dm_event_daemon_message *msg)
+{
+       unsigned bytes = 0;
+       int ret, i;
+       fd_set fds;
+       struct timeval tval = { 0, 0 };
+       size_t size = 2 * sizeof(uint32_t);     /* status + size */
+       uint32_t *header = alloca(size);
+       char *buf = (char *)header;
+
+       while (bytes < size) {
+               for (i = 0, ret = 0; (i < 20) && (ret < 1); i++) {
+                       /* Watch daemon read FIFO for input. */
+                       FD_ZERO(&fds);
+                       FD_SET(fifos->server, &fds);
+                       tval.tv_sec = 1;
+                       ret = select(fifos->server + 1, &fds, NULL, NULL,
+                                    &tval);
+                       if (ret < 0 && errno != EINTR) {
+                               log_error("Unable to read from event server");
+                               return 0;
+                       }
+               }
+               if (ret < 1) {
+                       log_error("Unable to read from event server.");
+                       return 0;
+               }
+
+               ret = read(fifos->server, buf + bytes, size);
+               if (ret < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       else {
+                               log_error("Unable to read from event server.");
+                               return 0;
+                       }
+               }
+
+               bytes += ret;
+               if (header && (bytes == 2 * sizeof(uint32_t))) {
+                       msg->cmd = ntohl(header[0]);
+                       msg->size = ntohl(header[1]);
+                       buf = msg->data = dm_malloc(msg->size);
+                       size = msg->size;
+                       bytes = 0;
+                       header = 0;
+               }
+       }
+
+       if (bytes != size) {
+               dm_free(msg->data);
+               msg->data = NULL;
+       }
+       return bytes == size;
+}
+
+/* Write message to daemon. */
+static int _daemon_write(struct dm_event_fifos *fifos,
+                        struct dm_event_daemon_message *msg)
+{
+       unsigned bytes = 0;
+       int ret = 0;
+       fd_set fds;
+
+       size_t size = 2 * sizeof(uint32_t) + msg->size;
+       uint32_t *header = alloca(size);
+       char *buf = (char *)header;
+       char drainbuf[128];
+       struct timeval tval = { 0, 0 };
+
+       header[0] = htonl(msg->cmd);
+       header[1] = htonl(msg->size);
+       memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
+
+       /* drain the answer fifo */
+       while (1) {
+               FD_ZERO(&fds);
+               FD_SET(fifos->server, &fds);
+               tval.tv_usec = 100;
+               ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
+               if ((ret < 0) && (errno != EINTR)) {
+                       log_error("Unable to talk to event daemon");
+                       return 0;
+               }
+               if (ret == 0)
+                       break;
+               read(fifos->server, drainbuf, 127);
+       }
+
+       while (bytes < size) {
+               do {
+                       /* Watch daemon write FIFO to be ready for output. */
+                       FD_ZERO(&fds);
+                       FD_SET(fifos->client, &fds);
+                       ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
+                       if ((ret < 0) && (errno != EINTR)) {
+                               log_error("Unable to talk to event daemon");
+                               return 0;
+                       }
+               } while (ret < 1);
+
+               ret = write(fifos->client, buf + bytes, size - bytes);
+               if (ret < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       else {
+                               log_error("Unable to talk to event daemon");
+                               return 0;
+                       }
+               }
+
+               bytes += ret;
+       }
+
+       return bytes == size;
+}
+
+int daemon_talk(struct dm_event_fifos *fifos,
+               struct dm_event_daemon_message *msg, int cmd,
+               const char *dso_name, const char *dev_name,
+               enum dm_event_mask evmask, uint32_t timeout)
+{
+       const char *dso = dso_name ? dso_name : "-";
+       const char *dev = dev_name ? dev_name : "-";
+       const char *fmt = "%d:%d %s %s %u %" PRIu32;
+       int msg_size;
+       memset(msg, 0, sizeof(*msg));
+
+       /*
+        * Set command and pack the arguments
+        * into ASCII message string.
+        */
+       msg->cmd = cmd;
+       if (cmd == DM_EVENT_CMD_HELLO)
+               fmt = "%d:%d HELLO";
+       if ((msg_size = dm_asprintf(&(msg->data), fmt, getpid(), _sequence_nr,
+                                   dso, dev, evmask, timeout)) < 0) {
+               log_error("_daemon_talk: message allocation failed");
+               return -ENOMEM;
+       }
+       msg->size = msg_size;
+
+       /*
+        * Write command and message to and
+        * read status return code from daemon.
+        */
+       if (!_daemon_write(fifos, msg)) {
+               stack;
+               dm_free(msg->data);
+               msg->data = 0;
+               return -EIO;
+       }
+
+       do {
+
+               dm_free(msg->data);
+               msg->data = 0;
+
+               if (!_daemon_read(fifos, msg)) {
+                       stack;
+                       return -EIO;
+               }
+       } while (!_check_message_id(msg));
+
+       _sequence_nr++;
+
+       return (int32_t) msg->cmd;
+}
+
+/*
+ * start_daemon
+ *
+ * This function forks off a process (dmeventd) that will handle
+ * the events.  I am currently test opening one of the fifos to
+ * ensure that the daemon is running and listening...  I thought
+ * this would be less expensive than fork/exec'ing every time.
+ * Perhaps there is an even quicker/better way (no, checking the
+ * lock file is _not_ a better way).
+ *
+ * Returns: 1 on success, 0 otherwise
+ */
+static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
+{
+       int pid, ret = 0;
+       int status;
+       struct stat statbuf;
+       char default_dmeventd_path[] = DMEVENTD_PATH;
+       char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL };
+
+       if (stat(fifos->client_path, &statbuf))
+               goto start_server;
+
+       if (!S_ISFIFO(statbuf.st_mode)) {
+               log_error("%s is not a fifo.", fifos->client_path);
+               return 0;
+       }
+
+       /* Anyone listening?  If not, errno will be ENXIO */
+       fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
+       if (fifos->client >= 0) {
+               /* server is running and listening */
+
+               close(fifos->client);
+               return 1;
+       } else if (errno != ENXIO) {
+               /* problem */
+
+               log_error("%s: Can't open client fifo %s: %s",
+                         __func__, fifos->client_path, strerror(errno));
+               stack;
+               return 0;
+       }
+
+      start_server:
+       /* server is not running */
+
+       if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) {
+               log_error("Unable to find dmeventd.");
+               return_0;
+       }
+
+       pid = fork();
+
+       if (pid < 0)
+               log_error("Unable to fork.");
+
+       else if (!pid) {
+               execvp(args[0], args);
+               log_error("Unable to exec dmeventd: %s", strerror(errno));
+               _exit(EXIT_FAILURE);
+       } else {
+               if (waitpid(pid, &status, 0) < 0)
+                       log_error("Unable to start dmeventd: %s",
+                                 strerror(errno));
+               else if (WEXITSTATUS(status))
+                       log_error("Unable to start dmeventd.");
+               else
+                       ret = 1;
+       }
+
+       return ret;
+}
+
+int init_fifos(struct dm_event_fifos *fifos)
+{
+       /* FIXME? Is fifo the most suitable method? Why not share
+          comms/daemon code with something else e.g. multipath? */
+
+       /* FIXME Make these either configurable or depend directly on dmeventd_path */
+       fifos->client_path = DM_EVENT_FIFO_CLIENT;
+       fifos->server_path = DM_EVENT_FIFO_SERVER;
+
+       /* Open the fifo used to read from the daemon. */
+       if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
+               log_error("%s: open server fifo %s",
+                         __func__, fifos->server_path);
+               stack;
+               return 0;
+       }
+
+       /* Lock out anyone else trying to do communication with the daemon. */
+       if (flock(fifos->server, LOCK_EX) < 0) {
+               log_error("%s: flock %s", __func__, fifos->server_path);
+               close(fifos->server);
+               return 0;
+       }
+
+/*     if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
+       if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) {
+               log_error("%s: Can't open client fifo %s: %s",
+                         __func__, fifos->client_path, strerror(errno));
+               close(fifos->server);
+               stack;
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Initialize client. */
+static int _init_client(char *dmeventd_path, struct dm_event_fifos *fifos)
+{
+       /* init fifos */
+       memset(fifos, 0, sizeof(*fifos));
+
+       /* FIXME Make these either configurable or depend directly on dmeventd_path */
+       fifos->client_path = DM_EVENT_FIFO_CLIENT;
+       fifos->server_path = DM_EVENT_FIFO_SERVER;
+
+       if (!_start_daemon(dmeventd_path, fifos))
+               return_0;
+
+       return init_fifos(fifos);
+}
+
+void fini_fifos(struct dm_event_fifos *fifos)
+{
+       if (flock(fifos->server, LOCK_UN))
+               log_error("flock unlock %s", fifos->server_path);
+
+       close(fifos->client);
+       close(fifos->server);
+}
+
+/* Get uuid of a device */
+static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
+{
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+               log_error("_get_device_info: dm_task creation for info failed");
+               return NULL;
+       }
+
+       if (dmevh->uuid)
+               dm_task_set_uuid(dmt, dmevh->uuid);
+       else if (dmevh->dev_name)
+               dm_task_set_name(dmt, dmevh->dev_name);
+       else if (dmevh->major && dmevh->minor) {
+               dm_task_set_major(dmt, dmevh->major);
+               dm_task_set_minor(dmt, dmevh->minor);
+        }
+
+       /* FIXME Add name or uuid or devno to messages */
+       if (!dm_task_run(dmt)) {
+               log_error("_get_device_info: dm_task_run() failed");
+               goto failed;
+       }
+
+       if (!dm_task_get_info(dmt, &info)) {
+               log_error("_get_device_info: failed to get info for device");
+               goto failed;
+       }
+
+       if (!info.exists) {
+               log_error("_get_device_info: device not found");
+               goto failed;
+       }
+
+       return dmt;
+
+failed:
+       dm_task_destroy(dmt);
+       return NULL;
+}
+
+/* Handle the event (de)registration call and return negative error codes. */
+static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_message *msg,
+                    const char *dso_name, const char *dev_name,
+                    enum dm_event_mask evmask, uint32_t timeout)
+{
+       int ret;
+       struct dm_event_fifos fifos;
+
+       if (!_init_client(dmeventd_path, &fifos)) {
+               stack;
+               return -ESRCH;
+       }
+
+       ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
+
+       dm_free(msg->data);
+       msg->data = 0;
+
+       if (!ret)
+               ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
+
+       /* what is the opposite of init? */
+       fini_fifos(&fifos);
+
+       return ret;
+}
+
+/* External library interface. */
+int dm_event_register_handler(const struct dm_event_handler *dmevh)
+{
+       int ret = 1, err;
+       const char *uuid;
+       struct dm_task *dmt;
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+       if (!(dmt = _get_device_info(dmevh))) {
+               stack;
+               return 0;
+       }
+
+       uuid = dm_task_get_uuid(dmt);
+
+       if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
+                            dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
+               log_error("%s: event registration failed: %s",
+                         dm_task_get_name(dmt),
+                         msg.data ? msg.data : strerror(-err));
+               ret = 0;
+       }
+
+       dm_free(msg.data);
+
+       dm_task_destroy(dmt);
+
+       return ret;
+}
+
+int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
+{
+       int ret = 1, err;
+       const char *uuid;
+       struct dm_task *dmt;
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+       if (!(dmt = _get_device_info(dmevh))) {
+               stack;
+               return 0;
+       }
+
+       uuid = dm_task_get_uuid(dmt);
+
+       if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
+                           dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
+               log_error("%s: event deregistration failed: %s",
+                         dm_task_get_name(dmt),
+                         msg.data ? msg.data : strerror(-err));
+               ret = 0;
+       }
+
+       dm_free(msg.data);
+
+       dm_task_destroy(dmt);
+
+       return ret;
+}
+
+/* Fetch a string off src and duplicate it into *dest. */
+/* FIXME: move to separate module to share with the daemon. */
+static char *_fetch_string(char **src, const int delimiter)
+{
+       char *p, *ret;
+
+       if ((p = strchr(*src, delimiter)))
+               *p = 0;
+
+       if ((ret = dm_strdup(*src)))
+               *src += strlen(ret) + 1;
+
+       if (p)
+               *p = delimiter;
+
+       return ret;
+}
+
+/* Parse a device message from the daemon. */
+static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
+                        char **uuid, enum dm_event_mask *evmask)
+{
+       char *id = NULL;
+       char *p = msg->data;
+
+       if ((id = _fetch_string(&p, ' ')) &&
+           (*dso_name = _fetch_string(&p, ' ')) &&
+           (*uuid = _fetch_string(&p, ' '))) {
+               *evmask = atoi(p);
+
+               dm_free(id);
+               return 0;
+       }
+
+       if (id)
+               dm_free(id);
+       return -ENOMEM;
+}
+
+/*
+ * Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
+ */
+int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
+{
+       int ret = 0;
+       const char *uuid = NULL;
+       char *reply_dso = NULL, *reply_uuid = NULL;
+       enum dm_event_mask reply_mask = 0;
+       struct dm_task *dmt = NULL;
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+       struct dm_info info;
+
+       if (!(dmt = _get_device_info(dmevh))) {
+               stack;
+               return 0;
+       }
+
+       uuid = dm_task_get_uuid(dmt);
+
+       if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
+                            DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
+                             &msg, dmevh->dso, uuid, dmevh->mask, 0))) {
+               /* FIXME this will probably horribly break if we get
+                  ill-formatted reply */
+               ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
+       } else {
+               ret = -ENOENT;
+               goto fail;
+       }
+
+       dm_task_destroy(dmt);
+       dmt = NULL;
+
+       dm_free(msg.data);
+       msg.data = NULL;
+
+       _dm_event_handler_clear_dev_info(dmevh);
+       dmevh->uuid = dm_strdup(reply_uuid);
+       if (!dmevh->uuid) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       if (!(dmt = _get_device_info(dmevh))) {
+               ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
+               goto fail;
+       }
+
+       dm_event_handler_set_dso(dmevh, reply_dso);
+       dm_event_handler_set_event_mask(dmevh, reply_mask);
+
+       dm_free(reply_dso);
+       reply_dso = NULL;
+
+       dm_free(reply_uuid);
+       reply_uuid = NULL;
+
+       dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
+       if (!dmevh->dev_name) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       if (!dm_task_get_info(dmt, &info)) {
+               ret = -1;
+               goto fail;
+       }
+
+       dmevh->major = info.major;
+       dmevh->minor = info.minor;
+
+       dm_task_destroy(dmt);
+
+       return ret;
+
+ fail:
+       dm_free(msg.data);
+       dm_free(reply_dso);
+       dm_free(reply_uuid);
+       _dm_event_handler_clear_dev_info(dmevh);
+       if (dmt)
+               dm_task_destroy(dmt);
+       return ret;
+}
+
+#if 0                          /* left out for now */
+
+static char *_skip_string(char *src, const int delimiter)
+{
+       src = srtchr(src, delimiter);
+       if (src && *(src + 1))
+               return src + 1;
+       return NULL;
+}
+
+int dm_event_set_timeout(const char *device_path, uint32_t timeout)
+{
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+       if (!device_exists(device_path))
+               return -ENODEV;
+
+       return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
+                        NULL, device_path, 0, timeout);
+}
+
+int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
+{
+       int ret;
+       struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+       if (!device_exists(device_path))
+               return -ENODEV;
+       if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
+                            0, 0))) {
+               char *p = _skip_string(msg.data, ' ');
+               if (!p) {
+                       log_error("malformed reply from dmeventd '%s'\n",
+                                 msg.data);
+                       return -EIO;
+               }
+               *timeout = atoi(p);
+       }
+       if (msg.data)
+               dm_free(msg.data);
+       return ret;
+}
+#endif
diff --git a/daemons/dmeventd/libdevmapper-event.h b/daemons/dmeventd/libdevmapper-event.h
new file mode 100644 (file)
index 0000000..0de20c1
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Note that this file is released only as part of a technology preview
+ * and its contents may change in future updates in ways that do not
+ * preserve compatibility.
+ */
+
+#ifndef LIB_DMEVENT_H
+#define LIB_DMEVENT_H
+
+#include <stdint.h>
+
+/*
+ * Event library interface.
+ */
+
+enum dm_event_mask {
+       DM_EVENT_SETTINGS_MASK  = 0x0000FF,
+       DM_EVENT_SINGLE         = 0x000001, /* Report multiple errors just once. */
+       DM_EVENT_MULTI          = 0x000002, /* Report all of them. */
+
+       DM_EVENT_ERROR_MASK     = 0x00FF00,
+       DM_EVENT_SECTOR_ERROR   = 0x000100, /* Failure on a particular sector. */
+       DM_EVENT_DEVICE_ERROR   = 0x000200, /* Device failure. */
+       DM_EVENT_PATH_ERROR     = 0x000400, /* Failure on an io path. */
+       DM_EVENT_ADAPTOR_ERROR  = 0x000800, /* Failure of a host adaptor. */
+
+       DM_EVENT_STATUS_MASK    = 0xFF0000,
+       DM_EVENT_SYNC_STATUS    = 0x010000, /* Mirror synchronization completed/failed. */
+       DM_EVENT_TIMEOUT        = 0x020000, /* Timeout has occured */
+
+       DM_EVENT_REGISTRATION_PENDING = 0x1000000, /* Monitor thread is setting-up/shutting-down */
+};
+
+#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
+
+struct dm_event_handler;
+
+struct dm_event_handler *dm_event_handler_create(void);
+void dm_event_handler_destroy(struct dm_event_handler *dmevh);
+
+/*
+ * Path of shared library to handle events.
+ *
+ * All of dmeventd, dso, device_name and uuid strings are duplicated so
+ * you do not need to keep the pointers valid after the call succeeds.
+ * They may return -ENOMEM though.
+ */
+int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path);
+
+/*
+ * Path of dmeventd binary.
+ */
+int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path);
+
+/*
+ * Identify the device to monitor by exactly one of device_name, uuid or
+ * device number. String arguments are duplicated, see above.
+ */
+int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *device_name);
+
+int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid);
+
+void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major);
+void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor);
+void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout);
+
+/*
+ * Specify mask for events to monitor.
+ */
+void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
+                                    enum dm_event_mask evmask);
+
+const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh);
+const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh);
+const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_major(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_minor(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh);
+enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh);
+
+/* FIXME Review interface (what about this next thing?) */
+int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next);
+
+/*
+ * Initiate monitoring using dmeventd.
+ */
+int dm_event_register_handler(const struct dm_event_handler *dmevh);
+int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
+
+/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
+   detailed descriptions. */
+void process_event(struct dm_task *dmt, enum dm_event_mask evmask, void **user);
+int register_device(const char *device_name, const char *uuid, int major, int minor, void **user);
+int unregister_device(const char *device_name, const char *uuid, int major,
+                     int minor, void **user);
+
+#endif
diff --git a/daemons/dmeventd/libdevmapper-event.pc.in b/daemons/dmeventd/libdevmapper-event.pc.in
new file mode 100644 (file)
index 0000000..839433f
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: devmapper-event
+Description: device-mapper event library
+Version: @DM_LIB_PATCHLEVEL@
+Cflags: -I${includedir}
+Libs: -L${libdir} -ldevmapper-event
+Requires.private: devmapper
diff --git a/daemons/dmeventd/plugins/Makefile.in b/daemons/dmeventd/plugins/Makefile.in
new file mode 100644 (file)
index 0000000..45176ad
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SUBDIRS += lvm2 mirror snapshot
+
+include $(top_builddir)/make.tmpl
+
+mirror: lvm2
+snapshot: lvm2
+
diff --git a/daemons/dmeventd/plugins/lvm2/.exported_symbols b/daemons/dmeventd/plugins/lvm2/.exported_symbols
new file mode 100644 (file)
index 0000000..ebe3d05
--- /dev/null
@@ -0,0 +1,6 @@
+dmeventd_lvm2_init
+dmeventd_lvm2_exit
+dmeventd_lvm2_lock
+dmeventd_lvm2_unlock
+dmeventd_lvm2_pool
+dmeventd_lvm2_run
diff --git a/daemons/dmeventd/plugins/lvm2/Makefile.in b/daemons/dmeventd/plugins/lvm2/Makefile.in
new file mode 100644 (file)
index 0000000..46247aa
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CLDFLAGS += -L$(top_builddir)/tools
+
+SOURCES = dmeventd_lvm.c
+
+LIB_SHARED = libdevmapper-event-lvm2.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS)
+
+install_lvm2: install_lib_shared
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c
new file mode 100644 (file)
index 0000000..937d81d
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "log.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include <pthread.h>
+#include <syslog.h>
+
+extern int dmeventd_debug;
+
+/*
+ * register_device() is called first and performs initialisation.
+ * Only one device may be registered or unregistered at a time.
+ */
+static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Number of active registrations.
+ */
+static int _register_count = 0;
+static struct dm_pool *_mem_pool = NULL;
+static void *_lvm_handle = NULL;
+
+/*
+ * Currently only one event can be processed at a time.
+ */
+static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * FIXME Do not pass things directly to syslog, rather use the existing logging
+ * facilities to sort logging ... however that mechanism needs to be somehow
+ * configurable and we don't have that option yet
+ */
+static void _temporary_log_fn(int level,
+                             const char *file __attribute__((unused)),
+                             int line __attribute__((unused)),
+                             int dm_errno __attribute__((unused)),
+                             const char *message)
+{
+       level &= ~(_LOG_STDERR | _LOG_ONCE);
+
+       switch (level) {
+       case _LOG_DEBUG:
+               if (dmeventd_debug >= 3)
+                       syslog(LOG_DEBUG, "%s", message);
+               break;
+       case _LOG_INFO:
+               if (dmeventd_debug >= 2)
+                       syslog(LOG_INFO, "%s", message);
+               break;
+       case _LOG_NOTICE:
+               if (dmeventd_debug >= 1)
+                       syslog(LOG_NOTICE, "%s", message);
+               break;
+       case _LOG_WARN:
+               syslog(LOG_WARNING, "%s", message);
+               break;
+       case _LOG_ERR:
+               syslog(LOG_ERR, "%s", message);
+               break;
+       default:
+               syslog(LOG_CRIT, "%s", message);
+       }
+}
+
+void dmeventd_lvm2_lock(void)
+{
+       if (pthread_mutex_trylock(&_event_mutex)) {
+               syslog(LOG_NOTICE, "Another thread is handling an event. Waiting...");
+               pthread_mutex_lock(&_event_mutex);
+       }
+}
+
+void dmeventd_lvm2_unlock(void)
+{
+       pthread_mutex_unlock(&_event_mutex);
+}
+
+int dmeventd_lvm2_init(void)
+{
+       int r = 0;
+
+       pthread_mutex_lock(&_register_mutex);
+
+       /*
+        * Need some space for allocations.  1024 should be more
+        * than enough for what we need (device mapper name splitting)
+        */
+       if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024)))
+               goto out;
+
+       if (!_lvm_handle) {
+               lvm2_log_fn(_temporary_log_fn);
+               if (!(_lvm_handle = lvm2_init())) {
+                       dm_pool_destroy(_mem_pool);
+                       _mem_pool = NULL;
+                       goto out;
+               }
+               /* FIXME Temporary: move to dmeventd core */
+               lvm2_run(_lvm_handle, "_memlock_inc");
+       }
+
+       _register_count++;
+       r = 1;
+
+out:
+       pthread_mutex_unlock(&_register_mutex);
+       return r;
+}
+
+void dmeventd_lvm2_exit(void)
+{
+       pthread_mutex_lock(&_register_mutex);
+
+       if (!--_register_count) {
+               lvm2_run(_lvm_handle, "_memlock_dec");
+               dm_pool_destroy(_mem_pool);
+               _mem_pool = NULL;
+               lvm2_exit(_lvm_handle);
+               _lvm_handle = NULL;
+       }
+
+       pthread_mutex_unlock(&_register_mutex);
+}
+
+struct dm_pool *dmeventd_lvm2_pool(void)
+{
+       return _mem_pool;
+}
+
+int dmeventd_lvm2_run(const char *cmdline)
+{
+       return lvm2_run(_lvm_handle, cmdline);
+}
+
diff --git a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h
new file mode 100644 (file)
index 0000000..8efcb9b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Wrappers around liblvm2cmd functions for dmeventd plug-ins.
+ *
+ * liblvm2cmd is not thread-safe so the locking in this library helps dmeventd
+ * threads to co-operate in sharing a single instance.
+ *
+ * FIXME Either support this properly as a generic liblvm2cmd wrapper or make
+ * liblvm2cmd thread-safe so this can go away.
+ */
+
+#include "libdevmapper.h"
+
+#ifndef _DMEVENTD_LVMWRAP_H
+#define _DMEVENTD_LVMWRAP_H
+
+int dmeventd_lvm2_init(void);
+void dmeventd_lvm2_exit(void);
+int dmeventd_lvm2_run(const char *cmdline);
+
+void dmeventd_lvm2_lock(void);
+void dmeventd_lvm2_unlock(void);
+
+struct dm_pool *dmeventd_lvm2_pool(void);
+
+#endif /* _DMEVENTD_LVMWRAP_H */
diff --git a/daemons/dmeventd/plugins/mirror/.exported_symbols b/daemons/dmeventd/plugins/mirror/.exported_symbols
new file mode 100644 (file)
index 0000000..b88c705
--- /dev/null
@@ -0,0 +1,3 @@
+process_event
+register_device
+unregister_device
diff --git a/daemons/dmeventd/plugins/mirror/Makefile.in b/daemons/dmeventd/plugins/mirror/Makefile.in
new file mode 100644 (file)
index 0000000..7f80629
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2005, 2008-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
+CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
+
+SOURCES = dmeventd_mirror.c
+
+LIB_NAME = libdevmapper-event-lvm2mirror
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper-event-lvm2 -ldevmapper
+
+install_lvm2: install_dm_plugin
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c
new file mode 100644 (file)
index 0000000..3e97d87
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include <syslog.h> /* FIXME Replace syslog with multilog */
+/* FIXME Missing openlog? */
+/* FIXME Replace most syslogs with log_error() style messages and add complete context. */
+/* FIXME Reformat to 80 char lines. */
+
+#define ME_IGNORE    0
+#define ME_INSYNC    1
+#define ME_FAILURE   2
+
+static int _process_status_code(const char status_code, const char *dev_name,
+                               const char *dev_type, int r)
+{
+       /*
+        *    A => Alive - No failures
+        *    D => Dead - A write failure occurred leaving mirror out-of-sync
+        *    F => Flush failed.
+        *    S => Sync - A sychronization failure occurred, mirror out-of-sync
+        *    R => Read - A read failure occurred, mirror data unaffected
+        *    U => Unclassified failure (bug)
+        */ 
+       if (status_code == 'F') {
+               syslog(LOG_ERR, "%s device %s flush failed.",
+                      dev_type, dev_name);
+               r = ME_FAILURE;
+       } else if (status_code == 'S')
+               syslog(LOG_ERR, "%s device %s sync failed.",
+                      dev_type, dev_name);
+       else if (status_code == 'R')
+               syslog(LOG_ERR, "%s device %s read failed.",
+                      dev_type, dev_name);
+       else if (status_code != 'A') {
+               syslog(LOG_ERR, "%s device %s has failed (%c).",
+                      dev_type, dev_name, status_code);
+               r = ME_FAILURE;
+       }
+
+       return r;
+}
+
+static int _get_mirror_event(char *params)
+{
+       int i, r = ME_INSYNC;
+       char **args = NULL;
+       char *dev_status_str;
+       char *log_status_str;
+       char *sync_str;
+       char *p = NULL;
+       int log_argc, num_devs;
+
+       /*
+        * dm core parms:            0 409600 mirror
+        * Mirror core parms:        2 253:4 253:5 400/400
+        * New-style failure params: 1 AA
+        * New-style log params:     3 cluster 253:3 A
+        *                       or  3 disk 253:3 A
+        *                       or  1 core
+        */
+
+       /* number of devices */
+       if (!dm_split_words(params, 1, 0, &p))
+               goto out_parse;
+
+       if (!(num_devs = atoi(p)))
+               goto out_parse;
+       p += strlen(p) + 1;
+
+       /* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */
+       args = dm_malloc((num_devs + 7) * sizeof(char *));
+       if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5)
+               goto out_parse;
+
+       dev_status_str = args[2 + num_devs];
+       log_argc = atoi(args[3 + num_devs]);
+       log_status_str = args[3 + num_devs + log_argc];
+       sync_str = args[num_devs];
+
+       /* Check for bad mirror devices */
+       for (i = 0; i < num_devs; i++)
+               r = _process_status_code(dev_status_str[i], args[i],
+                       i ? "Secondary mirror" : "Primary mirror", r);
+
+       /* Check for bad disk log device */
+       if (log_argc > 1)
+               r = _process_status_code(log_status_str[0],
+                                        args[2 + num_devs + log_argc],
+                                        "Log", r);
+
+       if (r == ME_FAILURE)
+               goto out;
+
+       p = strstr(sync_str, "/");
+       if (p) {
+               p[0] = '\0';
+               if (strcmp(sync_str, p+1))
+                       r = ME_IGNORE;
+               p[0] = '/';
+       } else
+               goto out_parse;
+
+out:
+       dm_free(args);
+       return r;
+       
+out_parse:
+       dm_free(args);
+       syslog(LOG_ERR, "Unable to parse mirror status string.");
+       return ME_IGNORE;
+}
+
+static int _remove_failed_devices(const char *device)
+{
+       int r;
+#define CMD_SIZE 256   /* FIXME Use system restriction */
+       char cmd_str[CMD_SIZE];
+       char *vg = NULL, *lv = NULL, *layer = NULL;
+
+       if (strlen(device) > 200)  /* FIXME Use real restriction */
+               return -ENAMETOOLONG;   /* FIXME These return code distinctions are not used so remove them! */
+
+       if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
+               syslog(LOG_ERR, "Unable to determine VG name from %s.",
+                      device);
+               return -ENOMEM; /* FIXME Replace with generic error return - reason for failure has already got logged */
+       }
+
+       /* strip off the mirror component designations */
+       layer = strstr(lv, "_mlog");
+       if (layer)
+               *layer = '\0';
+
+       /* FIXME Is any sanity-checking required on %s? */
+       if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) {
+               /* this error should be caught above, but doesn't hurt to check again */
+               syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
+               return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */
+       }
+
+       r = dmeventd_lvm2_run(cmd_str);
+
+       syslog(LOG_INFO, "Repair of mirrored LV %s/%s %s.", vg, lv,
+              (r == ECMD_PROCESSED) ? "finished successfully" : "failed");
+
+       return (r == ECMD_PROCESSED) ? 0 : -1;
+}
+
+void process_event(struct dm_task *dmt,
+                  enum dm_event_mask event __attribute__((unused)),
+                  void **unused __attribute__((unused)))
+{
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params;
+       const char *device = dm_task_get_name(dmt);
+
+       dmeventd_lvm2_lock();
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &target_type, &params);
+
+               if (!target_type) {
+                       syslog(LOG_INFO, "%s mapping lost.", device);
+                       continue;
+               }
+
+               if (strcmp(target_type, "mirror")) {
+                       syslog(LOG_INFO, "%s has unmirrored portion.", device);
+                       continue;
+               }
+
+               switch(_get_mirror_event(params)) {
+               case ME_INSYNC:
+                       /* FIXME: all we really know is that this
+                          _part_ of the device is in sync
+                          Also, this is not an error
+                       */
+                       syslog(LOG_NOTICE, "%s is now in-sync.", device);
+                       break;
+               case ME_FAILURE:
+                       syslog(LOG_ERR, "Device failure in %s.", device);
+                       if (_remove_failed_devices(device))
+                               /* FIXME Why are all the error return codes unused? Get rid of them? */
+                               syslog(LOG_ERR, "Failed to remove faulty devices in %s.",
+                                      device);
+                       /* Should check before warning user that device is now linear
+                       else
+                               syslog(LOG_NOTICE, "%s is now a linear device.\n",
+                                       device);
+                       */
+                       break;
+               case ME_IGNORE:
+                       break;
+               default:
+                       /* FIXME Provide value then! */
+                       syslog(LOG_INFO, "Unknown event received.");
+               }
+       } while (next);
+
+       dmeventd_lvm2_unlock();
+}
+
+int register_device(const char *device,
+                   const char *uuid __attribute__((unused)),
+                   int major __attribute__((unused)),
+                   int minor __attribute__((unused)),
+                   void **unused __attribute__((unused)))
+{
+       int r = dmeventd_lvm2_init();
+       syslog(LOG_INFO, "Monitoring mirror device %s for events.", device);
+       return r;
+}
+
+int unregister_device(const char *device,
+                     const char *uuid __attribute__((unused)),
+                     int major __attribute__((unused)),
+                     int minor __attribute__((unused)),
+                     void **unused __attribute__((unused)))
+{
+       syslog(LOG_INFO, "No longer monitoring mirror device %s for events.",
+              device);
+       dmeventd_lvm2_exit();
+       return 1;
+}
diff --git a/daemons/dmeventd/plugins/snapshot/.exported_symbols b/daemons/dmeventd/plugins/snapshot/.exported_symbols
new file mode 100644 (file)
index 0000000..b88c705
--- /dev/null
@@ -0,0 +1,3 @@
+process_event
+register_device
+unregister_device
diff --git a/daemons/dmeventd/plugins/snapshot/Makefile.in b/daemons/dmeventd/plugins/snapshot/Makefile.in
new file mode 100644 (file)
index 0000000..b8414b2
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
+CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
+
+SOURCES = dmeventd_snapshot.c
+
+LIB_SHARED = libdevmapper-event-lvm2snapshot.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper-event-lvm2 -ldevmapper
+
+install_lvm2: install_dm_plugin
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
new file mode 100644 (file)
index 0000000..6fc9f56
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include "lvm-string.h"
+
+#include <sys/wait.h>
+#include <syslog.h> /* FIXME Replace syslog with multilog */
+/* FIXME Missing openlog? */
+
+/* First warning when snapshot is 80% full. */
+#define WARNING_THRESH 80
+/* Run a check every 5%. */
+#define CHECK_STEP 5
+/* Do not bother checking snapshots less than 50% full. */
+#define CHECK_MINIMUM 50
+
+#define UMOUNT_COMMAND "/bin/umount"
+
+struct snap_status {
+       int invalid;
+       int used;
+       int max;
+};
+
+/* FIXME possibly reconcile this with target_percent when we gain
+   access to regular LVM library here. */
+static void _parse_snapshot_params(char *params, struct snap_status *status)
+{
+       char *p;
+       /*
+        * xx/xx        -- fractions used/max
+        * Invalid      -- snapshot invalidated
+        * Unknown      -- status unknown
+        */
+       status->used = status->max = 0;
+
+       if (!strncmp(params, "Invalid", 7)) {
+               status->invalid = 1;
+               return;
+       }
+
+       /*
+        * When we return without setting non-zero max, the parent is
+        * responsible for reporting errors.
+        */
+       if (!strncmp(params, "Unknown", 7))
+               return;
+
+       if (!(p = strstr(params, "/")))
+               return;
+
+       *p = '\0';
+       p++;
+
+       status->used = atoi(params);
+       status->max = atoi(p);
+}
+
+static int _run(const char *cmd, ...)
+{
+        va_list ap;
+        int argc = 1; /* for argv[0], i.e. cmd */
+        int i = 0;
+        const char **argv;
+        pid_t pid = fork();
+        int status;
+
+        if (pid == 0) { /* child */
+                va_start(ap, cmd);
+                while (va_arg(ap, const char *))
+                        ++ argc;
+                va_end(ap);
+
+                /* + 1 for the terminating NULL */
+                argv = alloca(sizeof(const char *) * (argc + 1));
+
+                argv[0] = cmd;
+                va_start(ap, cmd);
+                while ((argv[++i] = va_arg(ap, const char *)));
+                va_end(ap);
+
+                execvp(cmd, (char **)argv);
+                syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno));
+                exit(127);
+        }
+
+        if (pid > 0) { /* parent */
+                if (waitpid(pid, &status, 0) != pid)
+                        return 0; /* waitpid failed */
+                if (!WIFEXITED(status) || WEXITSTATUS(status))
+                        return 0; /* the child failed */
+        }
+
+        if (pid < 0)
+                return 0; /* fork failed */
+
+        return 1; /* all good */
+}
+
+static int _extend(const char *device)
+{
+       char *vg = NULL, *lv = NULL, *layer = NULL;
+       char cmd_str[1024];
+       int r = 0;
+
+       if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
+               syslog(LOG_ERR, "Unable to determine VG name from %s.", device);
+               return 0;
+       }
+       if (sizeof(cmd_str) <= snprintf(cmd_str, sizeof(cmd_str),
+                                       "lvextend --use-policies %s/%s", vg, lv)) {
+               syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
+               return 0;
+       }
+
+       r = dmeventd_lvm2_run(cmd_str);
+       syslog(LOG_INFO, "Extension of snapshot %s/%s %s.", vg, lv,
+              (r == ECMD_PROCESSED) ? "finished successfully" : "failed");
+       return r == ECMD_PROCESSED;
+}
+
+static void _umount(const char *device, int major, int minor)
+{
+       FILE *mounts;
+       char buffer[4096];
+       char *words[3];
+       struct stat st;
+
+       if (!(mounts = fopen("/proc/mounts", "r"))) {
+               syslog(LOG_ERR, "Could not read /proc/mounts. Not umounting %s.\n", device);
+               return;
+       }
+
+       while (!feof(mounts)) {
+               /* read a line of /proc/mounts */
+               if (!fgets(buffer, sizeof(buffer), mounts))
+                       break; /* eof, likely */
+
+               /* words[0] is the mount point and words[1] is the device path */
+               dm_split_words(buffer, 3, 0, words);
+
+               /* find the major/minor of the device */
+               if (stat(words[0], &st))
+                       continue; /* can't stat, skip this one */
+
+               if (S_ISBLK(st.st_mode) &&
+                   major(st.st_rdev) == major &&
+                   minor(st.st_rdev) == minor) {
+                       syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.", device, words[1]);
+                        if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
+                                syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.",
+                                       device, words[1], strerror(errno));
+               }
+       }
+
+       if (fclose(mounts))
+               syslog(LOG_ERR, "Failed to close /proc/mounts.\n");
+}
+
+void process_event(struct dm_task *dmt,
+                  enum dm_event_mask event __attribute__((unused)),
+                  void **private)
+{
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params;
+       struct snap_status status = { 0 };
+       const char *device = dm_task_get_name(dmt);
+       int percent, *percent_check = (int*)private;
+
+       /* No longer monitoring, waiting for remove */
+       if (!*percent_check)
+               return;
+
+       dmeventd_lvm2_lock();
+
+       dm_get_next_target(dmt, next, &start, &length, &target_type, &params);
+       if (!target_type)
+               goto out;
+
+       _parse_snapshot_params(params, &status);
+
+       if (status.invalid) {
+               syslog(LOG_ERR, "Trying to umount invalid snapshot %s...\n", device);
+               struct dm_info info;
+               if (dm_task_get_info(dmt, &info)) {
+                       dmeventd_lvm2_unlock();
+                       _umount(device, info.major, info.minor);
+                        return;
+               } /* else; too bad, but this is best-effort thing... */
+       }
+
+       /*
+        * If the snapshot has been invalidated or we failed to parse
+        * the status string. Report the full status string to syslog.
+        */
+       if (status.invalid || !status.max) {
+               syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
+               *percent_check = 0;
+               goto out;
+       }
+
+       percent = 100 * status.used / status.max;
+       if (percent >= *percent_check) {
+               /* Usage has raised more than CHECK_STEP since the last
+                  time. Run actions. */
+               *percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
+               if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
+                       syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
+               /* Try to extend the snapshot, in accord with user-set policies */
+               if (!_extend(device))
+                       syslog(LOG_ERR, "Failed to extend snapshot %s.", device);
+       }
+out:
+       dmeventd_lvm2_unlock();
+}
+
+int register_device(const char *device,
+                   const char *uuid __attribute__((unused)),
+                   int major __attribute__((unused)),
+                   int minor __attribute__((unused)),
+                   void **private)
+{
+       int *percent_check = (int*)private;
+       int r = dmeventd_lvm2_init();
+
+       *percent_check = CHECK_MINIMUM;
+
+       syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
+       return r;
+}
+
+int unregister_device(const char *device,
+                     const char *uuid __attribute__((unused)),
+                     int major __attribute__((unused)),
+                     int minor __attribute__((unused)),
+                     void **unused __attribute__((unused)))
+{
+       syslog(LOG_INFO, "No longer monitoring snapshot %s\n",
+              device);
+       dmeventd_lvm2_exit();
+       return 1;
+}
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644 (file)
index 0000000..c63e3ce
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CONFSRC=example.conf
+CONFDEST=lvm.conf
+
+include $(top_builddir)/make.tmpl
+
+install_lvm2: $(CONFSRC)
+       @if [ ! -e $(confdir)/$(CONFDEST) ]; then \
+               echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST)"; \
+               $(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST); \
+       fi
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += $(CONFSRC)
diff --git a/doc/example.conf.in b/doc/example.conf.in
new file mode 100644 (file)
index 0000000..8504f5d
--- /dev/null
@@ -0,0 +1,577 @@
+# This is an example configuration file for the LVM2 system.
+# It contains the default settings that would be used if there was no
+# @DEFAULT_SYS_DIR@/lvm.conf file.
+#
+# Refer to 'man lvm.conf' for further information including the file layout.
+#
+# To put this file in a different directory and override @DEFAULT_SYS_DIR@ set
+# the environment variable LVM_SYSTEM_DIR before running the tools.
+
+
+# This section allows you to configure which block devices should
+# be used by the LVM system.
+devices {
+
+    # Where do you want your volume groups to appear ?
+    dir = "/dev"
+
+    # An array of directories that contain the device nodes you wish
+    # to use with LVM2.
+    scan = [ "/dev" ]
+
+    # If several entries in the scanned directories correspond to the
+    # same block device and the tools need to display a name for device,
+    # all the pathnames are matched against each item in the following
+    # list of regular expressions in turn and the first match is used.
+    preferred_names = [ ]
+
+    # Try to avoid using undescriptive /dev/dm-N names, if present.
+    # preferred_names = [ "^/dev/mpath/", "^/dev/mapper/mpath", "^/dev/[hs]d" ]
+
+    # A filter that tells LVM2 to only use a restricted set of devices.
+    # The filter consists of an array of regular expressions.  These
+    # expressions can be delimited by a character of your choice, and
+    # prefixed with either an 'a' (for accept) or 'r' (for reject).
+    # The first expression found to match a device name determines if
+    # the device will be accepted or rejected (ignored).  Devices that
+    # don't match any patterns are accepted.
+
+    # Be careful if there there are symbolic links or multiple filesystem 
+    # entries for the same device as each name is checked separately against
+    # the list of patterns.  The effect is that if any name matches any 'a'
+    # pattern, the device is accepted; otherwise if any name matches any 'r'
+    # pattern it is rejected; otherwise it is accepted.
+
+    # Don't have more than one filter line active at once: only one gets used.
+
+    # Run vgscan after you change this parameter to ensure that
+    # the cache file gets regenerated (see below).
+    # If it doesn't do what you expect, check the output of 'vgscan -vvvv'.
+
+
+    # By default we accept every block device:
+    filter = [ "a/.*/" ]
+
+    # Exclude the cdrom drive
+    # filter = [ "r|/dev/cdrom|" ]
+
+    # When testing I like to work with just loopback devices:
+    # filter = [ "a/loop/", "r/.*/" ]
+
+    # Or maybe all loops and ide drives except hdc:
+    # filter =[ "a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|" ]
+
+    # Use anchors if you want to be really specific
+    # filter = [ "a|^/dev/hda8$|", "r/.*/" ]
+
+    # The results of the filtering are cached on disk to avoid
+    # rescanning dud devices (which can take a very long time).
+    # By default this cache is stored in the @DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@ directory
+    # in a file called '.cache'.
+    # It is safe to delete the contents: the tools regenerate it.
+    # (The old setting 'cache' is still respected if neither of
+    # these new ones is present.)
+    cache_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@"
+    cache_file_prefix = ""
+
+    # You can turn off writing this cache file by setting this to 0.
+    write_cache_state = 1
+
+    # Advanced settings.
+
+    # List of pairs of additional acceptable block device types found 
+    # in /proc/devices with maximum (non-zero) number of partitions.
+    # types = [ "fd", 16 ]
+
+    # If sysfs is mounted (2.6 kernels) restrict device scanning to 
+    # the block devices it believes are valid.
+    # 1 enables; 0 disables.
+    sysfs_scan = 1
+
+    # By default, LVM2 will ignore devices used as components of
+    # software RAID (md) devices by looking for md superblocks.
+    # 1 enables; 0 disables.
+    md_component_detection = 1
+
+    # By default, if a PV is placed directly upon an md device, LVM2
+    # will align its data blocks with the md device's stripe-width.
+    # 1 enables; 0 disables.
+    md_chunk_alignment = 1
+
+    # Default alignment of the start of a data area in MB.  If set to 0,
+    # a value of 64KB will be used.  Set to 1 for 1MiB, 2 for 2MiB, etc.
+    # default_data_alignment = @DEFAULT_DATA_ALIGNMENT@
+
+    # By default, the start of a PV's data area will be a multiple of
+    # the 'minimum_io_size' or 'optimal_io_size' exposed in sysfs.
+    # - minimum_io_size - the smallest request the device can perform
+    #   w/o incurring a read-modify-write penalty (e.g. MD's chunk size)
+    # - optimal_io_size - the device's preferred unit of receiving I/O
+    #   (e.g. MD's stripe width)
+    # minimum_io_size is used if optimal_io_size is undefined (0).
+    # If md_chunk_alignment is enabled, that detects the optimal_io_size.
+    # This setting takes precedence over md_chunk_alignment.
+    # 1 enables; 0 disables.
+    data_alignment_detection = 1
+
+    # Alignment (in KB) of start of data area when creating a new PV.
+    # md_chunk_alignment and data_alignment_detection are disabled if set.
+    # Set to 0 for the default alignment (see: data_alignment_default)
+    # or page size, if larger.
+    data_alignment = 0
+
+    # By default, the start of the PV's aligned data area will be shifted by
+    # the 'alignment_offset' exposed in sysfs.  This offset is often 0 but
+    # may be non-zero; e.g.: certain 4KB sector drives that compensate for
+    # windows partitioning will have an alignment_offset of 3584 bytes
+    # (sector 7 is the lowest aligned logical block, the 4KB sectors start
+    # at LBA -1, and consequently sector 63 is aligned on a 4KB boundary).
+    # But note that pvcreate --dataalignmentoffset will skip this detection.
+    # 1 enables; 0 disables.
+    data_alignment_offset_detection = 1
+
+    # If, while scanning the system for PVs, LVM2 encounters a device-mapper
+    # device that has its I/O suspended, it waits for it to become accessible.
+    # Set this to 1 to skip such devices.  This should only be needed
+    # in recovery situations.
+    ignore_suspended_devices = 0
+
+    # During each LVM operation errors received from each device are counted.
+    # If the counter of a particular device exceeds the limit set here, no
+    # further I/O is sent to that device for the remainder of the respective
+    # operation. Setting the parameter to 0 disables the counters altogether.
+    disable_after_error_count = 0
+
+    # Allow use of pvcreate --uuid without requiring --restorefile.
+    require_restorefile_with_uuid = 1
+}
+
+# This section allows you to configure the way in which LVM selects
+# free space for its Logical Volumes.
+#allocation {
+#    When searching for free space to extend an LV, the "cling"
+#    allocation policy will choose space on the same PVs as the last
+#    segment of the existing LV.  If there is insufficient space and a
+#    list of tags is defined here, it will check whether any of them are
+#    attached to the PVs concerned and then seek to match those PV tags
+#    between existing extents and new extents.
+#    Use the special tag "@*" as a wildcard to match any PV tag.
+#    
+#    Example: LVs are mirrored between two sites within a single VG.
+#    PVs are tagged with either @site1 or @site2 to indicate where
+#    they are situated.
+#
+#    cling_tag_list = [ "@site1", "@site2" ]
+#    cling_tag_list = [ "@*" ]
+#}
+
+# This section that allows you to configure the nature of the
+# information that LVM2 reports.
+log {
+
+    # Controls the messages sent to stdout or stderr.
+    # There are three levels of verbosity, 3 being the most verbose.
+    verbose = 0
+
+    # Should we send log messages through syslog?
+    # 1 is yes; 0 is no.
+    syslog = 1
+
+    # Should we log error and debug messages to a file?
+    # By default there is no log file.
+    #file = "/var/log/lvm2.log"
+
+    # Should we overwrite the log file each time the program is run?
+    # By default we append.
+    overwrite = 0
+
+    # What level of log messages should we send to the log file and/or syslog?
+    # There are 6 syslog-like log levels currently in use - 2 to 7 inclusive.
+    # 7 is the most verbose (LOG_DEBUG).
+    level = 0
+
+    # Format of output messages
+    # Whether or not (1 or 0) to indent messages according to their severity
+    indent = 1
+
+    # Whether or not (1 or 0) to display the command name on each line output
+    command_names = 0
+
+    # A prefix to use before the message text (but after the command name,
+    # if selected).  Default is two spaces, so you can see/grep the severity
+    # of each message.
+    prefix = "  "
+
+    # To make the messages look similar to the original LVM tools use:
+    #   indent = 0
+    #   command_names = 1
+    #   prefix = " -- "
+
+    # Set this if you want log messages during activation.
+    # Don't use this in low memory situations (can deadlock).
+    # activation = 0
+}
+
+# Configuration of metadata backups and archiving.  In LVM2 when we
+# talk about a 'backup' we mean making a copy of the metadata for the
+# *current* system.  The 'archive' contains old metadata configurations.
+# Backups are stored in a human readeable text format.
+backup {
+
+    # Should we maintain a backup of the current metadata configuration ?
+    # Use 1 for Yes; 0 for No.
+    # Think very hard before turning this off!
+    backup = 1
+
+    # Where shall we keep it ?
+    # Remember to back up this directory regularly!
+    backup_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_BACKUP_SUBDIR@"
+
+    # Should we maintain an archive of old metadata configurations.
+    # Use 1 for Yes; 0 for No.
+    # On by default.  Think very hard before turning this off.
+    archive = 1
+
+    # Where should archived files go ?
+    # Remember to back up this directory regularly!
+    archive_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_ARCHIVE_SUBDIR@"
+
+    # What is the minimum number of archive files you wish to keep ?
+    retain_min = 10
+
+    # What is the minimum time you wish to keep an archive file for ?
+    retain_days = 30
+}
+
+# Settings for the running LVM2 in shell (readline) mode.
+shell {
+
+    # Number of lines of history to store in ~/.lvm_history
+    history_size = 100
+}
+
+
+# Miscellaneous global LVM2 settings
+global {
+
+    # The file creation mask for any files and directories created.
+    # Interpreted as octal if the first digit is zero.
+    umask = 077
+
+    # Allow other users to read the files
+    #umask = 022
+
+    # Enabling test mode means that no changes to the on disk metadata
+    # will be made.  Equivalent to having the -t option on every
+    # command.  Defaults to off.
+    test = 0
+
+    # Default value for --units argument
+    units = "h"
+
+    # Since version 2.02.54, the tools distinguish between powers of
+    # 1024 bytes (e.g. KiB, MiB, GiB) and powers of 1000 bytes (e.g.
+    # KB, MB, GB).
+    # If you have scripts that depend on the old behaviour, set this to 0
+    # temporarily until you update them.
+    si_unit_consistency = 1
+
+    # Whether or not to communicate with the kernel device-mapper.
+    # Set to 0 if you want to use the tools to manipulate LVM metadata 
+    # without activating any logical volumes.
+    # If the device-mapper kernel driver is not present in your kernel
+    # setting this to 0 should suppress the error messages.
+    activation = 1
+
+    # If we can't communicate with device-mapper, should we try running 
+    # the LVM1 tools?
+    # This option only applies to 2.4 kernels and is provided to help you
+    # switch between device-mapper kernels and LVM1 kernels.
+    # The LVM1 tools need to be installed with .lvm1 suffices
+    # e.g. vgscan.lvm1 and they will stop working after you start using
+    # the new lvm2 on-disk metadata format.
+    # The default value is set when the tools are built.
+    # fallback_to_lvm1 = 0
+
+    # The default metadata format that commands should use - "lvm1" or "lvm2".
+    # The command line override is -M1 or -M2.
+    # Defaults to "lvm2".
+    # format = "lvm2"
+
+    # Location of proc filesystem
+    proc = "/proc"
+
+    # Type of locking to use. Defaults to local file-based locking (1).
+    # Turn locking off by setting to 0 (dangerous: risks metadata corruption
+    # if LVM2 commands get run concurrently).
+    # Type 2 uses the external shared library locking_library.
+    # Type 3 uses built-in clustered locking.
+    # Type 4 uses read-only locking which forbids any operations that might 
+    # change metadata.
+    locking_type = 1
+
+    # Set to 0 to fail when a lock request cannot be satisfied immediately.
+    wait_for_locks = 1
+
+    # If using external locking (type 2) and initialisation fails,
+    # with this set to 1 an attempt will be made to use the built-in
+    # clustered locking.
+    # If you are using a customised locking_library you should set this to 0.
+    fallback_to_clustered_locking = 1
+
+    # If an attempt to initialise type 2 or type 3 locking failed, perhaps
+    # because cluster components such as clvmd are not running, with this set
+    # to 1 an attempt will be made to use local file-based locking (type 1).
+    # If this succeeds, only commands against local volume groups will proceed.
+    # Volume Groups marked as clustered will be ignored.
+    fallback_to_local_locking = 1
+
+    # Local non-LV directory that holds file-based locks while commands are
+    # in progress.  A directory like /tmp that may get wiped on reboot is OK.
+    locking_dir = "@DEFAULT_LOCK_DIR@"
+
+    # Whenever there are competing read-only and read-write access requests for
+    # a volume group's metadata, instead of always granting the read-only
+    # requests immediately, delay them to allow the read-write requests to be
+    # serviced.  Without this setting, write access may be stalled by a high
+    # volume of read-only requests.
+    # NB. This option only affects locking_type = 1 viz. local file-based
+    # locking.
+    prioritise_write_locks = 1
+
+    # Other entries can go here to allow you to load shared libraries
+    # e.g. if support for LVM1 metadata was compiled as a shared library use
+    #   format_libraries = "liblvm2format1.so" 
+    # Full pathnames can be given.
+
+    # Search this directory first for shared libraries.
+    #   library_dir = "/lib"
+
+    # The external locking library to load if locking_type is set to 2.
+    #   locking_library = "liblvm2clusterlock.so"
+
+    # Treat any internal errors as fatal errors, aborting the process that
+    # encountered the internal error. Please only enable for debugging.
+    abort_on_internal_errors = 0
+
+    # If set to 1, no operations that change on-disk metadata will be permitted.
+    # Additionally, read-only commands that encounter metadata in need of repair
+    # will still be allowed to proceed exactly as if the repair had been 
+    # performed (except for the unchanged vg_seqno).
+    # Inappropriate use could mess up your system, so seek advice first!
+    metadata_read_only = 0
+}
+
+activation {
+    # Set to 0 to disable udev synchronisation (if compiled into the binaries).
+    # Processes will not wait for notification from udev.
+    # They will continue irrespective of any possible udev processing
+    # in the background.  You should only use this if udev is not running
+    # or has rules that ignore the devices LVM2 creates.
+    # The command line argument --nodevsync takes precedence over this setting.
+    # If set to 1 when udev is not running, and there are LVM2 processes
+    # waiting for udev, run 'dmsetup udevcomplete_all' manually to wake them up.
+    udev_sync = 1
+
+    # Set to 0 to disable the udev rules installed by LVM2 (if built with
+    # --enable-udev_rules). LVM2 will then manage the /dev nodes and symlinks
+    # for active logical volumes directly itself.
+    # N.B. Manual intervention may be required if this setting is changed
+    # while any logical volumes are active.
+    udev_rules = 1
+
+    # How to fill in missing stripes if activating an incomplete volume.
+    # Using "error" will make inaccessible parts of the device return
+    # I/O errors on access.  You can instead use a device path, in which 
+    # case, that device will be used to in place of missing stripes.
+    # But note that using anything other than "error" with mirrored 
+    # or snapshotted volumes is likely to result in data corruption.
+    missing_stripe_filler = "error"
+
+    # How much stack (in KB) to reserve for use while devices suspended
+    reserved_stack = 256
+
+    # How much memory (in KB) to reserve for use while devices suspended
+    reserved_memory = 8192
+
+    # Nice value used while devices suspended
+    process_priority = -18
+
+    # If volume_list is defined, each LV is only activated if there is a
+    # match against the list.
+    #   "vgname" and "vgname/lvname" are matched exactly.
+    #   "@tag" matches any tag set in the LV or VG.
+    #   "@*" matches if any tag defined on the host is also set in the LV or VG
+    #
+    # volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
+
+    # Size (in KB) of each copy operation when mirroring
+    mirror_region_size = 512
+
+    # Setting to use when there is no readahead value stored in the metadata.
+    #
+    # "none" - Disable readahead.
+    # "auto" - Use default value chosen by kernel.
+    readahead = "auto"
+
+    # 'mirror_image_fault_policy' and 'mirror_log_fault_policy' define
+    # how a device failure affecting a mirror is handled.
+    # A mirror is composed of mirror images (copies) and a log.
+    # A disk log ensures that a mirror does not need to be re-synced
+    # (all copies made the same) every time a machine reboots or crashes.
+    #
+    # In the event of a failure, the specified policy will be used to determine
+    # what happens. This applies to automatic repairs (when the mirror is being
+    # monitored by dmeventd) and to manual lvconvert --repair when
+    # --use-policies is given.
+    #
+    # "remove" - Simply remove the faulty device and run without it.  If
+    #            the log device fails, the mirror would convert to using
+    #            an in-memory log.  This means the mirror will not
+    #            remember its sync status across crashes/reboots and
+    #            the entire mirror will be re-synced.  If a
+    #            mirror image fails, the mirror will convert to a
+    #            non-mirrored device if there is only one remaining good
+    #            copy.
+    #
+    # "allocate" - Remove the faulty device and try to allocate space on
+    #            a new device to be a replacement for the failed device.
+    #            Using this policy for the log is fast and maintains the
+    #            ability to remember sync state through crashes/reboots.
+    #            Using this policy for a mirror device is slow, as it
+    #            requires the mirror to resynchronize the devices, but it
+    #            will preserve the mirror characteristic of the device.
+    #            This policy acts like "remove" if no suitable device and
+    #            space can be allocated for the replacement.
+    #
+    # "allocate_anywhere" - Not yet implemented. Useful to place the log device
+    #            temporarily on same physical volume as one of the mirror
+    #            images. This policy is not recommended for mirror devices
+    #            since it would break the redundant nature of the mirror. This
+    #            policy acts like "remove" if no suitable device and space can
+    #            be allocated for the replacement.
+
+    mirror_log_fault_policy = "allocate"
+    mirror_image_fault_policy = "remove"
+
+    # 'snapshot_autoextend_threshold' and 'snapshot_autoextend_percent' define
+    # how to handle automatic snapshot extension. The former defines when the
+    # snapshot should be extended: when its space usage exceeds this many
+    # percent. The latter defines how much extra space should be allocated for
+    # the snapshot, in percent of its current size.
+    #
+    # For example, if you set snapshot_autoextend_threshold to 70 and
+    # snapshot_autoextend_percent to 20, whenever a snapshot exceeds 70% usage,
+    # it will be extended by another 20%. For a 1G snapshot, using up 700M will
+    # trigger a resize to 1.2G. When the usage exceeds 840M, the snapshot will
+    # be extended to 1.44G, and so on.
+    #
+    # Setting snapshot_autoextend_threshold to 100 disables automatic
+    # extensions. The minimum value is 50 (A setting below 50 will be treated
+    # as 50).
+
+    snapshot_autoextend_threshold = 100
+    snapshot_autoextend_percent = 20
+
+    # While activating devices, I/O to devices being (re)configured is
+    # suspended, and as a precaution against deadlocks, LVM2 needs to pin
+    # any memory it is using so it is not paged out.  Groups of pages that
+    # are known not to be accessed during activation need not be pinned
+    # into memory.  Each string listed in this setting is compared against
+    # each line in /proc/self/maps, and the pages corresponding to any
+    # lines that match are not pinned.  On some systems locale-archive was
+    # found to make up over 80% of the memory used by the process.
+    # mlock_filter = [ "locale/locale-archive", "gconv/gconv-modules.cache" ]
+
+    # Set to 1 to revert to the default behaviour prior to version 2.02.62
+    # which used mlockall() to pin the whole process's memory while activating
+    # devices.
+    use_mlockall = 0
+
+    # Monitoring is enabled by default when activating logical volumes.
+    # Set to 0 to disable monitoring or use the --ignoremonitoring option.
+    monitoring = 1
+
+    # When pvmove or lvconvert must wait for the kernel to finish
+    # synchronising or merging data, they check and report progress
+    # at intervals of this number of seconds.  The default is 15 seconds.
+    # If this is set to 0 and there is only one thing to wait for, there
+    # are no progress reports, but the process is awoken immediately the
+    # operation is complete.
+    polling_interval = 15
+}
+
+
+####################
+# Advanced section #
+####################
+
+# Metadata settings
+#
+# metadata {
+    # Default number of copies of metadata to hold on each PV.  0, 1 or 2.
+    # You might want to override it from the command line with 0 
+    # when running pvcreate on new PVs which are to be added to large VGs.
+
+    # pvmetadatacopies = 1
+
+    # Default number of copies of metadata to maintain for each VG.
+    # If set to a non-zero value, LVM automatically chooses which of
+    # the available metadata areas to use to achieve the requested
+    # number of copies of the VG metadata.  If you set a value larger
+    # than the the total number of metadata areas available then
+    # metadata is stored in them all.
+    # The default value of 0 ("unmanaged") disables this automatic
+    # management and allows you to control which metadata areas
+    # are used at the individual PV level using 'pvchange
+    # --metadataignore y/n'.
+
+    # vgmetadatacopies = 0
+
+    # Approximate default size of on-disk metadata areas in sectors.
+    # You should increase this if you have large volume groups or
+    # you want to retain a large on-disk history of your metadata changes.
+
+    # pvmetadatasize = 255
+
+    # List of directories holding live copies of text format metadata.
+    # These directories must not be on logical volumes!
+    # It's possible to use LVM2 with a couple of directories here,
+    # preferably on different (non-LV) filesystems, and with no other 
+    # on-disk metadata (pvmetadatacopies = 0). Or this can be in
+    # addition to on-disk metadata areas.
+    # The feature was originally added to simplify testing and is not
+    # supported under low memory situations - the machine could lock up.
+    #
+    # Never edit any files in these directories by hand unless you
+    # you are absolutely sure you know what you are doing! Use
+    # the supplied toolset to make changes (e.g. vgcfgrestore).
+
+    # dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ]
+#}
+
+# Event daemon
+#
+dmeventd {
+    # mirror_library is the library used when monitoring a mirror device.
+    #
+    # "libdevmapper-event-lvm2mirror.so" attempts to recover from
+    # failures.  It removes failed devices from a volume group and
+    # reconfigures a mirror as necessary. If no mirror library is
+    # provided, mirrors are not monitored through dmeventd.
+
+    mirror_library = "libdevmapper-event-lvm2mirror.so"
+
+    # snapshot_library is the library used when monitoring a snapshot device.
+    #
+    # "libdevmapper-event-lvm2snapshot.so" monitors the filling of
+    # snapshots and emits a warning through syslog when the use of
+    # the snapshot exceeds 80%. The warning is repeated when 85%, 90% and
+    # 95% of the snapshot is filled.
+
+    snapshot_library = "libdevmapper-event-lvm2snapshot.so"
+
+    # Full path of the dmeventd binary.
+    #
+    # executable = "@DMEVENTD_PATH@"
+}
diff --git a/doc/example_cmdlib.c b/doc/example_cmdlib.c
new file mode 100644 (file)
index 0000000..4d1a49d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "lvm2cmd.h"
+#include <stdio.h>
+
+/* All output gets passed to this function line-by-line */
+void test_log_fn(int level, const char *file, int line,
+                int dm_errno, const char *format)
+{
+       /* Extract and process output here rather than printing it */
+
+       if (level != 4)
+               return;
+
+       printf("%s\n", format);
+       return;
+}
+
+int main(int argc, char **argv)
+{
+       void *handle;
+       int r;
+
+       lvm2_log_fn(test_log_fn);
+
+       handle = lvm2_init();
+
+       lvm2_log_level(handle, 1);
+       r = lvm2_run(handle, "vgs --noheadings vg1");
+
+       /* More commands here */
+
+       lvm2_exit(handle);
+
+       return r;
+}
+
diff --git a/doc/lvm_fault_handling.txt b/doc/lvm_fault_handling.txt
new file mode 100644 (file)
index 0000000..fa30c0c
--- /dev/null
@@ -0,0 +1,221 @@
+LVM device fault handling
+=========================
+
+Introduction
+------------
+This document is to serve as the definitive source for information
+regarding the policies and procedures surrounding device failures
+in LVM.  It codifies LVM's responses to device failures as well as
+the responsibilities of administrators.
+
+Device failures can be permanent or transient.  A permanent failure
+is one where a device becomes inaccessible and will never be
+revived.  A transient failure is a failure that can be recovered
+from (e.g. a power failure, intermittent network outage, block
+relocation, etc).  The policies for handling both types of failures
+is described herein.
+
+Available Operations During a Device Failure
+--------------------------------------------
+When there is a device failure, LVM behaves somewhat differently because
+only a subset of the available devices will be found for the particular
+volume group.  The number of operations available to the administrator
+is diminished.  It is not possible to create new logical volumes while
+PVs cannot be accessed, for example.  Operations that create, convert, or
+resize logical volumes are disallowed, such as:
+- lvcreate
+- lvresize
+- lvreduce
+- lvextend
+- lvconvert (unless '--repair' is used)
+Operations that activate, deactivate, remove, report, or repair logical
+volumes are allowed, such as:
+- lvremove
+- vgremove (will remove all LVs, but not the VG until consistent)
+- pvs
+- vgs
+- lvs
+- lvchange -a [yn]
+- vgchange -a [yn]
+Operations specific to the handling of failed devices are allowed and
+are as follows:
+
+- 'vgreduce --removemissing <VG>':  This action is designed to remove
+  the reference of a failed device from the LVM metadata stored on the
+  remaining devices.  If there are (portions of) logical volumes on the
+  failed devices, the ability of the operation to proceed will depend
+  on the type of logical volumes found.  If an image (i.e leg or side)
+  of a mirror is located on the device, that image/leg of the mirror
+  is eliminated along with the failed device.  The result of such a
+  mirror reduction could be a no-longer-redundant linear device.  If
+  a linear, stripe, or snapshot device is located on the failed device
+  the command will not proceed without a '--force' option.  The result
+  of using the '--force' option is the entire removal and complete
+  loss of the non-redundant logical volume.  Once this operation is
+  complete, the volume group will again have a complete and consistent
+  view of the devices it contains.  Thus, all operations will be
+  permitted - including creation, conversion, and resizing operations.
+
+- 'lvconvert --repair <VG/LV>':  This action is designed specifically
+  to operate on mirrored logical volumes.  It is used on logical volumes
+  individually and does not remove the faulty device from the volume
+  group.  If, for example, a failed device happened to contain the
+  images of four distinct mirrors, it would be necessary to run
+  'lvconvert --repair' on each of them.  The ultimate result is to leave
+  the faulty device in the volume group, but have no logical volumes
+  referencing it.  In addition to removing mirror images that reside
+  on failed devices, 'lvconvert --repair' can also replace the failed
+  device if there are spare devices available in the volume group.  The
+  user is prompted whether to simply remove the failed portions of the
+  mirror or to also allocate a replacement, if run from the command-line.
+  Optionally, the '--use-policies' flag can be specified which will
+  cause the operation not to prompt the user, but instead respect
+  the policies outlined in the LVM configuration file - usually,
+  /etc/lvm/lvm.conf.  Once this operation is complete, mirrored logical
+  volumes will be consistent and I/O will be allowed to continue.
+  However, the volume group will still be inconsistent -  due to the
+  refernced-but-missing device/PV - and operations will still be
+  restricted to the aformentioned actions until either the device is
+  restored or 'vgreduce --removemissing' is run.
+
+Device Revival (transient failures):
+------------------------------------
+During a device failure, the above section describes what limitations
+a user can expect.  However, if the device returns after a period of
+time, what to expect will depend on what has happened during the time
+period when the device was failed.  If no automated actions (described
+below) or user actions were necessary or performed, then no change in
+operations or logical volume layout will occur.  However, if an
+automated action or one of the aforementioned repair commands was
+manually run, the returning device will be perceived as having stale
+LVM metadata.  In this case, the user can expect to see a warning
+concerning inconsistent metadata.  The metadata on the returning
+device will be automatically replaced with the latest copy of the
+LVM metadata - restoring consistency.  Note, while most LVM commands
+will automatically update the metadata on a restored devices, the
+following possible exceptions exist:
+- pvs (when it does not read/update VG metadata)
+
+Automated Target Response to Failures:
+--------------------------------------
+The only LVM target type (i.e. "personality") that has an automated
+response to failures is a mirrored logical volume.  The other target
+types (linear, stripe, snapshot, etc) will simply propagate the failure.
+[A snapshot becomes invalid if its underlying device fails, but the
+origin will remain valid - presuming the origin device has not failed.]
+There are three types of errors that a mirror can suffer - read, write,
+and resynchronization errors.  Each is described in depth below.
+
+Mirror read failures:
+If a mirror is 'in-sync' (i.e. all images have been initialized and
+are identical), a read failure will only produce a warning.  Data is
+simply pulled from one of the other images and the fault is recorded.
+Sometimes - like in the case of bad block relocation - read errors can
+be recovered from by the storage hardware.  Therefore, it is up to the
+user to decide whether to reconfigure the mirror and remove the device
+that caused the error.  Managing the composition of a mirror is done with
+'lvconvert' and removing a device from a volume group can be done with
+'vgreduce'.
+
+If a mirror is not 'in-sync', a read failure will produce an I/O error.
+This error will propagate all the way up to the applications above the
+logical volume (e.g. the file system).  No automatic intervention will
+take place in this case either.  It is up to the user to decide what
+can be done/salvaged in this senario.  If the user is confident that the
+images of the mirror are the same (or they are willing to simply attempt
+to retreive whatever data they can), 'lvconvert' can be used to eliminate
+the failed image and proceed.
+
+Mirror resynchronization errors:
+A resynchronization error is one that occurs when trying to initialize
+all mirror images to be the same.  It can happen due to a failure to
+read the primary image (the image considered to have the 'good' data), or
+due to a failure to write the secondary images.  This type of failure
+only produces a warning, and it is up to the user to take action in this
+case.  If the error is transient, the user can simply reactivate the
+mirrored logical volume to make another attempt at resynchronization.
+If attempts to finish resynchronization fail, 'lvconvert' can be used to
+remove the faulty device from the mirror.
+
+TODO...
+Some sort of response to this type of error could be automated.
+Since this document is the definitive source for how to handle device
+failures, the process should be defined here.  If the process is defined
+but not implemented, it should be noted as such.  One idea might be to
+make a single attempt to suspend/resume the mirror in an attempt to
+redo the sync operation that failed.  On the other hand, if there is
+a permanent failure, it may simply be best to wait for the user or the
+automated response that is sure to follow from a write failure.
+...TODO
+
+Mirror write failures:
+When a write error occurs on a mirror constituent device, an attempt
+to handle the failure is automatically made.  This is done by calling
+'lvconvert --repair --use-policies'.  The policies implied by this
+command are set in the LVM configuration file.  They are:
+- mirror_log_fault_policy:  This defines what action should be taken
+  if the device containing the log fails.  The available options are
+  "remove" and "allocate".  Either of these options will cause the
+  faulty log device to be removed from the mirror.  The "allocate"
+  policy will attempt the further action of trying to replace the
+  failed disk log by using space that might be available in the
+  volume group.  If the allocation fails (or the "remove" policy
+  is specified), the mirror log will be maintained in memory.  Should
+  the machine be rebooted or the logical volume deactivated, a
+  complete resynchronization of the mirror will be necessary upon
+  the follow activation - such is the nature of a mirror with a 'core'
+  log.  The default policy for handling log failures is "allocate".
+  The service disruption incurred by replacing the failed log is
+  negligible, while the benefits of having persistent log is
+  pronounced.
+- mirror_image_fault_policy:  This defines what action should be taken
+  if a device containing an image fails.  Again, the available options
+  are "remove" and "allocate".  Both of these options will cause the
+  faulty image device to be removed - adjusting the logical volume
+  accordingly.  For example, if one image of a 2-way mirror fails, the
+  mirror will be converted to a linear device.  If one image of a
+  3-way mirror fails, the mirror will be converted to a 2-way mirror.
+  The "allocate" policy takes the further action of trying to replace
+  the failed image using space that is available in the volume group.
+  Replacing a failed mirror image will incure the cost of
+  resynchronizing - degrading the performance of the mirror.  The
+  default policy for handling an image failure is "remove".  This
+  allows the mirror to still function, but gives the administrator the
+  choice of when to incure the extra performance costs of replacing
+  the failed image.
+
+TODO...
+The appropriate time to take permanent corrective action on a mirror
+should be driven by policy.  There should be a directive that takes
+a time or percentage argument.  Something like the following:
+- mirror_fault_policy_WHEN = "10sec"/"10%"
+A time value would signal the amount of time to wait for transient
+failures to resolve themselves.  The percentage value would signal the
+amount a mirror could become out-of-sync before the faulty device is
+removed.
+
+A mirror cannot be used unless /some/ corrective action is taken,
+however.  One option is to replace the failed mirror image with an
+error target, forgo the use of 'handle_errors', and simply let the
+out-of-sync regions accumulate and be tracked by the log.  Mirrors
+that have more than 2 images would have to "stack" to perform the
+tracking, as each failed image would have to be associated with a
+log.  If the failure is transient, the device would replace the
+error target that was holding its spot and the log that was tracking
+the deltas would be used to quickly restore the portions that changed.
+
+One unresolved issue with the above scheme is how to know which
+regions of the mirror are out-of-sync when a problem occurs.  When
+a write failure occurs in the kernel, the log will contain those
+regions that are not in-sync.  If the log is a disk log, that log
+could continue to be used to track differences.  However, if the
+log was a core log - or if the log device failed at the same time
+as an image device - there would be no way to determine which
+regions are out-of-sync to begin with as we start to track the
+deltas for the failed image.  I don't have a solution for this
+problem other than to only be able to handle errors in this way
+if conditions are right.  These issues will have to be ironed out
+before proceeding.  This could be another case, where it is better
+to handle failures in the kernel by allowing the kernel to store
+updates in various metadata areas.
+...TODO
diff --git a/doc/pvmove_outline.txt b/doc/pvmove_outline.txt
new file mode 100644 (file)
index 0000000..8746b8f
--- /dev/null
@@ -0,0 +1,52 @@
+Let's say we have an LV, made up of three segments of different PV's,
+I've also added in the device major:minor as this will be useful
+later:
+
++-----------------------------+
+|  PV1     |   PV2   |   PV3  | 254:3
++----------+---------+--------+
+
+
+Now our hero decides to PV move PV2 to PV4:
+
+1. Suspend our LV (254:3), this starts queueing all io, and flushes
+   all pending io.  Once the suspend has completed we are free to change
+   the mapping table.
+
+2. Set up *another* (254:4) device with the mapping table of our LV.
+
+3. Load a new mapping table into (254:3) that has identity targets for
+   parts that aren't moving, and a mirror target for parts that are.
+
+4. Unsuspend (254:3)
+
+So now we have:
+                           destination of copy
+               +--------------------->--------------+
+               |                                    |
++-----------------------------+               + -----------+
+| Identity | mirror  | Ident. | 254:3         |    PV4     |
++----------+---------+--------+               +------------+
+     |         |         |
+     \/        \/        \/
++-----------------------------+
+|  PV1     |   PV2   |   PV3  | 254:4
++----------+---------+--------+
+
+Any writes to segment2 of the LV get intercepted by the mirror target
+who checks that that chunk has been copied to the new destination, if
+it hasn't it queues the initial copy and defers the current io until
+it has finished.  Then the current io is written to *both* PV2 and the
+PV4.
+
+5. When the copying has completed 254:3 is suspended/pending flushed.
+
+6. 254:4 is taken down
+
+7. metadata is updated on disk
+
+8. 254:3 has new mapping table loaded:
+
++-----------------------------+
+|  PV1     |   PV4   |   PV3  | 254:3
++----------+---------+--------+
diff --git a/doc/tagging.txt b/doc/tagging.txt
new file mode 100644 (file)
index 0000000..5a3f520
--- /dev/null
@@ -0,0 +1,165 @@
+Tagging aims
+============
+  1) Ability to attach an unordered list of tags to LVM metadata objects.
+  2) Ability to add or remove tags easily.
+  3) Ability to select LVM objects for processing according to presence/absence
+     of specific tags.
+  4) Ability to control through the config file which VGs/LVs are activated 
+     on different machines using names or tags.
+  5) Ability to overlay settings from different config files e.g. override
+     some settings in a global config file locally.
+
+Clarifications
+==============
+  1) Tag character set: A-Za-z0-9_+.- 
+     Can't start with hyphen & max length is 128 (NAME_LEN).
+  2) LVM object types that can be tagged:
+       VG, LV, LV segment
+       PV - tags are stored in VG metadata so disappear when PV becomes orphaned
+     Snapshots can't be tagged, but their origin may be.
+  3) A tag can be used in place of any command line LVM object reference that
+     accepts (a) a list of objects; or (b) a single object as long as the
+     tag expands to a single object.  This is not supported everywhere yet.
+     Duplicate arguments in a list after argument expansion may get removed 
+     retaining the first copy of each argument.
+  4) Wherever there may be ambiguity of argument type, a tag must be prefixed 
+     by '@'; elsewhere an '@' prefix is optional.
+  5) LVM1 objects cannot be tagged, as the disk format doesn't support it.
+  6) Tags can be added or removed with --addtag or --deltag.
+
+Config file Extensions
+======================
+  To define host tags in config file:
+
+  tags {
+       # Set a tag with the hostname
+       hosttags = 1
+
+       tag1 { }
+
+       tag2 {
+               # If no exact match, tag is not set.
+               host_list = [ "hostname", "dbase" ]
+       }
+  }
+
+Activation config file example
+==============================
+  activation {
+      volume_list = [ "vg1/lvol0", "@database" ]
+  }
+
+  Matches against vgname, vgname/lvname or @tag set in *metadata*.
+  @* matches exactly against *any* tag set on the host.
+  The VG or LV only gets activated if a metadata tag matches.
+  The default if there is no match is not to activate.
+  If volume_list is not present and any tags are defined on the host 
+  then it only activates if a host tag matches a metadata tag.
+  If volume_list is not present and no tags are defined on the host 
+  then it does activate.
+
+Multiple config files
+=====================
+  (a) lvm.conf
+  (b) lvm_<host_tag>.conf
+
+  At startup, load lvm.conf.
+  Process tag settings.
+  If any host tags were defined, load lvm_tag.conf for each tag, if present.
+
+  When searching for a specific config file entry, search order is (b)
+  then (a), stopping at the first match.  
+  Within (b) use reverse order tags got set, so file for last tag set is
+  searched first.
+  New tags set in (b) *do* trigger additional config file loads. 
+
+Usage Examples
+==============
+  1) Simple activation control via metadata with static config files
+
+  lvm.conf:  (Identical on every machine - global settings)
+    tags {
+      hostname_tags = 1
+    }
+
+  From any machine in the cluster, add db1 to the list of machines that
+  activate vg1/lvol2:
+
+  lvchange --tag @db1 vg1/lvol2
+  (followed by lvchange -ay to actually activate it)
+
+
+  2) Multiple hosts.  
+
+    Activate vg1 only on the database hosts, db1 and db2.
+    Activate vg2 only on the fileserver host fs1.
+    Activate nothing initially on the fileserver backup host fsb1, but be
+    prepared for it to take over from fs1.
+
+  Option (i) - centralised admin, static configuration replicated between hosts  
+    # Add @database tag to vg1's metadata
+    vgchange --tag @database vg1
+
+    # Add @fileserver tag to vg2's metadata
+    vgchange --tag @fileserver vg2
+
+    lvm.conf:  (Identical on every machine)
+      tags {
+        database {
+          host_list = [ "db1", "db2" ]
+        }
+        fileserver {
+         host_list = [ "fs1" ]
+        }
+        fileserverbackup {
+          host_list = [ "fsb1" ]
+        }
+      }
+
+      activation {
+        # Only activate if host has a tag that matches a metadata tag
+        volume_list = [ "@*" ]
+      }
+  
+  In the event of the fileserver host going down, vg2 can be brought up
+  on fsb1 by running *on any node* 'vgchange --tag @fileserverbackup vg2'
+  followed by 'vgchange -ay vg2'
+  
+  
+  Option (ii) - localised admin & configuation
+  (i.e. each host holds *locally* which classes of volumes to activate)
+    # Add @database tag to vg1's metadata
+    vgchange --tag @database vg1
+  
+    # Add @fileserver tag to vg2's metadata
+    vgchange --tag @fileserver vg2
+  
+    lvm.conf:  (Identical on every machine - global settings)
+      tags {
+        hosttags = 1
+      }
+  
+    lvm_db1.conf: (only needs to be on db1 - could be symlink to lvm_db.conf)
+      activation {
+        volume_list = [ "@database" ]
+      }
+  
+    lvm_db2.conf: (only needs to be on db2 - could be symlink to lvm_db.conf)
+      activation {
+        volume_list = [ "@database" ]
+      }
+  
+    lvm_fs1.conf: (only needs to be on fs1 - could be symlink to lvm_fs.conf)
+      activation {
+        volume_list = [ "@fileserver" ]
+      }
+  
+    If fileserver goes down, to bring a spare machine fsb1 in as fileserver,
+    create lvm_fsb1.conf on fsb1 (or symlink to lvm_fs.conf):
+
+      activation {
+        volume_list = [ "@fileserver" ]
+      }
+
+    and run 'vgchange -ay vg2' or 'vgchange -ay @fileserver'
+
diff --git a/doc/testing.txt b/doc/testing.txt
new file mode 100644 (file)
index 0000000..214435a
--- /dev/null
@@ -0,0 +1,41 @@
+Here's how I test new LVM2 builds without interfering with the stable
+LVM2 that is running the LV's on my development box.
+
+1) Create a set of loopback devices.
+
+2) Create a new directory to contain the LVM2 configuration files for
+   this setup.  (I use /etc/lvm_loops)
+
+3) Write a suitable lvm.conf file, this goes in the directory you just
+   created.  eg, my /etc/lvm_loops/lvm.conf looks like:
+
+   log {
+        file="/tmp/lvm2_loop.log"
+        level=9
+        verbose=0
+        overwrite=1
+   }
+
+   devices {
+        scan = "/dev"
+        filter = ["a/loop/", "r/.*/"]
+   }
+
+
+   The important thing to note is the devices section which makes sure
+   that only the loopback devices are considered for LVM2 operations.
+
+4) When you want to use this test setup just set the environment
+   variable LVM_SYSTEM_DIR to point to your config directory
+   (/etc/lvm_loops in my case).
+
+5) It's a good idea to do a vgscan to initialise the filters:
+
+   export LVM_SYSTEM_DIR=/etc/lvm_loops
+   ./lvm vgscan
+
+   where ./lvm is the new build of LVM2 that I'm trying out.
+
+7) Test away.  Make sure that you are explicit about which lvm
+   executable you want to execute (eg, ./lvm if you are in
+   LVM2/tools).
diff --git a/include/.symlinks.in b/include/.symlinks.in
new file mode 100644 (file)
index 0000000..c16e107
--- /dev/null
@@ -0,0 +1,67 @@
+@top_srcdir@/daemons/clvmd/clvm.h
+@top_srcdir@/daemons/dmeventd/libdevmapper-event.h
+@top_srcdir@/liblvm/lvm2app.h
+@top_srcdir@/lib/activate/activate.h
+@top_srcdir@/lib/activate/targets.h
+@top_srcdir@/lib/cache/lvmcache.h
+@top_srcdir@/lib/commands/errors.h
+@top_srcdir@/lib/commands/toolcontext.h
+@top_srcdir@/lib/config/config.h
+@top_srcdir@/lib/config/defaults.h
+@top_srcdir@/lib/datastruct/btree.h
+@top_srcdir@/lib/datastruct/lvm-types.h
+@top_srcdir@/lib/datastruct/str_list.h
+@top_srcdir@/lib/device/dev-cache.h
+@top_srcdir@/lib/device/device.h
+@top_srcdir@/lib/display/display.h
+@top_srcdir@/lib/filters/filter-composite.h
+@top_srcdir@/lib/filters/filter-md.h
+@top_srcdir@/lib/filters/filter-persistent.h
+@top_srcdir@/lib/filters/filter-regex.h
+@top_srcdir@/lib/filters/filter-sysfs.h
+@top_srcdir@/lib/filters/filter.h
+@top_srcdir@/lib/format1/format1.h
+@top_srcdir@/lib/format_pool/format_pool.h
+@top_srcdir@/lib/format_text/archiver.h
+@top_srcdir@/lib/format_text/format-text.h
+@top_srcdir@/lib/format_text/text_export.h
+@top_srcdir@/lib/format_text/text_import.h
+@top_srcdir@/lib/label/label.h
+@top_srcdir@/lib/locking/locking.h
+@top_srcdir@/lib/log/log.h
+@top_srcdir@/lib/log/lvm-logging.h
+@top_srcdir@/lib/metadata/lv.h
+@top_srcdir@/lib/metadata/lv_alloc.h
+@top_srcdir@/lib/metadata/metadata.h
+@top_srcdir@/lib/metadata/metadata-exported.h
+@top_srcdir@/lib/metadata/pv.h
+@top_srcdir@/lib/metadata/pv_alloc.h
+@top_srcdir@/lib/metadata/segtype.h
+@top_srcdir@/lib/metadata/vg.h
+@top_srcdir@/lib/mm/memlock.h
+@top_srcdir@/lib/mm/xlate.h
+@top_builddir@/lib/misc/configure.h
+@top_srcdir@/lib/misc/crc.h
+@top_srcdir@/lib/misc/intl.h
+@top_srcdir@/lib/misc/util.h
+@top_srcdir@/lib/misc/last-path-component.h
+@top_srcdir@/lib/misc/lib.h
+@top_srcdir@/lib/misc/lvm-exec.h
+@top_srcdir@/lib/misc/lvm-file.h
+@top_srcdir@/lib/misc/lvm-globals.h
+@top_srcdir@/lib/misc/lvm-string.h
+@top_builddir@/lib/misc/lvm-version.h
+@top_srcdir@/lib/misc/lvm-wrappers.h
+@top_srcdir@/lib/misc/lvm-percent.h
+@top_srcdir@/lib/misc/sharedlib.h
+@top_srcdir@/lib/report/properties.h
+@top_srcdir@/lib/report/report.h
+@top_srcdir@/lib/uuid/uuid.h
+@top_srcdir@/libdm/libdevmapper.h
+@top_srcdir@/libdm/misc/dm-ioctl.h
+@top_srcdir@/libdm/misc/dm-logging.h
+@top_srcdir@/libdm/misc/dm-log-userspace.h
+@top_srcdir@/libdm/misc/dmlib.h
+@top_srcdir@/libdm/misc/kdev_t.h
+@top_srcdir@/po/pogen.h
+@top_srcdir@/tools/lvm2cmd.h
diff --git a/include/Makefile.in b/include/Makefile.in
new file mode 100644 (file)
index 0000000..3daaab1
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+include $(top_builddir)/make.tmpl
+
+all: .symlinks_created
+
+.symlinks_created: .symlinks 
+       find . -maxdepth 1 -type l -exec $(RM) \{\} \;
+       for i in `cat $<`; do $(LN_S) $$i ; done
+       touch $@
+
+pofile: all
+
+device-mapper: all
+
+cflow: all
+
+DISTCLEAN_TARGETS += $(shell find . -maxdepth 1 -type l)
+DISTCLEAN_TARGETS += .include_symlinks .symlinks_created .symlinks
diff --git a/lib/Makefile.in b/lib/Makefile.in
new file mode 100644 (file)
index 0000000..010e888
--- /dev/null
@@ -0,0 +1,184 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+ifeq ("@LVM1@", "shared")
+  SUBDIRS = format1
+endif
+
+ifeq ("@POOL@", "shared")
+  SUBDIRS += format_pool
+endif
+
+ifeq ("@SNAPSHOTS@", "shared")
+  SUBDIRS += snapshot
+endif
+
+ifeq ("@MIRRORS@", "shared")
+  SUBDIRS += mirror
+endif
+
+ifeq ("@REPLICATORS@", "shared")
+  SUBDIRS += replicator
+endif
+
+SOURCES =\
+       activate/activate.c \
+       cache/lvmcache.c \
+       commands/toolcontext.c \
+       config/config.c \
+       datastruct/btree.c \
+       datastruct/str_list.c \
+       device/dev-cache.c \
+       device/dev-io.c \
+       device/dev-md.c \
+       device/dev-swap.c \
+       device/dev-luks.c \
+       device/device.c \
+       display/display.c \
+       error/errseg.c \
+       unknown/unknown.c \
+       filters/filter-composite.c \
+       filters/filter-persistent.c \
+       filters/filter-regex.c \
+       filters/filter-sysfs.c \
+       filters/filter-md.c \
+       filters/filter.c \
+       format_text/archive.c \
+       format_text/archiver.c \
+       format_text/export.c \
+       format_text/flags.c \
+       format_text/format-text.c \
+       format_text/import.c \
+       format_text/import_vsn1.c \
+       format_text/tags.c \
+       format_text/text_label.c \
+       freeseg/freeseg.c \
+       label/label.c \
+       locking/file_locking.c \
+       locking/locking.c \
+       locking/no_locking.c \
+       log/log.c \
+       metadata/lv.c \
+       metadata/lv_manip.c \
+       metadata/merge.c \
+       metadata/metadata.c \
+       metadata/mirror.c \
+       metadata/pv.c \
+       metadata/pv_manip.c \
+       metadata/pv_map.c \
+       metadata/replicator_manip.c \
+       metadata/segtype.c \
+       metadata/snapshot_manip.c \
+       metadata/vg.c \
+       misc/crc.c \
+       misc/lvm-exec.c \
+       misc/lvm-file.c \
+       misc/lvm-globals.c \
+       misc/lvm-string.c \
+       misc/lvm-wrappers.c \
+       misc/lvm-percent.c \
+       misc/util.c \
+       mm/memlock.c \
+       report/properties.c \
+       report/report.c \
+       striped/striped.c \
+       uuid/uuid.c \
+       zero/zero.c
+
+ifeq ("@HAVE_REALTIME@", "yes")
+  SOURCES +=\
+       misc/timestamp.c
+endif
+
+ifeq ("@LVM1@", "internal")
+  SOURCES +=\
+       format1/disk-rep.c \
+       format1/format1.c \
+       format1/import-export.c \
+       format1/import-extents.c \
+       format1/layout.c \
+       format1/lvm1-label.c \
+       format1/vg_number.c
+endif
+
+ifeq ("@POOL@", "internal")
+  SOURCES +=\
+       format_pool/disk_rep.c \
+       format_pool/format_pool.c \
+       format_pool/import_export.c \
+       format_pool/pool_label.c
+endif
+
+ifeq ("@CLUSTER@", "internal")
+  SOURCES += locking/cluster_locking.c
+endif
+
+ifeq ("@CLUSTER@", "shared")
+  SUBDIRS += locking
+endif
+
+ifeq ("@SNAPSHOTS@", "internal")
+  SOURCES += snapshot/snapshot.c
+endif
+
+ifeq ("@MIRRORS@", "internal")
+  SOURCES += mirror/mirrored.c
+endif
+
+ifeq ("@REPLICATORS@", "internal")
+  SOURCES += replicator/replicator.c
+endif
+
+ifeq ("@DEVMAPPER@", "yes")
+  SOURCES +=\
+       activate/dev_manager.c \
+       activate/fs.c
+endif
+
+ifeq ("@HAVE_LIBDL@", "yes")
+  SOURCES +=\
+       locking/external_locking.c \
+       misc/sharedlib.c
+endif
+
+ifeq ("@DMEVENTD@", "yes")
+  CLDFLAGS += -L$(top_builddir)/daemons/dmeventd
+  LIBS += -ldevmapper-event
+endif
+
+LIB_NAME = liblvm-internal
+LIB_STATIC = $(LIB_NAME).a
+
+ifeq ($(MAKECMDGOALS),distclean)
+  SUBDIRS =\
+       format1 \
+       format_pool \
+       snapshot \
+       mirror \
+       replicator \
+       locking
+endif
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+
+include $(top_builddir)/make.tmpl
+
+$(SUBDIRS): $(LIB_STATIC)
+
+DISTCLEAN_TARGETS += misc/configure.h misc/lvm-version.h
diff --git a/lib/activate/activate.c b/lib/activate/activate.c
new file mode 100644 (file)
index 0000000..174f09e
--- /dev/null
@@ -0,0 +1,1385 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "activate.h"
+#include "memlock.h"
+#include "display.h"
+#include "fs.h"
+#include "lvm-exec.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "toolcontext.h"
+#include "dev_manager.h"
+#include "str_list.h"
+#include "config.h"
+#include "filter.h"
+#include "segtype.h"
+#include "sharedlib.h"
+
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args)
+
+int lvm1_present(struct cmd_context *cmd)
+{
+       char path[PATH_MAX];
+
+       if (dm_snprintf(path, sizeof(path), "%s/lvm/global", cmd->proc_dir)
+           < 0) {
+               log_error("LVM1 proc global snprintf failed");
+               return 0;
+       }
+
+       if (path_exists(path))
+               return 1;
+       else
+               return 0;
+}
+
+int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
+                        struct dm_list *modules)
+{
+       unsigned int s;
+       struct lv_segment *seg2, *snap_seg;
+       struct dm_list *snh;
+
+       if (seg->segtype->ops->modules_needed &&
+           !seg->segtype->ops->modules_needed(mem, seg, modules)) {
+               log_error("module string allocation failed");
+               return 0;
+       }
+
+       if (lv_is_origin(seg->lv))
+               dm_list_iterate(snh, &seg->lv->snapshot_segs)
+                       if (!list_lv_modules(mem,
+                                            dm_list_struct_base(snh,
+                                                             struct lv_segment,
+                                                             origin_list)->cow,
+                                            modules))
+                               return_0;
+
+       if (lv_is_cow(seg->lv)) {
+               snap_seg = find_cow(seg->lv);
+               if (snap_seg->segtype->ops->modules_needed &&
+                   !snap_seg->segtype->ops->modules_needed(mem, snap_seg,
+                                                           modules)) {
+                       log_error("snap_seg module string allocation failed");
+                       return 0;
+               }
+       }
+
+       for (s = 0; s < seg->area_count; s++) {
+               switch (seg_type(seg, s)) {
+               case AREA_LV:
+                       seg2 = find_seg_by_le(seg_lv(seg, s), seg_le(seg, s));
+                       if (seg2 && !list_segment_modules(mem, seg2, modules))
+                               return_0;
+                       break;
+               case AREA_PV:
+               case AREA_UNASSIGNED:
+                       ;
+               }
+       }
+
+       return 1;
+}
+
+int list_lv_modules(struct dm_pool *mem, const struct logical_volume *lv,
+                   struct dm_list *modules)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv->segments)
+               if (!list_segment_modules(mem, seg, modules))
+                       return_0;
+
+       return 1;
+}
+
+#ifndef DEVMAPPER_SUPPORT
+void set_activation(int act)
+{
+       static int warned = 0;
+
+       if (warned || !act)
+               return;
+
+       log_error("Compiled without libdevmapper support. "
+                 "Can't enable activation.");
+
+       warned = 1;
+}
+int activation(void)
+{
+       return 0;
+}
+int library_version(char *version, size_t size)
+{
+       return 0;
+}
+int driver_version(char *version, size_t size)
+{
+       return 0;
+}
+int target_version(const char *target_name, uint32_t *maj,
+                  uint32_t *min, uint32_t *patchlevel)
+{
+       return 0;
+}
+int target_present(struct cmd_context *cmd, const char *target_name,
+                  int use_modprobe)
+{
+       return 0;
+}
+int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, unsigned origin_only,
+           struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+       return 0;
+}
+int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s,
+                   unsigned origin_only,
+                   struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+       return 0;
+}
+int lv_snapshot_percent(const struct logical_volume *lv, percent_t *percent)
+{
+       return 0;
+}
+int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
+                     int wait, percent_t *percent, uint32_t *event_nr)
+{
+       return 0;
+}
+int lvs_in_vg_activated(struct volume_group *vg)
+{
+       return 0;
+}
+int lvs_in_vg_opened(struct volume_group *vg)
+{
+       return 0;
+}
+/******
+int lv_suspend(struct cmd_context *cmd, const char *lvid_s)
+{
+       return 1;
+}
+*******/
+int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s)
+{
+       return 1;
+}
+int lv_resume(struct cmd_context *cmd, const char *lvid_s)
+{
+       return 1;
+}
+int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s)
+{
+       return 1;
+}
+int lv_deactivate(struct cmd_context *cmd, const char *lvid_s)
+{
+       return 1;
+}
+int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
+                        int *activate_lv)
+{
+       return 1;
+}
+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+       return 1;
+}
+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+       return 1;
+}
+
+int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
+{
+       return 1;
+}
+
+int pv_uses_vg(struct physical_volume *pv,
+              struct volume_group *vg)
+{
+       return 0;
+}
+
+void activation_release(void)
+{
+       return;
+}
+
+void activation_exit(void)
+{
+       return;
+}
+
+#else                          /* DEVMAPPER_SUPPORT */
+
+static int _activation = 1;
+
+void set_activation(int act)
+{
+       if (act == _activation)
+               return;
+
+       _activation = act;
+       if (_activation)
+               log_verbose("Activation enabled. Device-mapper kernel "
+                           "driver will be used.");
+       else
+               log_warn("WARNING: Activation disabled. No device-mapper "
+                         "interaction will be attempted.");
+}
+
+int activation(void)
+{
+       return _activation;
+}
+
+static int _passes_activation_filter(struct cmd_context *cmd,
+                                    struct logical_volume *lv)
+{
+       const struct config_node *cn;
+       const struct config_value *cv;
+       const char *str;
+       char path[PATH_MAX];
+
+       if (!(cn = find_config_tree_node(cmd, "activation/volume_list"))) {
+               log_verbose("activation/volume_list configuration setting "
+                           "not defined, checking only host tags for %s/%s",
+                           lv->vg->name, lv->name);
+
+               /* If no host tags defined, activate */
+               if (dm_list_empty(&cmd->tags))
+                       return 1;
+
+               /* If any host tag matches any LV or VG tag, activate */
+               if (str_list_match_list(&cmd->tags, &lv->tags, NULL) ||
+                   str_list_match_list(&cmd->tags, &lv->vg->tags, NULL))
+                       return 1;
+
+               log_verbose("No host tag matches %s/%s",
+                           lv->vg->name, lv->name);
+
+               /* Don't activate */
+               return 0;
+       }
+       else
+               log_verbose("activation/volume_list configuration setting "
+                           "defined, checking the list to match %s/%s",
+                           lv->vg->name, lv->name);
+
+       for (cv = cn->v; cv; cv = cv->next) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Ignoring invalid string in config file "
+                                 "activation/volume_list");
+                       continue;
+               }
+               str = cv->v.str;
+               if (!*str) {
+                       log_error("Ignoring empty string in config file "
+                                 "activation/volume_list");
+                       continue;
+               }
+
+
+               /* Tag? */
+               if (*str == '@') {
+                       str++;
+                       if (!*str) {
+                               log_error("Ignoring empty tag in config file "
+                                         "activation/volume_list");
+                               continue;
+                       }
+                       /* If any host tag matches any LV or VG tag, activate */
+                       if (!strcmp(str, "*")) {
+                               if (str_list_match_list(&cmd->tags, &lv->tags, NULL)
+                                   || str_list_match_list(&cmd->tags,
+                                                          &lv->vg->tags, NULL))
+                                           return 1;
+                               else
+                                       continue;
+                       }
+                       /* If supplied tag matches LV or VG tag, activate */
+                       if (str_list_match_item(&lv->tags, str) ||
+                           str_list_match_item(&lv->vg->tags, str))
+                               return 1;
+                       else
+                               continue;
+               }
+               if (!strchr(str, '/')) {
+                       /* vgname supplied */
+                       if (!strcmp(str, lv->vg->name))
+                               return 1;
+                       else
+                               continue;
+               }
+               /* vgname/lvname */
+               if (dm_snprintf(path, sizeof(path), "%s/%s", lv->vg->name,
+                                lv->name) < 0) {
+                       log_error("dm_snprintf error from %s/%s", lv->vg->name,
+                                 lv->name);
+                       continue;
+               }
+               if (!strcmp(path, str))
+                       return 1;
+       }
+
+       log_verbose("No item supplied in activation/volume_list configuration "
+                   "setting matches %s/%s", lv->vg->name, lv->name);
+
+       return 0;
+}
+
+int library_version(char *version, size_t size)
+{
+       if (!activation())
+               return 0;
+
+       return dm_get_library_version(version, size);
+}
+
+int driver_version(char *version, size_t size)
+{
+       if (!activation())
+               return 0;
+
+       log_very_verbose("Getting driver version");
+
+       return dm_driver_version(version, size);
+}
+
+int target_version(const char *target_name, uint32_t *maj,
+                  uint32_t *min, uint32_t *patchlevel)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_versions *target, *last_target;
+
+       log_very_verbose("Getting target version for %s", target_name);
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+               return_0;
+
+       if (!dm_task_run(dmt)) {
+               log_debug("Failed to get %s target version", target_name);
+               /* Assume this was because LIST_VERSIONS isn't supported */
+               return 1;
+       }
+
+       target = dm_task_get_versions(dmt);
+
+       do {
+               last_target = target;
+
+               if (!strcmp(target_name, target->name)) {
+                       r = 1;
+                       *maj = target->version[0];
+                       *min = target->version[1];
+                       *patchlevel = target->version[2];
+                       goto out;
+               }
+
+               target = (struct dm_versions *)((char *) target + target->next);
+       } while (last_target != target);
+
+      out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+int module_present(struct cmd_context *cmd, const char *target_name)
+{
+       int ret = 0;
+#ifdef MODPROBE_CMD
+       char module[128];
+       const char *argv[3];
+
+       if (dm_snprintf(module, sizeof(module), "dm-%s", target_name) < 0) {
+               log_error("module_present module name too long: %s",
+                         target_name);
+               return 0;
+       }
+
+       argv[0] = MODPROBE_CMD;
+       argv[1] = module;
+       argv[2] = NULL;
+
+       ret = exec_cmd(cmd, argv, NULL);
+#endif
+       return ret;
+}
+
+int target_present(struct cmd_context *cmd, const char *target_name,
+                  int use_modprobe)
+{
+       uint32_t maj, min, patchlevel;
+
+       if (!activation())
+               return 0;
+
+#ifdef MODPROBE_CMD
+       if (use_modprobe) {
+               if (target_version(target_name, &maj, &min, &patchlevel))
+                       return 1;
+
+               if (!module_present(cmd, target_name))
+                       return_0;
+       }
+#endif
+
+       return target_version(target_name, &maj, &min, &patchlevel);
+}
+
+/*
+ * Returns 1 if info structure populated, else 0 on failure.
+ */
+int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, unsigned origin_only,
+           struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+       struct dm_info dminfo;
+
+       if (!activation())
+               return 0;
+
+       if (!dev_manager_info(lv->vg->cmd->mem, lv, origin_only ? "real" : NULL, with_open_count,
+                             with_read_ahead, &dminfo, &info->read_ahead))
+               return_0;
+
+       info->exists = dminfo.exists;
+       info->suspended = dminfo.suspended;
+       info->open_count = dminfo.open_count;
+       info->major = dminfo.major;
+       info->minor = dminfo.minor;
+       info->read_only = dminfo.read_only;
+       info->live_table = dminfo.live_table;
+       info->inactive_table = dminfo.inactive_table;
+
+       return 1;
+}
+
+int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s,
+                   unsigned origin_only,
+                   struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+       int r;
+       struct logical_volume *lv;
+
+       if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+               return 0;
+
+       if (!lv_is_origin(lv))
+               origin_only = 0;
+
+       r = lv_info(cmd, lv, origin_only, info, with_open_count, with_read_ahead);
+       free_vg(lv->vg);
+
+       return r;
+}
+
+/*
+ * Returns 1 if percent set, else 0 on failure.
+ */
+int lv_check_transient(struct logical_volume *lv)
+{
+       int r;
+       struct dev_manager *dm;
+
+       if (!activation())
+               return 0;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_transient(dm, lv)))
+               stack;
+
+       dev_manager_destroy(dm);
+
+       return r;
+}
+
+/*
+ * Returns 1 if percent set, else 0 on failure.
+ */
+int lv_snapshot_percent(const struct logical_volume *lv, percent_t *percent)
+{
+       int r;
+       struct dev_manager *dm;
+
+       if (!activation())
+               return 0;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_snapshot_percent(dm, lv, percent)))
+               stack;
+
+       dev_manager_destroy(dm);
+
+       return r;
+}
+
+/* FIXME Merge with snapshot_percent */
+int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
+                     int wait, percent_t *percent, uint32_t *event_nr)
+{
+       int r;
+       struct dev_manager *dm;
+       struct lvinfo info;
+
+       /* If mirrored LV is temporarily shrinked to 1 area (= linear),
+        * it should be considered in-sync. */
+       if (dm_list_size(&lv->segments) == 1 && first_seg(lv)->area_count == 1) {
+               *percent = PERCENT_100;
+               return 1;
+       }
+
+       if (!activation())
+               return 0;
+
+       if (!lv_info(cmd, lv, 0, &info, 0, 0))
+               return_0;
+
+       if (!info.exists)
+               return 0;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_mirror_percent(dm, lv, wait, percent, event_nr)))
+               stack;
+
+       dev_manager_destroy(dm);
+
+       return r;
+}
+
+static int _lv_active(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       struct lvinfo info;
+
+       if (!lv_info(cmd, lv, 0, &info, 0, 0)) {
+               stack;
+               return -1;
+       }
+
+       return info.exists;
+}
+
+static int _lv_open_count(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       struct lvinfo info;
+
+       if (!lv_info(cmd, lv, 0, &info, 1, 0)) {
+               stack;
+               return -1;
+       }
+
+       return info.open_count;
+}
+
+static int _lv_activate_lv(struct logical_volume *lv, unsigned origin_only)
+{
+       int r;
+       struct dev_manager *dm;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_activate(dm, lv, origin_only)))
+               stack;
+
+       dev_manager_destroy(dm);
+       return r;
+}
+
+static int _lv_preload(struct logical_volume *lv, unsigned origin_only, int *flush_required)
+{
+       int r;
+       struct dev_manager *dm;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_preload(dm, lv, origin_only, flush_required)))
+               stack;
+
+       dev_manager_destroy(dm);
+       return r;
+}
+
+static int _lv_deactivate(struct logical_volume *lv)
+{
+       int r;
+       struct dev_manager *dm;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_deactivate(dm, lv)))
+               stack;
+
+       dev_manager_destroy(dm);
+       return r;
+}
+
+static int _lv_suspend_lv(struct logical_volume *lv, unsigned origin_only, int lockfs, int flush_required)
+{
+       int r;
+       struct dev_manager *dm;
+
+       if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+               return_0;
+
+       if (!(r = dev_manager_suspend(dm, lv, origin_only, lockfs, flush_required)))
+               stack;
+
+       dev_manager_destroy(dm);
+       return r;
+}
+
+/*
+ * These two functions return the number of visible LVs in the state,
+ * or -1 on error.
+ */
+int lvs_in_vg_activated(struct volume_group *vg)
+{
+       struct lv_list *lvl;
+       int count = 0;
+
+       if (!activation())
+               return 0;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (lv_is_visible(lvl->lv))
+                       count += (_lv_active(vg->cmd, lvl->lv) == 1);
+       }
+
+       return count;
+}
+
+int lvs_in_vg_opened(const struct volume_group *vg)
+{
+       const struct lv_list *lvl;
+       int count = 0;
+
+       if (!activation())
+               return 0;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (lv_is_visible(lvl->lv))
+                       count += (_lv_open_count(vg->cmd, lvl->lv) > 0);
+       }
+
+       return count;
+}
+
+/*
+ * Determine whether an LV is active locally or in a cluster.
+ * Assumes vg lock held.
+ * Returns:
+ * 0 - not active locally or on any node in cluster
+ * 1 - active either locally or some node in the cluster
+ */
+int lv_is_active(struct logical_volume *lv)
+{
+       int ret;
+
+       if (_lv_active(lv->vg->cmd, lv))
+               return 1;
+
+       if (!vg_is_clustered(lv->vg))
+               return 0;
+
+       if ((ret = remote_lock_held(lv->lvid.s)) >= 0)
+               return ret;
+
+       /*
+        * Old compatibility code if locking doesn't support lock query
+        * FIXME: check status to not deactivate already activate device
+        */
+       if (activate_lv_excl(lv->vg->cmd, lv)) {
+               if (!deactivate_lv(lv->vg->cmd, lv))
+                       stack;
+               return 0;
+       }
+
+       /*
+        * Exclusive local activation failed so assume it is active elsewhere.
+        */
+       return 1;
+}
+
+#ifdef DMEVENTD
+static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd, const char *dmuuid, const char *dso,
+                                                        const int timeout, enum dm_event_mask mask)
+{
+       struct dm_event_handler *dmevh;
+
+       if (!(dmevh = dm_event_handler_create()))
+               return_NULL;
+
+       if (dm_event_handler_set_dmeventd_path(dmevh, find_config_tree_str(cmd, "dmeventd/executable", NULL)))
+               goto_bad;
+
+       if (dm_event_handler_set_dso(dmevh, dso))
+               goto_bad;
+
+       if (dm_event_handler_set_uuid(dmevh, dmuuid))
+               goto_bad;
+
+       dm_event_handler_set_timeout(dmevh, timeout);
+       dm_event_handler_set_event_mask(dmevh, mask);
+
+       return dmevh;
+
+bad:
+       dm_event_handler_destroy(dmevh);
+       return NULL;
+}
+
+char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath)
+{
+       char *path;
+
+       if (!(path = dm_pool_alloc(cmd->mem, PATH_MAX))) {
+               log_error("Failed to allocate dmeventd library path.");
+               return NULL;
+       }
+
+       get_shared_library_path(cmd, libpath, path, PATH_MAX);
+
+       return path;
+}
+
+int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
+                                   struct logical_volume *lv, int *pending)
+{
+       char *uuid;
+       enum dm_event_mask evmask = 0;
+       struct dm_event_handler *dmevh;
+
+       *pending = 0;
+
+       if (!dso)
+               return_0;
+
+       /* We always monitor the "real" device, never the "snapshot-origin" itself. */
+       if (!(uuid = build_dm_uuid(cmd->mem, lv->lvid.s, lv_is_origin(lv) ? "real" : NULL)))
+               return_0;
+
+       if (!(dmevh = _create_dm_event_handler(cmd, uuid, dso, 0, DM_EVENT_ALL_ERRORS)))
+               return_0;
+
+       if (dm_event_get_registered_device(dmevh, 0)) {
+               dm_event_handler_destroy(dmevh);
+               return 0;
+       }
+
+       evmask = dm_event_handler_get_event_mask(dmevh);
+       if (evmask & DM_EVENT_REGISTRATION_PENDING) {
+               *pending = 1;
+               evmask &= ~DM_EVENT_REGISTRATION_PENDING;
+       }
+
+       dm_event_handler_destroy(dmevh);
+
+       return evmask;
+}
+
+int target_register_events(struct cmd_context *cmd, const char *dso, struct logical_volume *lv,
+                           int evmask __attribute__((unused)), int set, int timeout)
+{
+       char *uuid;
+       struct dm_event_handler *dmevh;
+       int r;
+
+       if (!dso)
+               return_0;
+
+       /* We always monitor the "real" device, never the "snapshot-origin" itself. */
+       if (!(uuid = build_dm_uuid(cmd->mem, lv->lvid.s, lv_is_origin(lv) ? "real" : NULL)))
+               return_0;
+
+       if (!(dmevh = _create_dm_event_handler(cmd, uuid, dso, timeout,
+                                              DM_EVENT_ALL_ERRORS | (timeout ? DM_EVENT_TIMEOUT : 0))))
+               return_0;
+
+       r = set ? dm_event_register_handler(dmevh) : dm_event_unregister_handler(dmevh);
+
+       dm_event_handler_destroy(dmevh);
+
+       if (!r)
+               return_0;
+
+       log_info("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
+
+       return 1;
+}
+
+#endif
+
+/*
+ * Returns 0 if an attempt to (un)monitor the device failed.
+ * Returns 1 otherwise.
+ */
+int monitor_dev_for_events(struct cmd_context *cmd, struct logical_volume *lv,
+                          unsigned origin_only, int monitor)
+{
+#ifdef DMEVENTD
+       int i, pending = 0, monitored;
+       int r = 1;
+       struct dm_list *tmp, *snh, *snht;
+       struct lv_segment *seg;
+       struct lv_segment *log_seg;
+       int (*monitor_fn) (struct lv_segment *s, int e);
+       uint32_t s;
+
+       /* skip dmeventd code altogether */
+       if (dmeventd_monitor_mode() == DMEVENTD_MONITOR_IGNORE)
+               return 1;
+
+       /*
+        * Nothing to do if dmeventd configured not to be used.
+        */
+       if (monitor && !dmeventd_monitor_mode())
+               return 1;
+
+       /*
+        * In case of a snapshot device, we monitor lv->snapshot->lv,
+        * not the actual LV itself.
+        */
+       if (lv_is_cow(lv) && !lv_is_merging_cow(lv))
+               return monitor_dev_for_events(cmd, lv->snapshot->lv, 0, monitor);
+
+       /*
+        * In case this LV is a snapshot origin, we instead monitor
+        * each of its respective snapshots.  The origin itself may
+        * also need to be monitored if it is a mirror, for example.
+        */
+       if (!origin_only && lv_is_origin(lv))
+               dm_list_iterate_safe(snh, snht, &lv->snapshot_segs)
+                       if (!monitor_dev_for_events(cmd, dm_list_struct_base(snh,
+                                   struct lv_segment, origin_list)->cow, 0, monitor))
+                               r = 0;
+
+       /*
+        * If the volume is mirrored and its log is also mirrored, monitor
+        * the log volume as well.
+        */
+       if ((seg = first_seg(lv)) != NULL && seg->log_lv != NULL &&
+           (log_seg = first_seg(seg->log_lv)) != NULL &&
+           seg_is_mirrored(log_seg))
+               if (!monitor_dev_for_events(cmd, seg->log_lv, 0, monitor))
+                       r = 0;
+
+       dm_list_iterate(tmp, &lv->segments) {
+               seg = dm_list_item(tmp, struct lv_segment);
+
+               /* Recurse for AREA_LV */
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_LV)
+                               continue;
+                       if (!monitor_dev_for_events(cmd, seg_lv(seg, s), 0,
+                                                   monitor)) {
+                               log_error("Failed to %smonitor %s",
+                                         monitor ? "" : "un",
+                                         seg_lv(seg, s)->name);
+                               r = 0;
+                       }
+               }
+
+               if (!seg_monitored(seg) || (seg->status & PVMOVE))
+                       continue;
+
+               monitor_fn = NULL;
+
+               /* Check monitoring status */
+               if (seg->segtype->ops->target_monitored)
+                       monitored = seg->segtype->ops->target_monitored(seg, &pending);
+               else
+                       continue;  /* segtype doesn't support registration */
+
+               /*
+                * FIXME: We should really try again if pending
+                */
+               monitored = (pending) ? 0 : monitored;
+
+               if (monitor) {
+                       if (monitored)
+                               log_verbose("%s/%s already monitored.", lv->vg->name, lv->name);
+                       else if (seg->segtype->ops->target_monitor_events)
+                               monitor_fn = seg->segtype->ops->target_monitor_events;
+               } else {
+                       if (!monitored)
+                               log_verbose("%s/%s already not monitored.", lv->vg->name, lv->name);
+                       else if (seg->segtype->ops->target_unmonitor_events)
+                               monitor_fn = seg->segtype->ops->target_unmonitor_events;
+               }
+
+               /* Do [un]monitor */
+               if (!monitor_fn)
+                       continue;
+
+               log_verbose("%sonitoring %s/%s%s", monitor ? "M" : "Not m", lv->vg->name, lv->name,
+                           test_mode() ? " [Test mode: skipping this]" : "");
+
+               /* FIXME Test mode should really continue a bit further. */
+               if (test_mode())
+                       continue;
+
+               /* FIXME specify events */
+               if (!monitor_fn(seg, 0)) {
+                       log_error("%s/%s: %s segment monitoring function failed.",
+                                 lv->vg->name, lv->name, seg->segtype->name);
+                       return 0;
+               }
+
+               /* Check [un]monitor results */
+               /* Try a couple times if pending, but not forever... */
+               for (i = 0; i < 10; i++) {
+                       pending = 0;
+                       monitored = seg->segtype->ops->target_monitored(seg, &pending);
+                       if (pending ||
+                           (!monitored && monitor) ||
+                           (monitored && !monitor))
+                               log_very_verbose("%s/%s %smonitoring still pending: waiting...",
+                                                lv->vg->name, lv->name, monitor ? "" : "un");
+                       else
+                               break;
+                       sleep(1);
+               }
+
+               if (r)
+                       r = (monitored && monitor) || (!monitored && !monitor);
+       }
+
+       return r;
+#else
+       return 1;
+#endif
+}
+
+static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
+                      unsigned origin_only, int error_if_not_suspended)
+{
+       struct logical_volume *lv = NULL, *lv_pre = NULL;
+       struct lvinfo info;
+       int r = 0, lockfs = 0, flush_required = 0;
+
+       if (!activation())
+               return 1;
+
+       if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+               goto_out;
+
+       /* Use precommitted metadata if present */
+       if (!(lv_pre = lv_from_lvid(cmd, lvid_s, 1)))
+               goto_out;
+
+       /* Ignore origin_only unless LV is origin in both old and new metadata */
+       if (!lv_is_origin(lv) || !lv_is_origin(lv_pre))
+               origin_only = 0;
+
+       if (test_mode()) {
+               _skip("Suspending %s%s.", lv->name, origin_only ? " origin without snapshots" : "");
+               r = 1;
+               goto out;
+       }
+
+       if (!lv_info(cmd, lv, origin_only, &info, 0, 0))
+               goto_out;
+
+       if (!info.exists || info.suspended) {
+               if (!error_if_not_suspended) {
+                       r = 1;
+                       if (info.suspended)
+                               memlock_inc(cmd);
+               }
+               goto out;
+       }
+
+       if (!lv_read_replicator_vgs(lv))
+               goto_out;
+
+       lv_calculate_readahead(lv, NULL);
+
+       /* If VG was precommitted, preload devices for the LV */
+       if ((lv_pre->vg->status & PRECOMMITTED)) {
+               if (!_lv_preload(lv_pre, origin_only, &flush_required)) {
+                       /* FIXME Revert preloading */
+                       goto_out;
+               }
+       }
+
+       if (!monitor_dev_for_events(cmd, lv, origin_only, 0))
+               /* FIXME Consider aborting here */
+               stack;
+
+       memlock_inc(cmd);
+
+       if (!origin_only &&
+           (lv_is_origin(lv_pre) || lv_is_cow(lv_pre)))
+               lockfs = 1;
+
+       if (!_lv_suspend_lv(lv, origin_only, lockfs, flush_required)) {
+               memlock_dec(cmd);
+               fs_unlock();
+               goto out;
+       }
+
+       r = 1;
+out:
+       if (lv_pre)
+               free_vg(lv_pre->vg);
+       if (lv) {
+               lv_release_replicator_vgs(lv);
+               free_vg(lv->vg);
+       }
+
+       return r;
+}
+
+/* Returns success if the device is not active */
+int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only)
+{
+       return _lv_suspend(cmd, lvid_s, origin_only, 0);
+}
+
+/* No longer used */
+/***********
+int lv_suspend(struct cmd_context *cmd, const char *lvid_s)
+{
+       return _lv_suspend(cmd, lvid_s, 1);
+}
+***********/
+
+static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
+                     unsigned origin_only,
+                     int error_if_not_active)
+{
+       struct logical_volume *lv;
+       struct lvinfo info;
+       int r = 0;
+
+       if (!activation())
+               return 1;
+
+       if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+               goto_out;
+
+       if (!lv_is_origin(lv))
+               origin_only = 0;
+
+       if (test_mode()) {
+               _skip("Resuming %s%s.", lv->name, origin_only ? " without snapshots" : "");
+               r = 1;
+               goto out;
+       }
+
+       if (!lv_info(cmd, lv, origin_only, &info, 0, 0))
+               goto_out;
+
+       if (!info.exists || !info.suspended) {
+               if (error_if_not_active)
+                       goto_out;
+               r = 1;
+               goto out;
+       }
+
+       if (!_lv_activate_lv(lv, origin_only))
+               goto_out;
+
+       memlock_dec(cmd);
+       fs_unlock();
+
+       if (!monitor_dev_for_events(cmd, lv, origin_only, 1))
+               stack;
+
+       r = 1;
+out:
+       if (lv)
+               free_vg(lv->vg);
+
+       return r;
+}
+
+/* Returns success if the device is not active */
+int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only)
+{
+       return _lv_resume(cmd, lvid_s, origin_only, 0);
+}
+
+int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only)
+{
+       return _lv_resume(cmd, lvid_s, origin_only, 1);
+}
+
+static int _lv_has_open_snapshots(struct logical_volume *lv)
+{
+       struct lv_segment *snap_seg;
+       struct lvinfo info;
+       int r = 0;
+
+       dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs, origin_list) {
+               if (!lv_info(lv->vg->cmd, snap_seg->cow, 0, &info, 1, 0)) {
+                       r = 1;
+                       continue;
+               }
+
+               if (info.exists && info.open_count) {
+                       log_error("LV %s/%s has open snapshot %s: "
+                                 "not deactivating", lv->vg->name, lv->name,
+                                 snap_seg->cow->name);
+                       r = 1;
+               }
+       }
+
+       return r;
+}
+
+int lv_deactivate(struct cmd_context *cmd, const char *lvid_s)
+{
+       struct logical_volume *lv;
+       struct lvinfo info;
+       int r = 0;
+
+       if (!activation())
+               return 1;
+
+       if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+               goto out;
+
+       if (test_mode()) {
+               _skip("Deactivating '%s'.", lv->name);
+               r = 1;
+               goto out;
+       }
+
+       if (!lv_info(cmd, lv, 0, &info, 1, 0))
+               goto_out;
+
+       if (!info.exists) {
+               r = 1;
+               goto out;
+       }
+
+       if (lv_is_visible(lv)) {
+               if (info.open_count) {
+                       log_error("LV %s/%s in use: not deactivating",
+                                 lv->vg->name, lv->name);
+                       goto out;
+               }
+               if (lv_is_origin(lv) && _lv_has_open_snapshots(lv))
+                       goto_out;
+       }
+
+       if (!lv_read_replicator_vgs(lv))
+               goto_out;
+
+       lv_calculate_readahead(lv, NULL);
+
+       if (!monitor_dev_for_events(cmd, lv, 0, 0))
+               stack;
+
+       memlock_inc(cmd);
+       r = _lv_deactivate(lv);
+       memlock_dec(cmd);
+       fs_unlock();
+
+       if (!lv_info(cmd, lv, 0, &info, 1, 0) || info.exists)
+               r = 0;
+out:
+       if (lv) {
+               lv_release_replicator_vgs(lv);
+               free_vg(lv->vg);
+       }
+
+       return r;
+}
+
+/* Test if LV passes filter */
+int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
+                        int *activate_lv)
+{
+       struct logical_volume *lv;
+       int r = 0;
+
+       if (!activation()) {
+               *activate_lv = 1;
+               return 1;
+       }
+
+       if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+               goto out;
+
+       if (!_passes_activation_filter(cmd, lv)) {
+               log_verbose("Not activating %s/%s since it does not pass "
+                           "activation filter.", lv->vg->name, lv->name);
+               *activate_lv = 0;
+       } else
+               *activate_lv = 1;
+       r = 1;
+out:
+       if (lv)
+               free_vg(lv->vg);
+
+       return r;
+}
+
+static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
+                       int exclusive, int filter)
+{
+       struct logical_volume *lv;
+       struct lvinfo info;
+       int r = 0;
+
+       if (!activation())
+               return 1;
+
+       if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+               goto out;
+
+       if (filter && !_passes_activation_filter(cmd, lv)) {
+               log_error("Not activating %s/%s since it does not pass "
+                         "activation filter.", lv->vg->name, lv->name);
+               goto out;
+       }
+
+       if ((!lv->vg->cmd->partial_activation) && (lv->status & PARTIAL_LV)) {
+               log_error("Refusing activation of partial LV %s. Use --partial to override.",
+                         lv->name);
+               goto_out;
+       }
+
+       if (lv_has_unknown_segments(lv)) {
+               log_error("Refusing activation of LV %s containing "
+                         "an unrecognised segment.", lv->name);
+               goto_out;
+       }
+
+       if (test_mode()) {
+               _skip("Activating '%s'.", lv->name);
+               r = 1;
+               goto out;
+       }
+
+       if (!lv_info(cmd, lv, 0, &info, 0, 0))
+               goto_out;
+
+       if (info.exists && !info.suspended && info.live_table) {
+               r = 1;
+               goto out;
+       }
+
+       if (!lv_read_replicator_vgs(lv))
+               goto_out;
+
+       lv_calculate_readahead(lv, NULL);
+
+       if (exclusive)
+               lv->status |= ACTIVATE_EXCL;
+
+       memlock_inc(cmd);
+       if (!(r = _lv_activate_lv(lv, 0)))
+               stack;
+       memlock_dec(cmd);
+       fs_unlock();
+
+       if (r && !monitor_dev_for_events(cmd, lv, 0, 1))
+               stack;
+
+out:
+       if (lv) {
+               lv_release_replicator_vgs(lv);
+               free_vg(lv->vg);
+       }
+
+       return r;
+}
+
+/* Activate LV */
+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+       if (!_lv_activate(cmd, lvid_s, exclusive, 0))
+               return_0;
+
+       return 1;
+}
+
+/* Activate LV only if it passes filter */
+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+       if (!_lv_activate(cmd, lvid_s, exclusive, 1))
+               return_0;
+
+       return 1;
+}
+
+int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
+{
+       int r = 1;
+
+       if (!lv) {
+               r = dm_mknodes(NULL);
+               fs_unlock();
+               return r;
+       }
+
+       if (!activation())
+               return 1;
+
+       r = dev_manager_mknodes(lv);
+
+       fs_unlock();
+
+       return r;
+}
+
+/*
+ * Does PV use VG somewhere in its construction?
+ * Returns 1 on failure.
+ */
+int pv_uses_vg(struct physical_volume *pv,
+              struct volume_group *vg)
+{
+       if (!activation())
+               return 0;
+
+       if (!dm_is_dm_major(MAJOR(pv->dev->dev)))
+               return 0;
+
+       return dev_manager_device_uses_vg(pv->dev, vg);
+}
+
+void activation_release(void)
+{
+       dev_manager_release();
+}
+
+void activation_exit(void)
+{
+       dev_manager_exit();
+}
+#endif
diff --git a/lib/activate/activate.h b/lib/activate/activate.h
new file mode 100644 (file)
index 0000000..df46ac2
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef LVM_ACTIVATE_H
+#define LVM_ACTIVATE_H
+
+#include "metadata-exported.h"
+
+struct lvinfo {
+       int exists;
+       int suspended;
+       unsigned int open_count;
+       int major;
+       int minor;
+       int read_only;
+       int live_table;
+       int inactive_table;
+       uint32_t read_ahead;
+};
+
+/* target attribute flags */
+#define MIRROR_LOG_CLUSTERED   0x00000001U
+
+void set_activation(int activation);
+int activation(void);
+
+int driver_version(char *version, size_t size);
+int library_version(char *version, size_t size);
+int lvm1_present(struct cmd_context *cmd);
+
+int module_present(struct cmd_context *cmd, const char *target_name);
+int target_present(struct cmd_context *cmd, const char *target_name,
+                  int use_modprobe);
+int target_version(const char *target_name, uint32_t *maj,
+                  uint32_t *min, uint32_t *patchlevel);
+int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
+                        struct dm_list *modules);
+int list_lv_modules(struct dm_pool *mem, const struct logical_volume *lv,
+                   struct dm_list *modules);
+
+void activation_release(void);
+void activation_exit(void);
+
+/* int lv_suspend(struct cmd_context *cmd, const char *lvid_s); */
+int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only);
+int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only);
+int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only);
+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive);
+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s,
+                           int exclusive);
+int lv_deactivate(struct cmd_context *cmd, const char *lvid_s);
+
+int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
+
+/*
+ * Returns 1 if info structure has been populated, else 0.
+ */
+int lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
+           unsigned origin_only, struct lvinfo *info,
+           int with_open_count, int with_read_ahead);
+int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only,
+                   struct lvinfo *info, int with_open_count, int with_read_ahead);
+
+/*
+ * Returns 1 if activate_lv has been set: 1 = activate; 0 = don't.
+ */
+int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
+                        int *activate_lv);
+
+int lv_check_transient(struct logical_volume *lv);
+/*
+ * Returns 1 if percent has been set, else 0.
+ */
+int lv_snapshot_percent(const struct logical_volume *lv, percent_t *percent);
+int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
+                     int wait, percent_t *percent, uint32_t *event_nr);
+
+/*
+ * Return number of LVs in the VG that are active.
+ */
+int lvs_in_vg_activated(struct volume_group *vg);
+int lvs_in_vg_opened(const struct volume_group *vg);
+
+int lv_is_active(struct logical_volume *lv);
+
+int lv_has_target_type(struct dm_pool *mem, struct logical_volume *lv,
+                      const char *layer, const char *target_type);
+
+int monitor_dev_for_events(struct cmd_context *cmd, struct logical_volume *lv,
+                          unsigned origin_only, int do_reg);
+
+#ifdef DMEVENTD
+#  include "libdevmapper-event.h"
+char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath);
+int target_registered_with_dmeventd(struct cmd_context *cmd, const char *libpath,
+                                   struct logical_volume *lv, int *pending);
+int target_register_events(struct cmd_context *cmd, const char *dso, struct logical_volume *lv,
+                           int evmask __attribute__((unused)), int set, int timeout);
+#endif
+
+/*
+ * Returns 1 if PV has a dependency tree that uses anything in VG.
+ */
+int pv_uses_vg(struct physical_volume *pv,
+              struct volume_group *vg);
+
+/*
+ * Returns 1 if mapped device is not suspended.
+ */
+int device_is_usable(struct device *dev);
+
+#endif
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
new file mode 100644 (file)
index 0000000..a11d918
--- /dev/null
@@ -0,0 +1,1824 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "str_list.h"
+#include "dev_manager.h"
+#include "lvm-string.h"
+#include "fs.h"
+#include "defaults.h"
+#include "segtype.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "targets.h"
+#include "config.h"
+#include "filter.h"
+#include "activate.h"
+
+#include <limits.h>
+#include <dirent.h>
+
+#define MAX_TARGET_PARAMSIZE 50000
+
+typedef enum {
+       PRELOAD,
+       ACTIVATE,
+       DEACTIVATE,
+       SUSPEND,
+       SUSPEND_WITH_LOCKFS,
+       CLEAN
+} action_t;
+
+struct dev_manager {
+       struct dm_pool *mem;
+
+       struct cmd_context *cmd;
+
+       void *target_state;
+       uint32_t pvmove_mirror_count;
+       int flush_required;
+
+       char *vg_name;
+};
+
+struct lv_layer {
+       struct logical_volume *lv;
+       const char *old_name;
+};
+
+static int _read_only_lv(struct logical_volume *lv)
+{
+       return (!(lv->vg->status & LVM_WRITE) || !(lv->status & LVM_WRITE));
+}
+
+/*
+ * Low level device-layer operations.
+ */
+static struct dm_task *_setup_task(const char *name, const char *uuid,
+                                  uint32_t *event_nr, int task,
+                                  uint32_t major, uint32_t minor)
+{
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create(task)))
+               return_NULL;
+
+       if (name)
+               dm_task_set_name(dmt, name);
+
+       if (uuid && *uuid)
+               dm_task_set_uuid(dmt, uuid);
+
+       if (event_nr)
+               dm_task_set_event_nr(dmt, *event_nr);
+
+       if (major)
+               dm_task_set_major_minor(dmt, major, minor, 1);
+
+       return dmt;
+}
+
+static int _info_run(const char *name, const char *dlid, struct dm_info *info,
+                    uint32_t *read_ahead, int mknodes, int with_open_count,
+                    int with_read_ahead, uint32_t major, uint32_t minor)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       int dmtask;
+
+       dmtask = mknodes ? DM_DEVICE_MKNODES : DM_DEVICE_INFO;
+
+       if (!(dmt = _setup_task(mknodes ? name : NULL, dlid, 0, dmtask, major, minor)))
+               return_0;
+
+       if (!with_open_count)
+               if (!dm_task_no_open_count(dmt))
+                       log_error("Failed to disable open_count");
+
+       if (!dm_task_run(dmt))
+               goto_out;
+
+       if (!dm_task_get_info(dmt, info))
+               goto_out;
+
+       if (with_read_ahead && info->exists) {
+               if (!dm_task_get_read_ahead(dmt, read_ahead))
+                       goto_out;
+       } else if (read_ahead)
+               *read_ahead = DM_READ_AHEAD_NONE;
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+int device_is_usable(struct device *dev)
+{
+       struct dm_task *dmt;
+       struct dm_info info;
+       const char *name, *uuid;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params, *vgname = NULL, *lvname, *layer;
+       void *next = NULL;
+       int only_error_target = 1;
+       int r = 0;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) {
+               log_error("Failed to create dm_task struct to check dev status");
+               return 0;
+       }
+
+       if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
+               goto_out;
+
+       if (!dm_task_run(dmt)) {
+               log_error("Failed to get state of mapped device");
+               goto out;
+       }
+
+       if (!dm_task_get_info(dmt, &info))
+               goto_out;
+
+       if (!info.exists)
+               goto out;
+
+       name = dm_task_get_name(dmt);
+       uuid = dm_task_get_uuid(dmt);
+
+       if (!info.target_count) {
+               log_debug("%s: Empty device %s not usable.", dev_name(dev), name);
+               goto out;
+       }
+
+       if (info.suspended && ignore_suspended_devices()) {
+               log_debug("%s: Suspended device %s not usable.", dev_name(dev), name);
+               goto out;
+       }
+
+       /* FIXME Also check for mirror block_on_error and mpath no paths */
+       /* For now, we exclude all mirrors */
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &target_type, &params);
+               /* Skip if target type doesn't match */
+               if (target_type && !strcmp(target_type, "mirror") && ignore_suspended_devices()) {
+                       log_debug("%s: Mirror device %s not usable.", dev_name(dev), name);
+                       goto out;
+               }
+
+               /*
+                * Snapshot origin could be sitting on top of a mirror which
+                * could be blocking I/O.  Skip snapshot origins entirely for
+                * now.
+                *
+                * FIXME: rather than skipping origin, check if mirror is
+                * underneath and if the mirror is blocking I/O.
+                */
+               if (target_type && !strcmp(target_type, "snapshot-origin") &&
+                   ignore_suspended_devices()) {
+                       log_debug("%s: Snapshot-origin device %s not usable.",
+                                 dev_name(dev), name);
+                       goto out;
+               }
+
+               if (target_type && strcmp(target_type, "error"))
+                       only_error_target = 0;
+       } while (next);
+
+       /* Skip devices consisting entirely of error targets. */
+       /* FIXME Deal with device stacked above error targets? */
+       if (only_error_target) {
+               log_debug("%s: Error device %s not usable.",
+                         dev_name(dev), name);
+               goto out;
+       }
+
+       /* FIXME Also check dependencies? */
+
+       /* Check internal lvm devices */
+       if (uuid && !strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) {
+               if (!(vgname = dm_strdup(name)) ||
+                   !dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer))
+                       goto_out;
+
+               if (lvname && (is_reserved_lvname(lvname) || *layer)) {
+                       log_debug("%s: Reserved internal LV device %s/%s%s%s not usable.",
+                                 dev_name(dev), vgname, lvname, *layer ? "-" : "", layer);
+                       goto out;
+               }
+       }
+
+       r = 1;
+
+      out:
+       dm_free(vgname);
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _info(const char *dlid, int with_open_count, int with_read_ahead,
+                struct dm_info *info, uint32_t *read_ahead)
+{
+       int r = 0;
+
+       if ((r = _info_run(NULL, dlid, info, read_ahead, 0, with_open_count,
+                          with_read_ahead, 0, 0)) && info->exists)
+               return 1;
+       else if ((r = _info_run(NULL, dlid + sizeof(UUID_PREFIX) - 1, info,
+                               read_ahead, 0, with_open_count,
+                               with_read_ahead, 0, 0)) && info->exists)
+               return 1;
+
+       return r;
+}
+
+static int _info_by_dev(uint32_t major, uint32_t minor, struct dm_info *info)
+{
+       return _info_run(NULL, NULL, info, NULL, 0, 0, 0, major, minor);
+}
+
+int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
+                    const char *layer,
+                    int with_open_count, int with_read_ahead,
+                    struct dm_info *info, uint32_t *read_ahead)
+{
+       char *dlid, *name;
+       int r;
+
+       if (!(name = build_dm_name(mem, lv->vg->name, lv->name, layer))) {
+               log_error("name build failed for %s", lv->name);
+               return 0;
+       }
+
+       if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer))) {
+               log_error("dlid build failed for %s", name);
+               return 0;
+       }
+
+       log_debug("Getting device info for %s [%s]", name, dlid);
+       r = _info(dlid, with_open_count, with_read_ahead, info, read_ahead);
+
+       dm_pool_free(mem, name);
+       return r;
+}
+
+static const struct dm_info *_cached_info(struct dm_pool *mem,
+                                         const struct logical_volume *lv,
+                                         struct dm_tree *dtree)
+{
+       const char *dlid;
+       struct dm_tree_node *dnode;
+       const struct dm_info *dinfo;
+
+       if (!(dlid = build_dm_uuid(mem, lv->lvid.s, NULL))) {
+               log_error("dlid build failed for %s", lv->name);
+               return NULL;
+       }
+
+       /* An activating merging origin won't have a node in the tree yet */
+       if (!(dnode = dm_tree_find_node_by_uuid(dtree, dlid)))
+               return NULL;
+
+       if (!(dinfo = dm_tree_node_get_info(dnode))) {
+               log_error("failed to get info from tree node for %s", lv->name);
+               return NULL;
+       }
+
+       if (!dinfo->exists)
+               return NULL;
+
+       return dinfo;
+}
+
+/* FIXME Interface must cope with multiple targets */
+static int _status_run(const char *name, const char *uuid,
+                      unsigned long long *s, unsigned long long *l,
+                      char **t, uint32_t t_size, char **p, uint32_t p_size)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_info info;
+       void *next = NULL;
+       uint64_t start, length;
+       char *type = NULL;
+       char *params = NULL;
+
+       if (!(dmt = _setup_task(name, uuid, 0, DM_DEVICE_STATUS, 0, 0)))
+               return_0;
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_run(dmt))
+               goto_out;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto_out;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &type, &params);
+               if (type) {
+                       *s = start;
+                       *l = length;
+                       /* Make sure things are null terminated */
+                       strncpy(*t, type, t_size);
+                       (*t)[t_size - 1] = '\0';
+                       strncpy(*p, params, p_size);
+                       (*p)[p_size - 1] = '\0';
+
+                       r = 1;
+                       /* FIXME Cope with multiple targets! */
+                       break;
+               }
+
+       } while (next);
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _status(const char *name, const char *uuid,
+                  unsigned long long *start, unsigned long long *length,
+                  char **type, uint32_t type_size, char **params,
+                  uint32_t param_size) __attribute__ ((unused));
+
+static int _status(const char *name, const char *uuid,
+                  unsigned long long *start, unsigned long long *length,
+                  char **type, uint32_t type_size, char **params,
+                  uint32_t param_size)
+{
+       if (uuid && *uuid) {
+               if (_status_run(NULL, uuid, start, length, type,
+                               type_size, params, param_size) &&
+                   *params)
+                       return 1;
+               else if (_status_run(NULL, uuid + sizeof(UUID_PREFIX) - 1, start,
+                                    length, type, type_size, params,
+                                    param_size) &&
+                        *params)
+                       return 1;
+       }
+
+       if (name && _status_run(name, NULL, start, length, type, type_size,
+                               params, param_size))
+               return 1;
+
+       return 0;
+}
+
+int lv_has_target_type(struct dm_pool *mem, struct logical_volume *lv,
+                      const char *layer, const char *target_type)
+{
+       int r = 0;
+       char *dlid;
+       struct dm_task *dmt;
+       struct dm_info info;
+       void *next = NULL;
+       uint64_t start, length;
+       char *type = NULL;
+       char *params = NULL;
+
+       if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer)))
+               return_0;
+
+       if (!(dmt = _setup_task(NULL, dlid, 0,
+                               DM_DEVICE_STATUS, 0, 0)))
+               return_0;
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_run(dmt))
+               goto_out;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto_out;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &type, &params);
+               if (type && strncmp(type, target_type,
+                                   strlen(target_type)) == 0) {
+                       if (info.live_table)
+                               r = 1;
+                       break;
+               }
+       } while (next);
+
+ out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static percent_range_t _combine_percent(percent_t a, percent_t b,
+                                        uint32_t numerator, uint32_t denominator)
+{
+       if (a == PERCENT_INVALID || b == PERCENT_INVALID)
+               return PERCENT_INVALID;
+
+       if (a == PERCENT_100 && b == PERCENT_100)
+               return PERCENT_100;
+
+       if (a == PERCENT_0 && b == PERCENT_0)
+               return PERCENT_0;
+
+       return make_percent(numerator, denominator);
+}
+
+static int _percent_run(struct dev_manager *dm, const char *name,
+                       const char *dlid,
+                       const char *target_type, int wait,
+                       const struct logical_volume *lv, percent_t *overall_percent,
+                       uint32_t *event_nr, int fail_if_percent_unsupported)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_info info;
+       void *next = NULL;
+       uint64_t start, length;
+       char *type = NULL;
+       char *params = NULL;
+       const struct dm_list *segh = &lv->segments;
+       struct lv_segment *seg = NULL;
+       struct segment_type *segtype;
+       int first_time = 1;
+       percent_t percent;
+
+       uint64_t total_numerator = 0, total_denominator = 0;
+
+       *overall_percent = PERCENT_INVALID;
+
+       if (!(dmt = _setup_task(name, dlid, event_nr,
+                               wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS, 0, 0)))
+               return_0;
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_run(dmt))
+               goto_out;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto_out;
+
+       if (event_nr)
+               *event_nr = info.event_nr;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length, &type,
+                                         &params);
+               if (lv) {
+                       if (!(segh = dm_list_next(&lv->segments, segh))) {
+                               log_error("Number of segments in active LV %s "
+                                         "does not match metadata", lv->name);
+                               goto out;
+                       }
+                       seg = dm_list_item(segh, struct lv_segment);
+               }
+
+               if (!type || !params)
+                       continue;
+
+               if (!(segtype = get_segtype_from_string(dm->cmd, target_type)))
+                       continue;
+
+               if (strcmp(type, target_type)) {
+                       /* If kernel's type isn't an exact match is it compatible? */
+                       if (!segtype->ops->target_status_compatible ||
+                           !segtype->ops->target_status_compatible(type))
+                               continue;
+               }
+
+               if (!segtype->ops->target_percent)
+                       continue;
+
+               if (!segtype->ops->target_percent(&dm->target_state,
+                                                 &percent, dm->mem,
+                                                 dm->cmd, seg, params,
+                                                 &total_numerator,
+                                                 &total_denominator))
+                       goto_out;
+
+               if (first_time) {
+                       *overall_percent = percent;
+                       first_time = 0;
+               } else
+                       *overall_percent =
+                               _combine_percent(*overall_percent, percent,
+                                                total_numerator, total_denominator);
+       } while (next);
+
+       if (lv && dm_list_next(&lv->segments, segh)) {
+               log_error("Number of segments in active LV %s does not "
+                         "match metadata", lv->name);
+               goto out;
+       }
+
+       if (first_time) {
+               /* above ->target_percent() was not executed! */
+               /* FIXME why return PERCENT_100 et. al. in this case? */
+               *overall_percent = PERCENT_100;
+               if (fail_if_percent_unsupported)
+                       goto_out;
+       }
+
+       log_debug("LV percent: %f", percent_to_float(*overall_percent));
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _percent(struct dev_manager *dm, const char *name, const char *dlid,
+                   const char *target_type, int wait,
+                   const struct logical_volume *lv, percent_t *percent,
+                   uint32_t *event_nr, int fail_if_percent_unsupported)
+{
+       if (dlid && *dlid) {
+               if (_percent_run(dm, NULL, dlid, target_type, wait, lv, percent,
+                                event_nr, fail_if_percent_unsupported))
+                       return 1;
+               else if (_percent_run(dm, NULL, dlid + sizeof(UUID_PREFIX) - 1,
+                                     target_type, wait, lv, percent,
+                                     event_nr, fail_if_percent_unsupported))
+                       return 1;
+       }
+
+       if (name && _percent_run(dm, name, NULL, target_type, wait, lv, percent,
+                                event_nr, fail_if_percent_unsupported))
+               return 1;
+
+       return 0;
+}
+
+/* FIXME Merge with the percent function */
+int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_info info;
+       void *next = NULL;
+       uint64_t start, length;
+       char *type = NULL;
+       char *params = NULL;
+       char *dlid = NULL;
+       const char *layer = lv_is_origin(lv) ? "real" : NULL;
+       const struct dm_list *segh = &lv->segments;
+       struct lv_segment *seg = NULL;
+
+       if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer)))
+               return_0;
+
+       if (!(dmt = _setup_task(0, dlid, NULL, DM_DEVICE_STATUS, 0, 0)))
+               return_0;
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_run(dmt))
+               goto_out;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto_out;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length, &type,
+                                         &params);
+
+               if (!(segh = dm_list_next(&lv->segments, segh))) {
+                   log_error("Number of segments in active LV %s "
+                             "does not match metadata", lv->name);
+                   goto out;
+               }
+               seg = dm_list_item(segh, struct lv_segment);
+
+               if (!type || !params)
+                       continue;
+
+               if (seg->segtype->ops->check_transient_status &&
+                   !seg->segtype->ops->check_transient_status(seg, params))
+                       goto_out;
+
+       } while (next);
+
+       if (dm_list_next(&lv->segments, segh)) {
+               log_error("Number of segments in active LV %s does not "
+                         "match metadata", lv->name);
+               goto out;
+       }
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+/*
+ * dev_manager implementation.
+ */
+struct dev_manager *dev_manager_create(struct cmd_context *cmd,
+                                      const char *vg_name)
+{
+       struct dm_pool *mem;
+       struct dev_manager *dm;
+
+       if (!(mem = dm_pool_create("dev_manager", 16 * 1024)))
+               return_NULL;
+
+       if (!(dm = dm_pool_zalloc(mem, sizeof(*dm))))
+               goto_bad;
+
+       dm->cmd = cmd;
+       dm->mem = mem;
+
+       if (!(dm->vg_name = dm_pool_strdup(dm->mem, vg_name)))
+               goto_bad;
+
+       dm->target_state = NULL;
+
+       dm_udev_set_sync_support(cmd->current_settings.udev_sync);
+
+       return dm;
+
+      bad:
+       dm_pool_destroy(mem);
+       return NULL;
+}
+
+void dev_manager_destroy(struct dev_manager *dm)
+{
+       dm_pool_destroy(dm->mem);
+}
+
+void dev_manager_release(void)
+{
+       dm_lib_release();
+}
+
+void dev_manager_exit(void)
+{
+       dm_lib_exit();
+}
+
+int dev_manager_snapshot_percent(struct dev_manager *dm,
+                                const struct logical_volume *lv,
+                                percent_t *percent)
+{
+       char *name;
+       const char *dlid;
+       int fail_if_percent_unsupported = 0;
+
+       if (lv_is_merging_origin(lv)) {
+               /*
+                * Set 'fail_if_percent_unsupported', otherwise passing
+                * unsupported LV types to _percent will lead to a default
+                * successful return with percent_range as PERCENT_100.
+                * - For a merging origin, this will result in a polldaemon
+                *   that runs infinitely (because completion is PERCENT_0)
+                * - We unfortunately don't yet _know_ if a snapshot-merge
+                *   target is active (activation is deferred if dev is open);
+                *   so we can't short-circuit origin devices based purely on
+                *   existing LVM LV attributes.
+                */
+               fail_if_percent_unsupported = 1;
+       }
+
+       /*
+        * Build a name for the top layer.
+        */
+       if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, NULL)))
+               return_0;
+
+       if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, NULL)))
+               return_0;
+
+       /*
+        * Try and get some info on this device.
+        */
+       log_debug("Getting device status percentage for %s", name);
+       if (!(_percent(dm, name, dlid, "snapshot", 0, NULL, percent,
+                      NULL, fail_if_percent_unsupported)))
+               return_0;
+
+       /* FIXME dm_pool_free ? */
+
+       /* If the snapshot isn't available, percent will be -1 */
+       return 1;
+}
+
+/* FIXME Merge with snapshot_percent, auto-detecting target type */
+/* FIXME Cope with more than one target */
+int dev_manager_mirror_percent(struct dev_manager *dm,
+                              const struct logical_volume *lv, int wait,
+                              percent_t *percent, uint32_t *event_nr)
+{
+       char *name;
+       const char *dlid;
+       const char *layer = (lv_is_origin(lv)) ? "real" : NULL;
+
+       /*
+        * Build a name for the top layer.
+        */
+       if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
+               return_0;
+
+       /* FIXME dm_pool_free ? */
+
+       if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) {
+               log_error("dlid build failed for %s", lv->name);
+               return 0;
+       }
+
+       log_debug("Getting device mirror status percentage for %s", name);
+       if (!(_percent(dm, name, dlid, "mirror", wait, lv, percent,
+                      event_nr, 0)))
+               return_0;
+
+       return 1;
+}
+
+#if 0
+       log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", name);
+
+       log_verbose("Loading %s", dl->name);
+                       log_very_verbose("Activating %s read-only", dl->name);
+       log_very_verbose("Activated %s %s %03u:%03u", dl->name,
+                        dl->dlid, dl->info.major, dl->info.minor);
+
+       if (_get_flag(dl, VISIBLE))
+               log_verbose("Removing %s", dl->name);
+       else
+               log_very_verbose("Removing %s", dl->name);
+
+       log_debug("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
+                 extent_size * seg->le, extent_size * seg->len, target, params);
+
+       log_debug("Adding target: 0 %" PRIu64 " snapshot-origin %s",
+                 dl->lv->size, params);
+       log_debug("Adding target: 0 %" PRIu64 " snapshot %s", size, params);
+       log_debug("Getting device info for %s", dl->name);
+
+       /* Rename? */
+               if ((suffix = strrchr(dl->dlid + sizeof(UUID_PREFIX) - 1, '-')))
+                       suffix++;
+               new_name = build_dm_name(dm->mem, dm->vg_name, dl->lv->name,
+                                       suffix);
+
+static int _belong_to_vg(const char *vgname, const char *name)
+{
+       const char *v = vgname, *n = name;
+
+       while (*v) {
+               if ((*v != *n) || (*v == '-' && *(++n) != '-'))
+                       return 0;
+               v++, n++;
+       }
+
+       if (*n == '-' && *(n + 1) != '-')
+               return 1;
+       else
+               return 0;
+}
+
+       if (!(snap_seg = find_cow(lv)))
+               return 1;
+
+       old_origin = snap_seg->origin;
+
+       /* Was this the last active snapshot with this origin? */
+       dm_list_iterate_items(lvl, active_head) {
+               active = lvl->lv;
+               if ((snap_seg = find_cow(active)) &&
+                   snap_seg->origin == old_origin) {
+                       return 1;
+               }
+       }
+
+#endif
+
+/*************************/
+/*  NEW CODE STARTS HERE */
+/*************************/
+
+static int _dev_manager_lv_mknodes(const struct logical_volume *lv)
+{
+       char *name;
+
+       if (!(name = build_dm_name(lv->vg->cmd->mem, lv->vg->name,
+                                  lv->name, NULL)))
+               return_0;
+
+       return fs_add_lv(lv, name);
+}
+
+static int _dev_manager_lv_rmnodes(const struct logical_volume *lv)
+{
+       return fs_del_lv(lv);
+}
+
+int dev_manager_mknodes(const struct logical_volume *lv)
+{
+       struct dm_info dminfo;
+       char *name;
+       int r = 0;
+
+       if (!(name = build_dm_name(lv->vg->cmd->mem, lv->vg->name, lv->name, NULL)))
+               return_0;
+
+       if ((r = _info_run(name, NULL, &dminfo, NULL, 1, 0, 0, 0, 0))) {
+               if (dminfo.exists) {
+                       if (lv_is_visible(lv))
+                               r = _dev_manager_lv_mknodes(lv);
+               } else
+                       r = _dev_manager_lv_rmnodes(lv);
+       }
+
+       dm_pool_free(lv->vg->cmd->mem, name);
+       return r;
+}
+
+static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *lv,
+                               const char *layer)
+{
+       uint16_t udev_flags = 0;
+
+       /*
+        * Is this top-level and visible device?
+        * If not, create just the /dev/mapper content.
+        */
+       if (layer || !lv_is_visible(lv))
+               udev_flags |= DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG |
+                             DM_UDEV_DISABLE_DISK_RULES_FLAG |
+                             DM_UDEV_DISABLE_OTHER_RULES_FLAG;
+       /*
+        * There's no need for other udev rules to touch special LVs with
+        * reserved names. We don't need to populate /dev/disk here either.
+        * Even if they happen to be visible and top-level.
+        */
+       else if (is_reserved_lvname(lv->name))
+               udev_flags |= DM_UDEV_DISABLE_DISK_RULES_FLAG |
+                             DM_UDEV_DISABLE_OTHER_RULES_FLAG;
+
+       /*
+        * Snapshots and origins could have the same rule applied that will
+        * give symlinks exactly the same name (e.g. a name based on
+        * filesystem UUID). We give preference to origins to make such
+        * naming deterministic (e.g. symlinks in /dev/disk/by-uuid).
+        */
+       if (lv_is_cow(lv))
+               udev_flags |= DM_UDEV_LOW_PRIORITY_FLAG;
+
+       /*
+        * Finally, add flags to disable /dev/mapper and /dev/<vgname> content
+        * to be created by udev if it is requested by user's configuration.
+        * This is basically an explicit fallback to old node/symlink creation
+        * without udev.
+        */
+       if (!dm->cmd->current_settings.udev_rules)
+               udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+                             DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+       return udev_flags;
+}
+
+static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
+                              struct logical_volume *lv, const char *layer)
+{
+       char *dlid, *name;
+       struct dm_info info, info2;
+
+       if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
+               return_0;
+
+       if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer)))
+               return_0;
+
+       log_debug("Getting device info for %s [%s]", name, dlid);
+       if (!_info(dlid, 1, 0, &info, NULL)) {
+               log_error("Failed to get info for %s [%s].", name, dlid);
+               return 0;
+       }
+
+       /*
+        * For top level volumes verify that existing device match
+        * requested major/minor and that major/minor pair is available for use
+        */
+       if (!layer && lv->major != -1 && lv->minor != -1) {
+               /*
+                * FIXME compare info.major with lv->major if multiple major support
+                */
+               if (info.exists && (info.minor != lv->minor)) {
+                       log_error("Volume %s (%" PRIu32 ":%" PRIu32")"
+                                 " differs from already active device "
+                                 "(%" PRIu32 ":%" PRIu32")",
+                                 lv->name, lv->major, lv->minor, info.major, info.minor);
+                       return 0;
+               }
+               if (!info.exists && _info_by_dev(lv->major, lv->minor, &info2) &&
+                   info2.exists) {
+                       log_error("The requested major:minor pair "
+                                 "(%" PRIu32 ":%" PRIu32") is already used",
+                                 lv->major, lv->minor);
+                       return 0;
+               }
+       }
+
+       if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor,
+                                                       _get_udev_flags(dm, lv, layer))) {
+               log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree",
+                         info.major, info.minor);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Add replicator devices
+ *
+ * Using _add_dev_to_dtree() directly instead of _add_lv_to_dtree()
+ * to avoid extra checks with extensions.
+ */
+static int _add_partial_replicator_to_dtree(struct dev_manager *dm,
+                                           struct dm_tree *dtree,
+                                           struct logical_volume *lv)
+{
+       struct logical_volume *rlv = first_seg(lv)->replicator;
+       struct replicator_device *rdev;
+       struct replicator_site *rsite;
+       struct dm_tree_node *rep_node, *rdev_node;
+       const char *uuid;
+
+       if (!lv_is_active_replicator_dev(lv)) {
+               if (!_add_dev_to_dtree(dm, dtree, lv->rdevice->lv,
+                                     NULL))
+                       return_0;
+               return 1;
+       }
+
+       /* Add _rlog and replicator device */
+       if (!_add_dev_to_dtree(dm, dtree, first_seg(rlv)->rlog_lv, NULL))
+               return_0;
+
+       if (!_add_dev_to_dtree(dm, dtree, rlv, NULL))
+               return_0;
+
+       if (!(uuid = build_dm_uuid(dm->mem, rlv->lvid.s, NULL)))
+               return_0;
+
+       rep_node = dm_tree_find_node_by_uuid(dtree, uuid);
+
+       /* Add all related devices for replicator */
+       dm_list_iterate_items(rsite, &rlv->rsites)
+               dm_list_iterate_items(rdev, &rsite->rdevices) {
+                       if (rsite->state == REPLICATOR_STATE_ACTIVE) {
+                               /* Add _rimage LV */
+                               if (!_add_dev_to_dtree(dm, dtree, rdev->lv, NULL))
+                                       return_0;
+
+                               /* Add replicator-dev LV, except of the already added one */
+                               if ((lv != rdev->replicator_dev->lv) &&
+                                   !_add_dev_to_dtree(dm, dtree,
+                                                      rdev->replicator_dev->lv, NULL))
+                                       return_0;
+
+                               /* If replicator exists - try connect existing heads */
+                               if (rep_node) {
+                                       uuid = build_dm_uuid(dm->mem,
+                                                            rdev->replicator_dev->lv->lvid.s,
+                                                            NULL);
+                                       if (!uuid)
+                                               return_0;
+
+                                       rdev_node = dm_tree_find_node_by_uuid(dtree, uuid);
+                                       if (rdev_node)
+                                               dm_tree_node_set_presuspend_node(rdev_node,
+                                                                                rep_node);
+                               }
+                       }
+
+                       if (!rdev->rsite->vg_name)
+                               continue;
+
+                       if (!_add_dev_to_dtree(dm, dtree, rdev->lv, NULL))
+                               return_0;
+
+                       if (rdev->slog &&
+                           !_add_dev_to_dtree(dm, dtree, rdev->slog, NULL))
+                               return_0;
+               }
+
+       return 1;
+}
+
+/*
+ * Add LV and any known dependencies
+ */
+static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, struct logical_volume *lv, unsigned origin_only)
+{
+       if (!origin_only && !_add_dev_to_dtree(dm, dtree, lv, NULL))
+               return_0;
+
+       /* FIXME Can we avoid doing this every time? */
+       if (!_add_dev_to_dtree(dm, dtree, lv, "real"))
+               return_0;
+
+       if (!origin_only && !_add_dev_to_dtree(dm, dtree, lv, "cow"))
+               return_0;
+
+       if ((lv->status & MIRRORED) && first_seg(lv)->log_lv &&
+           !_add_dev_to_dtree(dm, dtree, first_seg(lv)->log_lv, NULL))
+               return_0;
+
+       /* Adding LV head of replicator adds all other related devs */
+       if (lv_is_replicator_dev(lv) &&
+           !_add_partial_replicator_to_dtree(dm, dtree, lv))
+               return_0;
+
+       return 1;
+}
+
+static struct dm_tree *_create_partial_dtree(struct dev_manager *dm, struct logical_volume *lv, unsigned origin_only)
+{
+       struct dm_tree *dtree;
+       struct dm_list *snh, *snht;
+       struct lv_segment *seg;
+       uint32_t s;
+
+       if (!(dtree = dm_tree_create())) {
+               log_error("Partial dtree creation failed for %s.", lv->name);
+               return NULL;
+       }
+
+       if (!_add_lv_to_dtree(dm, dtree, lv, origin_only))
+               goto_bad;
+
+       /* Add any snapshots of this LV */
+       if (!origin_only)
+               dm_list_iterate_safe(snh, snht, &lv->snapshot_segs)
+                       if (!_add_lv_to_dtree(dm, dtree, dm_list_struct_base(snh, struct lv_segment, origin_list)->cow, 0))
+                               goto_bad;
+
+       /* Add any LVs used by segments in this LV */
+       dm_list_iterate_items(seg, &lv->segments)
+               for (s = 0; s < seg->area_count; s++)
+                       if (seg_type(seg, s) == AREA_LV && seg_lv(seg, s)) {
+                               if (!_add_lv_to_dtree(dm, dtree, seg_lv(seg, s), 0))
+                                       goto_bad;
+                       }
+
+       return dtree;
+
+bad:
+       dm_tree_free(dtree);
+       return NULL;
+}
+
+static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
+                              struct lv_segment *seg, int s)
+{
+       char *id, *name;
+       char errid[32];
+       struct dm_tree_node *node;
+       struct lv_segment *seg_i;
+       int segno = -1, i = 0;;
+       uint64_t size = seg->len * seg->lv->vg->extent_size;
+
+       dm_list_iterate_items(seg_i, &seg->lv->segments) {
+               if (seg == seg_i)
+                       segno = i;
+               ++i;
+       }
+
+       if (segno < 0) {
+               log_error("_add_error_device called with bad segment");
+               return_NULL;
+       }
+
+       sprintf(errid, "missing_%d_%d", segno, s);
+
+       if (!(id = build_dm_uuid(dm->mem, seg->lv->lvid.s, errid)))
+               return_NULL;
+
+       if (!(name = build_dm_name(dm->mem, seg->lv->vg->name,
+                                  seg->lv->name, errid)))
+               return_NULL;
+       if (!(node = dm_tree_add_new_dev(dtree, name, id, 0, 0, 0, 0, 0)))
+               return_NULL;
+       if (!dm_tree_node_add_error_target(node, size))
+               return_NULL;
+
+       return id;
+}
+
+static int _add_error_area(struct dev_manager *dm, struct dm_tree_node *node,
+                          struct lv_segment *seg, int s)
+{
+       char *dlid;
+       uint64_t extent_size = seg->lv->vg->extent_size;
+
+       if (!strcmp(dm->cmd->stripe_filler, "error")) {
+               /*
+                * FIXME, the tree pointer is first field of dm_tree_node, but
+                * we don't have the struct definition available.
+                */
+               struct dm_tree **tree = (struct dm_tree **) node;
+               dlid = _add_error_device(dm, *tree, seg, s);
+               if (!dlid)
+                       return_0;
+               dm_tree_node_add_target_area(node, NULL, dlid,
+                                            extent_size * seg_le(seg, s));
+       } else
+               dm_tree_node_add_target_area(node,
+                                            dm->cmd->stripe_filler,
+                                            NULL, UINT64_C(0));
+
+       return 1;
+}
+
+int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+                  struct dm_tree_node *node, uint32_t start_area,
+                  uint32_t areas)
+{
+       uint64_t extent_size = seg->lv->vg->extent_size;
+       uint32_t s;
+       char *dlid;
+
+       for (s = start_area; s < areas; s++) {
+               if ((seg_type(seg, s) == AREA_PV &&
+                    (!seg_pvseg(seg, s) ||
+                     !seg_pv(seg, s) ||
+                     !seg_dev(seg, s))) ||
+                   (seg_type(seg, s) == AREA_LV && !seg_lv(seg, s))) {
+                       if (!_add_error_area(dm, node, seg, s))
+                               return_0;
+               } else if (seg_type(seg, s) == AREA_PV)
+                       dm_tree_node_add_target_area(node,
+                                                       dev_name(seg_dev(seg, s)),
+                                                       NULL,
+                                                       (seg_pv(seg, s)->pe_start +
+                                                        (extent_size * seg_pe(seg, s))));
+               else if (seg_type(seg, s) == AREA_LV) {
+                       if (!(dlid = build_dm_uuid(dm->mem,
+                                                  seg_lv(seg, s)->lvid.s,
+                                                  NULL)))
+                               return_0;
+                       dm_tree_node_add_target_area(node, NULL, dlid,
+                                                       extent_size * seg_le(seg, s));
+               } else {
+                       log_error(INTERNAL_ERROR "Unassigned area found in LV %s.",
+                                 seg->lv->name);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _add_origin_target_to_dtree(struct dev_manager *dm,
+                                        struct dm_tree_node *dnode,
+                                        struct logical_volume *lv)
+{
+       const char *real_dlid;
+
+       if (!(real_dlid = build_dm_uuid(dm->mem, lv->lvid.s, "real")))
+               return_0;
+
+       if (!dm_tree_node_add_snapshot_origin_target(dnode, lv->size, real_dlid))
+               return_0;
+
+       return 1;
+}
+
+static int _add_snapshot_merge_target_to_dtree(struct dev_manager *dm,
+                                              struct dm_tree_node *dnode,
+                                              struct logical_volume *lv)
+{
+       const char *origin_dlid, *cow_dlid, *merge_dlid;
+       struct lv_segment *merging_cow_seg = find_merging_cow(lv);
+
+       if (!(origin_dlid = build_dm_uuid(dm->mem, lv->lvid.s, "real")))
+               return_0;
+
+       if (!(cow_dlid = build_dm_uuid(dm->mem, merging_cow_seg->cow->lvid.s, "cow")))
+               return_0;
+
+       if (!(merge_dlid = build_dm_uuid(dm->mem, merging_cow_seg->cow->lvid.s, NULL)))
+               return_0;
+
+       if (!dm_tree_node_add_snapshot_merge_target(dnode, lv->size, origin_dlid,
+                                                   cow_dlid, merge_dlid,
+                                                   merging_cow_seg->chunk_size))
+               return_0;
+
+       return 1;
+}
+
+static int _add_snapshot_target_to_dtree(struct dev_manager *dm,
+                                          struct dm_tree_node *dnode,
+                                          struct logical_volume *lv)
+{
+       const char *origin_dlid;
+       const char *cow_dlid;
+       struct lv_segment *snap_seg;
+       uint64_t size;
+
+       if (!(snap_seg = find_cow(lv))) {
+               log_error("Couldn't find snapshot for '%s'.", lv->name);
+               return 0;
+       }
+
+       if (!(origin_dlid = build_dm_uuid(dm->mem, snap_seg->origin->lvid.s, "real")))
+               return_0;
+
+       if (!(cow_dlid = build_dm_uuid(dm->mem, snap_seg->cow->lvid.s, "cow")))
+               return_0;
+
+       size = (uint64_t) snap_seg->len * snap_seg->origin->vg->extent_size;
+
+       if (lv_is_merging_cow(lv)) {
+               /* cow is to be merged so load the error target */
+               if (!dm_tree_node_add_error_target(dnode, size))
+                       return_0;
+       }
+       else if (!dm_tree_node_add_snapshot_target(dnode, size, origin_dlid,
+                                                  cow_dlid, 1, snap_seg->chunk_size))
+               return_0;
+
+       return 1;
+}
+
+static int _add_target_to_dtree(struct dev_manager *dm,
+                                 struct dm_tree_node *dnode,
+                                 struct lv_segment *seg)
+{
+       uint64_t extent_size = seg->lv->vg->extent_size;
+
+       if (!seg->segtype->ops->add_target_line) {
+               log_error(INTERNAL_ERROR "_emit_target cannot handle "
+                         "segment type %s", seg->segtype->name);
+               return 0;
+       }
+
+       return seg->segtype->ops->add_target_line(dm, dm->mem, dm->cmd,
+                                                 &dm->target_state, seg,
+                                                 dnode,
+                                                 extent_size * seg->len,
+                                                 &dm-> pvmove_mirror_count);
+}
+
+static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
+                                 struct logical_volume *lv, const char *layer);
+
+/* Add all replicators' LVs */
+static int _add_replicator_dev_target_to_dtree(struct dev_manager *dm,
+                                              struct dm_tree *dtree,
+                                              struct lv_segment *seg)
+{
+       struct replicator_device *rdev;
+       struct replicator_site *rsite;
+
+       /* For inactive replicator add linear mapping */
+       if (!lv_is_active_replicator_dev(seg->lv)) {
+               if (!_add_new_lv_to_dtree(dm, dtree, seg->lv->rdevice->lv, NULL))
+                       return_0;
+               return 1;
+       }
+
+       /* Add rlog and replicator nodes */
+       if (!seg->replicator ||
+            !first_seg(seg->replicator)->rlog_lv ||
+           !_add_new_lv_to_dtree(dm, dtree,
+                                 first_seg(seg->replicator)->rlog_lv, NULL) ||
+           !_add_new_lv_to_dtree(dm, dtree, seg->replicator, NULL))
+           return_0;
+
+       /* Activation of one replicator_dev node activates all other nodes */
+       dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+               dm_list_iterate_items(rdev, &rsite->rdevices) {
+                       if (rdev->lv &&
+                           !_add_new_lv_to_dtree(dm, dtree, rdev->lv, NULL))
+                               return_0;
+
+                       if (rdev->slog &&
+                           !_add_new_lv_to_dtree(dm, dtree,
+                                                 rdev->slog, NULL))
+                               return_0;
+               }
+       }
+       /* Add remaining replicator-dev nodes in the second loop
+        * to avoid multiple retries for inserting all elements */
+       dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+               if (rsite->state != REPLICATOR_STATE_ACTIVE)
+                       continue;
+               dm_list_iterate_items(rdev, &rsite->rdevices) {
+                       if (rdev->replicator_dev->lv == seg->lv)
+                               continue;
+                       if (!rdev->replicator_dev->lv ||
+                           !_add_new_lv_to_dtree(dm, dtree,
+                                                 rdev->replicator_dev->lv,
+                                                 NULL))
+                               return_0;
+               }
+       }
+
+       return 1;
+}
+
+static int _add_segment_to_dtree(struct dev_manager *dm,
+                                  struct dm_tree *dtree,
+                                  struct dm_tree_node *dnode,
+                                  struct lv_segment *seg,
+                                  const char *layer)
+{
+       uint32_t s;
+       struct dm_list *snh;
+       struct lv_segment *seg_present;
+       const char *target_name;
+
+       /* Ensure required device-mapper targets are loaded */
+       seg_present = find_cow(seg->lv) ? : seg;
+       target_name = (seg_present->segtype->ops->target_name ?
+                      seg_present->segtype->ops->target_name(seg_present) :
+                      seg_present->segtype->name);
+
+       log_debug("Checking kernel supports %s segment type for %s%s%s",
+                 target_name, seg->lv->name,
+                 layer ? "-" : "", layer ? : "");
+
+       if (seg_present->segtype->ops->target_present &&
+           !seg_present->segtype->ops->target_present(seg_present->lv->vg->cmd,
+                                                      seg_present, NULL)) {
+               log_error("Can't process LV %s: %s target support missing "
+                         "from kernel?", seg->lv->name, target_name);
+               return 0;
+       }
+
+       /* Add mirror log */
+       if (seg->log_lv &&
+           !_add_new_lv_to_dtree(dm, dtree, seg->log_lv, NULL))
+               return_0;
+
+       if (seg_is_replicator_dev(seg)) {
+               if (!_add_replicator_dev_target_to_dtree(dm, dtree, seg))
+                       return_0;
+       /* If this is a snapshot origin, add real LV */
+       /* If this is a snapshot origin + merging snapshot, add cow + real LV */
+       } else if (lv_is_origin(seg->lv) && !layer) {
+               if (vg_is_clustered(seg->lv->vg)) {
+                       log_error("Clustered snapshots are not yet supported");
+                       return 0;
+               }
+               if (lv_is_merging_origin(seg->lv)) {
+                       if (!_add_new_lv_to_dtree(dm, dtree,
+                            find_merging_cow(seg->lv)->cow, "cow"))
+                               return_0;
+                       /*
+                        * Must also add "real" LV for use when
+                        * snapshot-merge target is added
+                        */
+               }
+               if (!_add_new_lv_to_dtree(dm, dtree, seg->lv, "real"))
+                       return_0;
+       } else if (lv_is_cow(seg->lv) && !layer) {
+               if (!_add_new_lv_to_dtree(dm, dtree, seg->lv, "cow"))
+                       return_0;
+       } else {
+               /* Add any LVs used by this segment */
+               for (s = 0; s < seg->area_count; s++)
+                       if ((seg_type(seg, s) == AREA_LV) &&
+                           (!_add_new_lv_to_dtree(dm, dtree, seg_lv(seg, s),
+                                                  NULL)))
+                               return_0;
+       }
+
+       /* Now we've added its dependencies, we can add the target itself */
+       if (lv_is_origin(seg->lv) && !layer) {
+               if (!lv_is_merging_origin(seg->lv)) {
+                       if (!_add_origin_target_to_dtree(dm, dnode, seg->lv))
+                               return_0;
+               } else {
+                       if (!_add_snapshot_merge_target_to_dtree(dm, dnode, seg->lv))
+                               return_0;
+               }
+       } else if (lv_is_cow(seg->lv) && !layer) {
+               if (!_add_snapshot_target_to_dtree(dm, dnode, seg->lv))
+                       return_0;
+       } else if (!_add_target_to_dtree(dm, dnode, seg))
+               return_0;
+
+       if (lv_is_origin(seg->lv) && !layer)
+               /* Add any snapshots of this LV */
+               dm_list_iterate(snh, &seg->lv->snapshot_segs)
+                       if (!_add_new_lv_to_dtree(dm, dtree, dm_list_struct_base(snh, struct lv_segment, origin_list)->cow, NULL))
+                               return_0;
+
+       return 1;
+}
+
+static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
+                                 struct logical_volume *lv, const char *layer)
+{
+       struct lv_segment *seg;
+       struct lv_layer *lvlayer;
+       struct dm_tree_node *dnode;
+       const struct dm_info *dinfo;
+       char *name, *dlid;
+       uint32_t max_stripe_size = UINT32_C(0);
+       uint32_t read_ahead = lv->read_ahead;
+       uint32_t read_ahead_flags = UINT32_C(0);
+
+       /* FIXME Seek a simpler way to lay out the snapshot-merge tree. */
+
+       if (lv_is_origin(lv) && lv_is_merging_origin(lv) && !layer) {
+               /*
+                * Clear merge attributes if merge isn't currently possible:
+                * either origin or merging snapshot are open
+                * - but use "snapshot-merge" if it is already in use
+                * - open_count is always retrieved (as of dm-ioctl 4.7.0)
+                *   so just use the tree's existing nodes' info
+                */
+               if (((dinfo = _cached_info(dm->mem, lv,
+                                          dtree)) && dinfo->open_count) ||
+                   ((dinfo = _cached_info(dm->mem, find_merging_cow(lv)->cow,
+                                          dtree)) && dinfo->open_count)) {
+                       /* FIXME Is there anything simpler to check for instead? */
+                       if (!lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge"))
+                               clear_snapshot_merge(lv);
+               }
+       }
+
+       if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
+               return_0;
+
+       if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer)))
+               return_0;
+
+       /* We've already processed this node if it already has a context ptr */
+       if ((dnode = dm_tree_find_node_by_uuid(dtree, dlid)) &&
+           dm_tree_node_get_context(dnode))
+               return 1;
+
+       if (!(lvlayer = dm_pool_alloc(dm->mem, sizeof(*lvlayer)))) {
+               log_error("_add_new_lv_to_dtree: pool alloc failed for %s %s.",
+                         lv->name, layer);
+               return 0;
+       }
+
+       lvlayer->lv = lv;
+
+       /*
+        * Add LV to dtree.
+        * If we're working with precommitted metadata, clear any
+        * existing inactive table left behind.
+        * Major/minor settings only apply to the visible layer.
+        */
+       if (!(dnode = dm_tree_add_new_dev_with_udev_flags(dtree, name, dlid,
+                                            layer ? UINT32_C(0) : (uint32_t) lv->major,
+                                            layer ? UINT32_C(0) : (uint32_t) lv->minor,
+                                            _read_only_lv(lv),
+                                            (lv->vg->status & PRECOMMITTED) ? 1 : 0,
+                                            lvlayer,
+                                            _get_udev_flags(dm, lv, layer))))
+               return_0;
+
+       /* Store existing name so we can do rename later */
+       lvlayer->old_name = dm_tree_node_get_name(dnode);
+
+       /* Create table */
+       dm->pvmove_mirror_count = 0u;
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (!_add_segment_to_dtree(dm, dtree, dnode, seg, layer))
+                       return_0;
+               /* These aren't real segments in the LVM2 metadata */
+               if (lv_is_origin(lv) && !layer)
+                       break;
+               if (lv_is_cow(lv) && !layer)
+                       break;
+               if (max_stripe_size < seg->stripe_size * seg->area_count)
+                       max_stripe_size = seg->stripe_size * seg->area_count;
+       }
+
+       if (read_ahead == DM_READ_AHEAD_AUTO) {
+               /* we need RA at least twice a whole stripe - see the comment in md/raid0.c */
+               read_ahead = max_stripe_size * 2;
+               if (!read_ahead)
+                       lv_calculate_readahead(lv, &read_ahead);
+               read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
+       }
+
+       dm_tree_node_set_read_ahead(dnode, read_ahead, read_ahead_flags);
+
+       return 1;
+}
+
+/* FIXME: symlinks should be created/destroyed at the same time
+ * as the kernel devices but we can't do that from within libdevmapper
+ * at present so we must walk the tree twice instead. */
+
+/*
+ * Create LV symlinks for children of supplied root node.
+ */
+static int _create_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root)
+{
+       void *handle = NULL;
+       struct dm_tree_node *child;
+       struct lv_layer *lvlayer;
+       char *old_vgname, *old_lvname, *old_layer;
+       char *new_vgname, *new_lvname, *new_layer;
+       const char *name;
+       int r = 1;
+
+       while ((child = dm_tree_next_child(&handle, root, 0))) {
+               if (!(lvlayer = dm_tree_node_get_context(child)))
+                       continue;
+
+               /* Detect rename */
+               name = dm_tree_node_get_name(child);
+
+               if (name && lvlayer->old_name && *lvlayer->old_name && strcmp(name, lvlayer->old_name)) {
+                       if (!dm_split_lvm_name(dm->mem, lvlayer->old_name, &old_vgname, &old_lvname, &old_layer)) {
+                               log_error("_create_lv_symlinks: Couldn't split up old device name %s", lvlayer->old_name);
+                               return 0;
+                       }
+                       if (!dm_split_lvm_name(dm->mem, name, &new_vgname, &new_lvname, &new_layer)) {
+                               log_error("_create_lv_symlinks: Couldn't split up new device name %s", name);
+                               return 0;
+                       }
+                       if (!fs_rename_lv(lvlayer->lv, name, old_vgname, old_lvname))
+                               r = 0;
+                       continue;
+               }
+               if (lv_is_visible(lvlayer->lv)) {
+                       if (!_dev_manager_lv_mknodes(lvlayer->lv))
+                               r = 0;
+                       continue;
+               }
+               if (!_dev_manager_lv_rmnodes(lvlayer->lv))
+                       r = 0;
+       }
+
+       return r;
+}
+
+/*
+ * Remove LV symlinks for children of supplied root node.
+ */
+static int _remove_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root)
+{
+       void *handle = NULL;
+       struct dm_tree_node *child;
+       char *vgname, *lvname, *layer;
+       int r = 1;
+
+       while ((child = dm_tree_next_child(&handle, root, 0))) {
+               if (!dm_split_lvm_name(dm->mem, dm_tree_node_get_name(child), &vgname, &lvname, &layer)) {
+                       r = 0;
+                       continue;
+               }
+
+               if (!*vgname)
+                       continue;
+
+               /* only top level layer has symlinks */
+               if (*layer)
+                       continue;
+
+               fs_del_lv_byname(dm->cmd->dev_dir, vgname, lvname,
+                                dm->cmd->current_settings.udev_rules);
+       }
+
+       return r;
+}
+
+static int _clean_tree(struct dev_manager *dm, struct dm_tree_node *root, char *non_toplevel_tree_dlid)
+{
+       void *handle = NULL;
+       struct dm_tree_node *child;
+       char *vgname, *lvname, *layer;
+       const char *name, *uuid;
+       int r;
+
+       while ((child = dm_tree_next_child(&handle, root, 0))) {
+               if (!(name = dm_tree_node_get_name(child)))
+                       continue;
+
+               if (!(uuid = dm_tree_node_get_uuid(child)))
+                       continue;
+
+               if (!dm_split_lvm_name(dm->mem, name, &vgname, &lvname, &layer)) {
+                       log_error("_clean_tree: Couldn't split up device name %s.", name);
+                       return 0;
+               }
+
+               /* Not meant to be top level? */
+               if (!*layer)
+                       continue;
+
+               /* If operation was performed on a partial tree, don't remove it */
+               if (non_toplevel_tree_dlid && !strcmp(non_toplevel_tree_dlid, uuid))
+                       continue;
+
+               dm_tree_set_cookie(root, 0);
+               r = dm_tree_deactivate_children(root, uuid, strlen(uuid));
+               if (!dm_udev_wait(dm_tree_get_cookie(root)))
+                       stack;
+
+               if (!r)
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _tree_action(struct dev_manager *dm, struct logical_volume *lv,
+                       unsigned origin_only, action_t action)
+{
+       struct dm_tree *dtree;
+       struct dm_tree_node *root;
+       char *dlid;
+       int r = 0;
+
+       if (!(dtree = _create_partial_dtree(dm, lv, origin_only)))
+               return_0;
+
+       if (!(root = dm_tree_find_node(dtree, 0, 0))) {
+               log_error("Lost dependency tree root node");
+               goto out;
+       }
+
+       if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, origin_only ? "real" : NULL)))
+               goto_out;
+
+       /* Only process nodes with uuid of "LVM-" plus VG id. */
+       switch(action) {
+       case CLEAN:
+               /* Deactivate any unused non-toplevel nodes */
+               if (!_clean_tree(dm, root, origin_only ? dlid : NULL))
+                       goto_out;
+               break;
+       case DEACTIVATE:
+               /* Deactivate LV and all devices it references that nothing else has open. */
+               dm_tree_set_cookie(root, 0);
+               r = dm_tree_deactivate_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1);
+               if (!dm_udev_wait(dm_tree_get_cookie(root)))
+                       stack;
+               if (!r)
+                       goto_out;
+               if (!_remove_lv_symlinks(dm, root))
+                       log_error("Failed to remove all device symlinks associated with %s.", lv->name);
+               break;
+       case SUSPEND:
+               dm_tree_skip_lockfs(root);
+               if (!dm->flush_required && (lv->status & MIRRORED) && !(lv->status & PVMOVE))
+                       dm_tree_use_no_flush_suspend(root);
+       case SUSPEND_WITH_LOCKFS:
+               if (!dm_tree_suspend_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1))
+                       goto_out;
+               break;
+       case PRELOAD:
+       case ACTIVATE:
+               /* Add all required new devices to tree */
+               if (!_add_new_lv_to_dtree(dm, dtree, lv, origin_only ? "real" : NULL))
+                       goto_out;
+
+               /* Preload any devices required before any suspensions */
+               dm_tree_set_cookie(root, 0);
+               r = dm_tree_preload_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1);
+               if (!dm_udev_wait(dm_tree_get_cookie(root)))
+                       stack;
+               if (!r)
+                       goto_out;
+
+               if (dm_tree_node_size_changed(root))
+                       dm->flush_required = 1;
+
+               if (action == ACTIVATE) {
+                       dm_tree_set_cookie(root, 0);
+                       r = dm_tree_activate_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1);
+                       if (!dm_udev_wait(dm_tree_get_cookie(root)))
+                               stack;
+                       if (!r)
+                               goto_out;
+                       if (!_create_lv_symlinks(dm, root)) {
+                               log_error("Failed to create symlinks for %s.", lv->name);
+                               goto out;
+                       }
+               }
+
+               break;
+       default:
+               log_error("_tree_action: Action %u not supported.", action);
+               goto out;
+       }
+
+       r = 1;
+
+out:
+       dm_tree_free(dtree);
+
+       return r;
+}
+
+/* origin_only may only be set if we are resuming (not activating) an origin LV */
+int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv, unsigned origin_only)
+{
+       if (!_tree_action(dm, lv, origin_only, ACTIVATE))
+               return_0;
+
+       return _tree_action(dm, lv, origin_only, CLEAN);
+}
+
+/* origin_only may only be set if we are resuming (not activating) an origin LV */
+int dev_manager_preload(struct dev_manager *dm, struct logical_volume *lv,
+                       unsigned origin_only, int *flush_required)
+{
+       /* FIXME Update the pvmove implementation! */
+       if ((lv->status & PVMOVE) || (lv->status & LOCKED))
+               return 1;
+
+       if (!_tree_action(dm, lv, origin_only, PRELOAD))
+               return 0;
+
+       *flush_required = dm->flush_required;
+
+       return 1;
+}
+
+int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv)
+{
+       int r;
+
+       r = _tree_action(dm, lv, 0, DEACTIVATE);
+
+       return r;
+}
+
+int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv,
+                       unsigned origin_only, int lockfs, int flush_required)
+{
+       dm->flush_required = flush_required;
+
+       return _tree_action(dm, lv, origin_only, lockfs ? SUSPEND_WITH_LOCKFS : SUSPEND);
+}
+
+/*
+ * Does device use VG somewhere in its construction?
+ * Returns 1 if uncertain.
+ */
+int dev_manager_device_uses_vg(struct device *dev,
+                              struct volume_group *vg)
+{
+       struct dm_tree *dtree;
+       struct dm_tree_node *root;
+       char dlid[sizeof(UUID_PREFIX) + sizeof(struct id) - 1] __attribute__((aligned(8)));
+       int r = 1;
+
+       if (!(dtree = dm_tree_create())) {
+               log_error("partial dtree creation failed");
+               return r;
+       }
+
+       if (!dm_tree_add_dev(dtree, (uint32_t) MAJOR(dev->dev), (uint32_t) MINOR(dev->dev))) {
+               log_error("Failed to add device %s (%" PRIu32 ":%" PRIu32") to dtree",
+                         dev_name(dev), (uint32_t) MAJOR(dev->dev), (uint32_t) MINOR(dev->dev));
+               goto out;
+       }
+
+       memcpy(dlid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1);
+       memcpy(dlid + sizeof(UUID_PREFIX) - 1, &vg->id.uuid[0], sizeof(vg->id));
+
+       if (!(root = dm_tree_find_node(dtree, 0, 0))) {
+               log_error("Lost dependency tree root node");
+               goto out;
+       }
+
+       if (dm_tree_children_use_uuid(root, dlid, sizeof(UUID_PREFIX) + sizeof(vg->id) - 1))
+               goto_out;
+
+       r = 0;
+
+out:
+       dm_tree_free(dtree);
+       return r;
+}
diff --git a/lib/activate/dev_manager.h b/lib/activate/dev_manager.h
new file mode 100644 (file)
index 0000000..b0bb275
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_DEV_MANAGER_H
+#define _LVM_DEV_MANAGER_H
+
+#include "metadata-exported.h"
+
+struct logical_volume;
+struct volume_group;
+struct cmd_context;
+struct dev_manager;
+struct dm_info;
+struct device;
+
+/*
+ * Constructor and destructor.
+ */
+struct dev_manager *dev_manager_create(struct cmd_context *cmd,
+                                      const char *vg_name);
+void dev_manager_destroy(struct dev_manager *dm);
+void dev_manager_release(void);
+void dev_manager_exit(void);
+
+/*
+ * The device handler is responsible for creating all the layered
+ * dm devices, and ensuring that all constraints are maintained
+ * (eg, an origin is created before its snapshot, but is not
+ * unsuspended until the snapshot is also created.)
+ */
+int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
+                    const char *layer,
+                    int with_open_count, int with_read_ahead,
+                    struct dm_info *info, uint32_t *read_ahead);
+int dev_manager_snapshot_percent(struct dev_manager *dm,
+                                const struct logical_volume *lv,
+                                percent_t *percent);
+int dev_manager_mirror_percent(struct dev_manager *dm,
+                              const struct logical_volume *lv, int wait,
+                              percent_t *percent, uint32_t *event_nr);
+int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv,
+                       unsigned origin_only, int lockfs, int flush_required);
+int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv, unsigned origin_only);
+int dev_manager_preload(struct dev_manager *dm, struct logical_volume *lv,
+                       unsigned origin_only, int *flush_required);
+int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv);
+int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv) __attribute__((nonnull(1, 2)));
+
+int dev_manager_mknodes(const struct logical_volume *lv);
+
+/*
+ * Put the desired changes into effect.
+ */
+int dev_manager_execute(struct dev_manager *dm);
+
+int dev_manager_device_uses_vg(struct device *dev,
+                              struct volume_group *vg);
+
+#endif
diff --git a/lib/activate/fs.c b/lib/activate/fs.c
new file mode 100644 (file)
index 0000000..1523115
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "fs.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "memlock.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+
+static int _mk_dir(const char *dev_dir, const char *vg_name)
+{
+       char vg_path[PATH_MAX];
+       mode_t old_umask;
+
+       if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
+                        dev_dir, vg_name) == -1) {
+               log_error("Couldn't construct name of volume "
+                         "group directory.");
+               return 0;
+       }
+
+       if (dir_exists(vg_path))
+               return 1;
+
+       log_very_verbose("Creating directory %s", vg_path);
+
+       (void) dm_prepare_selinux_context(vg_path, S_IFDIR);
+       old_umask = umask(DM_DEV_DIR_UMASK);
+       if (mkdir(vg_path, 0777)) {
+               log_sys_error("mkdir", vg_path);
+               umask(old_umask);
+               (void) dm_prepare_selinux_context(NULL, 0);
+               return 0;
+       }
+       umask(old_umask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       return 1;
+}
+
+static int _rm_dir(const char *dev_dir, const char *vg_name)
+{
+       char vg_path[PATH_MAX];
+
+       if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
+                        dev_dir, vg_name) == -1) {
+               log_error("Couldn't construct name of volume "
+                         "group directory.");
+               return 0;
+       }
+
+       if (dir_exists(vg_path) && is_empty_dir(vg_path)) {
+               log_very_verbose("Removing directory %s", vg_path);
+               rmdir(vg_path);
+       }
+
+       return 1;
+}
+
+static void _rm_blks(const char *dir)
+{
+       const char *name;
+       char path[PATH_MAX];
+       struct dirent *dirent;
+       struct stat buf;
+       DIR *d;
+
+       if (!(d = opendir(dir))) {
+               log_sys_error("opendir", dir);
+               return;
+       }
+
+       while ((dirent = readdir(d))) {
+               name = dirent->d_name;
+
+               if (!strcmp(name, ".") || !strcmp(name, ".."))
+                       continue;
+
+               if (dm_snprintf(path, sizeof(path), "%s/%s", dir, name) == -1) {
+                       log_error("Couldn't create path for %s", name);
+                       continue;
+               }
+
+               if (!lstat(path, &buf)) {
+                       if (!S_ISBLK(buf.st_mode))
+                               continue;
+                       log_very_verbose("Removing %s", path);
+                       if (unlink(path) < 0)
+                               log_sys_error("unlink", path);
+               }
+       }
+
+       if (closedir(d))
+               log_sys_error("closedir", dir);
+}
+
+static int _mk_link(const char *dev_dir, const char *vg_name,
+                   const char *lv_name, const char *dev, int check_udev)
+{
+       char lv_path[PATH_MAX], link_path[PATH_MAX], lvm1_group_path[PATH_MAX];
+       char vg_path[PATH_MAX];
+       struct stat buf, buf_lp;
+
+       if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
+                        dev_dir, vg_name) == -1) {
+               log_error("Couldn't create path for volume group dir %s",
+                         vg_name);
+               return 0;
+       }
+
+       if (dm_snprintf(lv_path, sizeof(lv_path), "%s/%s", vg_path,
+                        lv_name) == -1) {
+               log_error("Couldn't create source pathname for "
+                         "logical volume link %s", lv_name);
+               return 0;
+       }
+
+       if (dm_snprintf(link_path, sizeof(link_path), "%s/%s",
+                        dm_dir(), dev) == -1) {
+               log_error("Couldn't create destination pathname for "
+                         "logical volume link for %s", lv_name);
+               return 0;
+       }
+
+       if (dm_snprintf(lvm1_group_path, sizeof(lvm1_group_path), "%s/group",
+                        vg_path) == -1) {
+               log_error("Couldn't create pathname for LVM1 group file for %s",
+                         vg_name);
+               return 0;
+       }
+
+       /* To reach this point, the VG must have been locked.
+        * As locking fails if the VG is active under LVM1, it's
+        * now safe to remove any LVM1 devices we find here
+        * (as well as any existing LVM2 symlink). */
+       if (!lstat(lvm1_group_path, &buf)) {
+               if (!S_ISCHR(buf.st_mode)) {
+                       log_error("Non-LVM1 character device found at %s",
+                                 lvm1_group_path);
+               } else {
+                       _rm_blks(vg_path);
+
+                       log_very_verbose("Removing %s", lvm1_group_path);
+                       if (unlink(lvm1_group_path) < 0)
+                               log_sys_error("unlink", lvm1_group_path);
+               }
+       }
+
+       if (!lstat(lv_path, &buf)) {
+               if (!S_ISLNK(buf.st_mode) && !S_ISBLK(buf.st_mode)) {
+                       log_error("Symbolic link %s not created: file exists",
+                                 link_path);
+                       return 0;
+               }
+
+               if (dm_udev_get_sync_support() && udev_checking() && check_udev) {
+                       /* Check udev created the correct link. */
+                       if (!stat(link_path, &buf_lp) &&
+                           !stat(lv_path, &buf)) {
+                               if (buf_lp.st_rdev == buf.st_rdev)
+                                       return 1;
+                               else
+                                       log_warn("Symlink %s that should have been "
+                                                "created by udev does not have "
+                                                "correct target. Falling back to "
+                                                "direct link creation", lv_path);
+                       } else
+                               log_warn("Symlink %s that should have been "
+                                        "created by udev could not be checked "
+                                        "for its correctness. Falling back to "
+                                        "direct link creation.", lv_path);
+
+               }
+
+               log_very_verbose("Removing %s", lv_path);
+               if (unlink(lv_path) < 0) {
+                       log_sys_error("unlink", lv_path);
+                       return 0;
+               }
+       } else if (dm_udev_get_sync_support() && udev_checking() && check_udev)
+               log_warn("The link %s should had been created by udev "
+                         "but it was not found. Falling back to "
+                         "direct link creation.", lv_path);
+
+       log_very_verbose("Linking %s -> %s", lv_path, link_path);
+
+       (void) dm_prepare_selinux_context(lv_path, S_IFLNK);
+       if (symlink(link_path, lv_path) < 0) {
+               log_sys_error("symlink", lv_path);
+               (void) dm_prepare_selinux_context(NULL, 0);
+               return 0;
+       }
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       return 1;
+}
+
+static int _rm_link(const char *dev_dir, const char *vg_name,
+                   const char *lv_name, int check_udev)
+{
+       struct stat buf;
+       char lv_path[PATH_MAX];
+
+       if (dm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s",
+                        dev_dir, vg_name, lv_name) == -1) {
+               log_error("Couldn't determine link pathname.");
+               return 0;
+       }
+
+       if (lstat(lv_path, &buf) && errno == ENOENT)
+               return 1;
+       else if (dm_udev_get_sync_support() && udev_checking() && check_udev)
+               log_warn("The link %s should have been removed by udev "
+                        "but it is still present. Falling back to "
+                        "direct link removal.", lv_path);
+
+       if (!S_ISLNK(buf.st_mode)) {
+               log_error("%s not symbolic link - not removing", lv_path);
+               return 0;
+       }
+
+       log_very_verbose("Removing link %s", lv_path);
+       if (unlink(lv_path) < 0) {
+               log_sys_error("unlink", lv_path);
+               return 0;
+       }
+
+       return 1;
+}
+
+typedef enum {
+       FS_ADD,
+       FS_DEL,
+       FS_RENAME
+} fs_op_t;
+
+static int _do_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
+                    const char *lv_name, const char *dev,
+                    const char *old_lv_name, int check_udev)
+{
+       switch (type) {
+       case FS_ADD:
+               if (!_mk_dir(dev_dir, vg_name) ||
+                   !_mk_link(dev_dir, vg_name, lv_name, dev, check_udev))
+                       return_0;
+               break;
+       case FS_DEL:
+               if (!_rm_link(dev_dir, vg_name, lv_name, check_udev) ||
+                   !_rm_dir(dev_dir, vg_name))
+                       return_0;
+               break;
+               /* FIXME Use rename() */
+       case FS_RENAME:
+               if (old_lv_name && !_rm_link(dev_dir, vg_name, old_lv_name,
+                                            check_udev))
+                       stack;
+
+               if (!_mk_link(dev_dir, vg_name, lv_name, dev, check_udev))
+                       stack;
+       }
+
+       return 1;
+}
+
+static DM_LIST_INIT(_fs_ops);
+
+struct fs_op_parms {
+       struct dm_list list;
+       fs_op_t type;
+       int check_udev;
+       char *dev_dir;
+       char *vg_name;
+       char *lv_name;
+       char *dev;
+       char *old_lv_name;
+       char names[0];
+};
+
+static void _store_str(char **pos, char **ptr, const char *str)
+{
+       strcpy(*pos, str);
+       *ptr = *pos;
+       *pos += strlen(*ptr) + 1;
+}
+
+static int _stack_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
+                       const char *lv_name, const char *dev,
+                       const char *old_lv_name, int check_udev)
+{
+       struct fs_op_parms *fsp;
+       size_t len = strlen(dev_dir) + strlen(vg_name) + strlen(lv_name) +
+           strlen(dev) + strlen(old_lv_name) + 5;
+       char *pos;
+
+       if (!(fsp = dm_malloc(sizeof(*fsp) + len))) {
+               log_error("No space to stack fs operation");
+               return 0;
+       }
+
+       pos = fsp->names;
+       fsp->type = type;
+       fsp->check_udev = check_udev;
+
+       _store_str(&pos, &fsp->dev_dir, dev_dir);
+       _store_str(&pos, &fsp->vg_name, vg_name);
+       _store_str(&pos, &fsp->lv_name, lv_name);
+       _store_str(&pos, &fsp->dev, dev);
+       _store_str(&pos, &fsp->old_lv_name, old_lv_name);
+
+       dm_list_add(&_fs_ops, &fsp->list);
+
+       return 1;
+}
+
+static void _pop_fs_ops(void)
+{
+       struct dm_list *fsph, *fspht;
+       struct fs_op_parms *fsp;
+
+       dm_list_iterate_safe(fsph, fspht, &_fs_ops) {
+               fsp = dm_list_item(fsph, struct fs_op_parms);
+               _do_fs_op(fsp->type, fsp->dev_dir, fsp->vg_name, fsp->lv_name,
+                         fsp->dev, fsp->old_lv_name, fsp->check_udev);
+               dm_list_del(&fsp->list);
+               dm_free(fsp);
+       }
+}
+
+static int _fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
+                 const char *lv_name, const char *dev, const char *old_lv_name,
+                 int check_udev)
+{
+       if (memlock()) {
+               if (!_stack_fs_op(type, dev_dir, vg_name, lv_name, dev,
+                                 old_lv_name, check_udev))
+                       return_0;
+               return 1;
+       }
+
+       return _do_fs_op(type, dev_dir, vg_name, lv_name, dev,
+                        old_lv_name, check_udev);
+}
+
+int fs_add_lv(const struct logical_volume *lv, const char *dev)
+{
+       return _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
+                     dev, "", lv->vg->cmd->current_settings.udev_rules);
+}
+
+int fs_del_lv(const struct logical_volume *lv)
+{
+       return _fs_op(FS_DEL, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
+                     "", "", lv->vg->cmd->current_settings.udev_rules);
+}
+
+int fs_del_lv_byname(const char *dev_dir, const char *vg_name,
+                    const char *lv_name, int check_udev)
+{
+       return _fs_op(FS_DEL, dev_dir, vg_name, lv_name, "", "", check_udev);
+}
+
+int fs_rename_lv(struct logical_volume *lv, const char *dev, 
+               const char *old_vgname, const char *old_lvname)
+{
+       if (strcmp(old_vgname, lv->vg->name)) {
+               return
+                       (_fs_op(FS_DEL, lv->vg->cmd->dev_dir, old_vgname,
+                               old_lvname, "", "", lv->vg->cmd->current_settings.udev_rules) &&
+                        _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name,
+                               lv->name, dev, "", lv->vg->cmd->current_settings.udev_rules));
+       }
+       else 
+               return _fs_op(FS_RENAME, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
+                             dev, old_lvname, lv->vg->cmd->current_settings.udev_rules);
+}
+
+void fs_unlock(void)
+{
+       if (!memlock()) {
+               dm_lib_release();
+               _pop_fs_ops();
+       }
+}
diff --git a/lib/activate/fs.h b/lib/activate/fs.h
new file mode 100644 (file)
index 0000000..28b2c73
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FS_H
+#define _LVM_FS_H
+
+#include "metadata.h"
+
+/*
+ * These calls, private to the activate unit, set
+ * up the volume group directory in /dev and the
+ * symbolic links to the dm device.
+ */
+int fs_add_lv(const struct logical_volume *lv, const char *dev);
+int fs_del_lv(const struct logical_volume *lv);
+int fs_del_lv_byname(const char *dev_dir, const char *vg_name,
+                    const char *lv_name, int check_udev);
+int fs_rename_lv(struct logical_volume *lv, const char *dev, 
+                const char *old_vgname, const char *old_lvname);
+void fs_unlock(void);
+
+#endif
diff --git a/lib/activate/targets.h b/lib/activate/targets.h
new file mode 100644 (file)
index 0000000..ac68c5b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TARGETS_H
+#define _LVM_TARGETS_H
+
+struct dev_manager;
+struct lv_segment;
+
+int compose_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+                      char *params, size_t paramsize, int *pos,
+                      int start_area, int areas);
+
+int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+                   struct dm_tree_node *node, uint32_t start_area, uint32_t areas);
+
+int build_dev_string(struct dev_manager *dm, char *dlid, char *devbuf,
+                    size_t bufsize, const char *desc);
+
+#endif
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
new file mode 100644 (file)
index 0000000..d545563
--- /dev/null
@@ -0,0 +1,1444 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "lvmcache.h"
+#include "toolcontext.h"
+#include "dev-cache.h"
+#include "locking.h"
+#include "metadata.h"
+#include "filter.h"
+#include "filter-persistent.h"
+#include "memlock.h"
+#include "str_list.h"
+#include "format-text.h"
+#include "format_pool.h"
+#include "format1.h"
+
+static struct dm_hash_table *_pvid_hash = NULL;
+static struct dm_hash_table *_vgid_hash = NULL;
+static struct dm_hash_table *_vgname_hash = NULL;
+static struct dm_hash_table *_lock_hash = NULL;
+static DM_LIST_INIT(_vginfos);
+static int _scanning_in_progress = 0;
+static int _has_scanned = 0;
+static int _vgs_locked = 0;
+static int _vg_global_lock_held = 0;   /* Global lock held when cache wiped? */
+
+int lvmcache_init(void)
+{
+       /*
+        * FIXME add a proper lvmcache_locking_reset() that
+        * resets the cache so no previous locks are locked
+        */
+       _vgs_locked = 0;
+
+       dm_list_init(&_vginfos);
+
+       if (!(_vgname_hash = dm_hash_create(128)))
+               return 0;
+
+       if (!(_vgid_hash = dm_hash_create(128)))
+               return 0;
+
+       if (!(_pvid_hash = dm_hash_create(128)))
+               return 0;
+
+       if (!(_lock_hash = dm_hash_create(128)))
+               return 0;
+
+       /*
+        * Reinitialising the cache clears the internal record of
+        * which locks are held.  The global lock can be held during
+        * this operation so its state must be restored afterwards.
+        */
+       if (_vg_global_lock_held) {
+               lvmcache_lock_vgname(VG_GLOBAL, 0);
+               _vg_global_lock_held = 0;
+       }
+
+       return 1;
+}
+
+/* Volume Group metadata cache functions */
+static void _free_cached_vgmetadata(struct lvmcache_vginfo *vginfo)
+{
+       if (!vginfo || !vginfo->vgmetadata)
+               return;
+
+       dm_free(vginfo->vgmetadata);
+
+       vginfo->vgmetadata = NULL;
+
+       log_debug("Metadata cache: VG %s wiped.", vginfo->vgname);
+}
+
+/*
+ * Cache VG metadata against the vginfo with matching vgid.
+ */
+static void _store_metadata(struct volume_group *vg, unsigned precommitted)
+{
+       char uuid[64] __attribute__((aligned(8)));
+       struct lvmcache_vginfo *vginfo;
+       int size;
+
+       if (!(vginfo = vginfo_from_vgid((const char *)&vg->id))) {
+               stack;
+               return;
+       }
+
+       if (vginfo->vgmetadata)
+               _free_cached_vgmetadata(vginfo);
+
+       if (!(size = export_vg_to_buffer(vg, &vginfo->vgmetadata))) {
+               stack;
+               return;
+       }
+
+       vginfo->precommitted = precommitted;
+
+       if (!id_write_format((const struct id *)vginfo->vgid, uuid, sizeof(uuid))) {
+               stack;
+               return;
+       }
+
+       log_debug("Metadata cache: VG %s (%s) stored (%d bytes%s).",
+                 vginfo->vgname, uuid, size,
+                 precommitted ? ", precommitted" : "");
+}
+
+static void _update_cache_info_lock_state(struct lvmcache_info *info,
+                                         int locked,
+                                         int *cached_vgmetadata_valid)
+{
+       int was_locked = (info->status & CACHE_LOCKED) ? 1 : 0;
+
+       /*
+        * Cache becomes invalid whenever lock state changes unless
+        * exclusive VG_GLOBAL is held (i.e. while scanning).
+        */
+       if (!vgname_is_locked(VG_GLOBAL) && (was_locked != locked)) {
+               info->status |= CACHE_INVALID;
+               *cached_vgmetadata_valid = 0;
+       }
+
+       if (locked)
+               info->status |= CACHE_LOCKED;
+       else
+               info->status &= ~CACHE_LOCKED;
+}
+
+static void _update_cache_vginfo_lock_state(struct lvmcache_vginfo *vginfo,
+                                           int locked)
+{
+       struct lvmcache_info *info;
+       int cached_vgmetadata_valid = 1;
+
+       dm_list_iterate_items(info, &vginfo->infos)
+               _update_cache_info_lock_state(info, locked,
+                                             &cached_vgmetadata_valid);
+
+       if (!cached_vgmetadata_valid)
+               _free_cached_vgmetadata(vginfo);
+}
+
+static void _update_cache_lock_state(const char *vgname, int locked)
+{
+       struct lvmcache_vginfo *vginfo;
+
+       if (!(vginfo = vginfo_from_vgname(vgname, NULL)))
+               return;
+
+       _update_cache_vginfo_lock_state(vginfo, locked);
+}
+
+static void _drop_metadata(const char *vgname, int drop_precommitted)
+{
+       struct lvmcache_vginfo *vginfo;
+       struct lvmcache_info *info;
+
+       if (!(vginfo = vginfo_from_vgname(vgname, NULL)))
+               return;
+
+       /*
+        * Invalidate cached PV labels.
+        * If cached precommitted metadata exists that means we
+        * already invalidated the PV labels (before caching it)
+        * and we must not do it again.
+        */
+       if (!drop_precommitted && vginfo->precommitted && !vginfo->vgmetadata)
+               log_error(INTERNAL_ERROR "metadata commit (or revert) missing before "
+                         "dropping metadata from cache.");
+
+       if (drop_precommitted || !vginfo->precommitted)
+               dm_list_iterate_items(info, &vginfo->infos)
+                       info->status |= CACHE_INVALID;
+
+       _free_cached_vgmetadata(vginfo);
+}
+
+/*
+ * Remote node uses this to upgrade precommited metadata to commited state
+ * when receives vg_commit notification.
+ * (Note that devices can be suspended here, if so, precommited metadata are already read.)
+ */
+void lvmcache_commit_metadata(const char *vgname)
+{
+       struct lvmcache_vginfo *vginfo;
+
+       if (!(vginfo = vginfo_from_vgname(vgname, NULL)))
+               return;
+
+       if (vginfo->precommitted) {
+               log_debug("Precommitted metadata cache: VG %s upgraded to committed.",
+                         vginfo->vgname);
+               vginfo->precommitted = 0;
+       }
+}
+
+void lvmcache_drop_metadata(const char *vgname, int drop_precommitted)
+{
+       /* For VG_ORPHANS, we need to invalidate all labels on orphan PVs. */
+       if (!strcmp(vgname, VG_ORPHANS)) {
+               _drop_metadata(FMT_TEXT_ORPHAN_VG_NAME, 0);
+               _drop_metadata(FMT_LVM1_ORPHAN_VG_NAME, 0);
+               _drop_metadata(FMT_POOL_ORPHAN_VG_NAME, 0);
+
+               /* Indicate that PVs could now be missing from the cache */
+               init_full_scan_done(0);
+       } else if (!vgname_is_locked(VG_GLOBAL))
+               _drop_metadata(vgname, drop_precommitted);
+}
+
+/*
+ * Ensure vgname2 comes after vgname1 alphabetically.
+ * Orphan locks come last.
+ * VG_GLOBAL comes first.
+ */
+static int _vgname_order_correct(const char *vgname1, const char *vgname2)
+{
+       if (is_global_vg(vgname1))
+               return 1;
+
+       if (is_global_vg(vgname2))
+               return 0;
+
+       if (is_orphan_vg(vgname1))
+               return 0;
+
+       if (is_orphan_vg(vgname2))
+               return 1;
+
+       if (strcmp(vgname1, vgname2) < 0)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Ensure VG locks are acquired in alphabetical order.
+ */
+int lvmcache_verify_lock_order(const char *vgname)
+{
+       struct dm_hash_node *n;
+       const char *vgname2;
+
+       if (!_lock_hash)
+               return_0;
+
+       dm_hash_iterate(n, _lock_hash) {
+               if (!dm_hash_get_data(_lock_hash, n))
+                       return_0;
+
+               vgname2 = dm_hash_get_key(_lock_hash, n);
+
+               if (!_vgname_order_correct(vgname2, vgname)) {
+                       log_errno(EDEADLK, INTERNAL_ERROR "VG lock %s must "
+                                 "be requested before %s, not after.",
+                                 vgname, vgname2);
+                       return_0;
+               }
+       }
+
+       return 1;
+}
+
+void lvmcache_lock_vgname(const char *vgname, int read_only __attribute__((unused)))
+{
+       if (!_lock_hash && !lvmcache_init()) {
+               log_error("Internal cache initialisation failed");
+               return;
+       }
+
+       if (dm_hash_lookup(_lock_hash, vgname))
+               log_error(INTERNAL_ERROR "Nested locking attempted on VG %s.",
+                         vgname);
+
+       if (!dm_hash_insert(_lock_hash, vgname, (void *) 1))
+               log_error("Cache locking failure for %s", vgname);
+
+       _update_cache_lock_state(vgname, 1);
+
+       if (strcmp(vgname, VG_GLOBAL))
+               _vgs_locked++;
+}
+
+int vgname_is_locked(const char *vgname)
+{
+       if (!_lock_hash)
+               return 0;
+
+       return dm_hash_lookup(_lock_hash, is_orphan_vg(vgname) ? VG_ORPHANS : vgname) ? 1 : 0;
+}
+
+void lvmcache_unlock_vgname(const char *vgname)
+{
+       if (!dm_hash_lookup(_lock_hash, vgname))
+               log_error(INTERNAL_ERROR "Attempt to unlock unlocked VG %s.",
+                         vgname);
+
+       _update_cache_lock_state(vgname, 0);
+
+       dm_hash_remove(_lock_hash, vgname);
+
+       /* FIXME Do this per-VG */
+       if (strcmp(vgname, VG_GLOBAL) && !--_vgs_locked)
+               dev_close_all();
+}
+
+int vgs_locked(void)
+{
+       return _vgs_locked;
+}
+
+static void _vginfo_attach_info(struct lvmcache_vginfo *vginfo,
+                               struct lvmcache_info *info)
+{
+       if (!vginfo)
+               return;
+
+       info->vginfo = vginfo;
+       dm_list_add(&vginfo->infos, &info->list);
+}
+
+static void _vginfo_detach_info(struct lvmcache_info *info)
+{
+       if (!dm_list_empty(&info->list)) {
+               dm_list_del(&info->list);
+               dm_list_init(&info->list);
+       }
+
+       info->vginfo = NULL;
+}
+
+/* If vgid supplied, require a match. */
+struct lvmcache_vginfo *vginfo_from_vgname(const char *vgname, const char *vgid)
+{
+       struct lvmcache_vginfo *vginfo;
+
+       if (!vgname)
+               return vginfo_from_vgid(vgid);
+
+       if (!_vgname_hash)
+               return NULL;
+
+       if (!(vginfo = dm_hash_lookup(_vgname_hash, vgname)))
+               return NULL;
+
+       if (vgid)
+               do
+                       if (!strncmp(vgid, vginfo->vgid, ID_LEN))
+                               return vginfo;
+               while ((vginfo = vginfo->next));
+
+       return vginfo;
+}
+
+const struct format_type *fmt_from_vgname(const char *vgname, const char *vgid, unsigned revalidate_labels)
+{
+       struct lvmcache_vginfo *vginfo;
+       struct lvmcache_info *info;
+       struct label *label;
+       struct dm_list *devh, *tmp;
+       struct dm_list devs;
+       struct device_list *devl;
+       char vgid_found[ID_LEN + 1] __attribute__((aligned(8)));
+
+       if (!(vginfo = vginfo_from_vgname(vgname, vgid)))
+               return NULL;
+
+       /*
+        * If this function is called repeatedly, only the first one needs to revalidate.
+        */
+       if (!revalidate_labels)
+               goto out;
+
+       /*
+        * This function is normally called before reading metadata so
+        * we check cached labels here. Unfortunately vginfo is volatile.
+        */
+       dm_list_init(&devs);
+       dm_list_iterate_items(info, &vginfo->infos) {
+               if (!(devl = dm_malloc(sizeof(*devl)))) {
+                       log_error("device_list element allocation failed");
+                       return NULL;
+               }
+               devl->dev = info->dev;
+               dm_list_add(&devs, &devl->list);
+       }
+
+       memcpy(vgid_found, vginfo->vgid, sizeof(vgid_found));
+
+       dm_list_iterate_safe(devh, tmp, &devs) {
+               devl = dm_list_item(devh, struct device_list);
+               label_read(devl->dev, &label, UINT64_C(0));
+               dm_list_del(&devl->list);
+               dm_free(devl);
+       }
+
+       /* If vginfo changed, caller needs to rescan */
+       if (!(vginfo = vginfo_from_vgname(vgname, vgid_found)) ||
+           strncmp(vginfo->vgid, vgid_found, ID_LEN))
+               return NULL;
+
+out:
+       return vginfo->fmt;
+}
+
+struct lvmcache_vginfo *vginfo_from_vgid(const char *vgid)
+{
+       struct lvmcache_vginfo *vginfo;
+       char id[ID_LEN + 1] __attribute__((aligned(8)));
+
+       if (!_vgid_hash || !vgid)
+               return NULL;
+
+       /* vgid not necessarily NULL-terminated */
+       strncpy(&id[0], vgid, ID_LEN);
+       id[ID_LEN] = '\0';
+
+       if (!(vginfo = dm_hash_lookup(_vgid_hash, id)))
+               return NULL;
+
+       return vginfo;
+}
+
+const char *vgname_from_vgid(struct dm_pool *mem, const char *vgid)
+{
+       struct lvmcache_vginfo *vginfo;
+       const char *vgname = NULL;
+
+       if ((vginfo = vginfo_from_vgid(vgid)))
+               vgname = vginfo->vgname;
+
+       if (mem && vgname)
+               return dm_pool_strdup(mem, vgname);
+
+       return vgname;
+}
+
+static int _info_is_valid(struct lvmcache_info *info)
+{
+       if (info->status & CACHE_INVALID)
+               return 0;
+
+       /*
+        * The caller must hold the VG lock to manipulate metadata.
+        * In a cluster, remote nodes sometimes read metadata in the
+        * knowledge that the controlling node is holding the lock.
+        * So if the VG appears to be unlocked here, it should be safe
+        * to use the cached value.
+        */
+       if (info->vginfo && !vgname_is_locked(info->vginfo->vgname))
+               return 1;
+
+       if (!(info->status & CACHE_LOCKED))
+               return 0;
+
+       return 1;
+}
+
+static int _vginfo_is_valid(struct lvmcache_vginfo *vginfo)
+{
+       struct lvmcache_info *info;
+
+       /* Invalid if any info is invalid */
+       dm_list_iterate_items(info, &vginfo->infos)
+               if (!_info_is_valid(info))
+                       return 0;
+
+       return 1;
+}
+
+/* vginfo is invalid if it does not contain at least one valid info */
+static int _vginfo_is_invalid(struct lvmcache_vginfo *vginfo)
+{
+       struct lvmcache_info *info;
+
+       dm_list_iterate_items(info, &vginfo->infos)
+               if (_info_is_valid(info))
+                       return 0;
+
+       return 1;
+}
+
+/*
+ * If valid_only is set, data will only be returned if the cached data is
+ * known still to be valid.
+ */
+struct lvmcache_info *info_from_pvid(const char *pvid, int valid_only)
+{
+       struct lvmcache_info *info;
+       char id[ID_LEN + 1] __attribute__((aligned(8)));
+
+       if (!_pvid_hash || !pvid)
+               return NULL;
+
+       strncpy(&id[0], pvid, ID_LEN);
+       id[ID_LEN] = '\0';
+
+       if (!(info = dm_hash_lookup(_pvid_hash, id)))
+               return NULL;
+
+       if (valid_only && !_info_is_valid(info))
+               return NULL;
+
+       return info;
+}
+
+char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid)
+{
+       struct lvmcache_info *info;
+       char *vgname;
+
+       if (!device_from_pvid(cmd, (const struct id *)pvid, NULL)) {
+               log_error("Couldn't find device with uuid %s.", pvid);
+               return NULL;
+       }
+
+       info = info_from_pvid(pvid, 0);
+       if (!info)
+               return_NULL;
+
+       if (!(vgname = dm_pool_strdup(cmd->mem, info->vginfo->vgname))) {
+               log_errno(ENOMEM, "vgname allocation failed");
+               return NULL;
+       }
+       return vgname;
+}
+
+static void _rescan_entry(struct lvmcache_info *info)
+{
+       struct label *label;
+
+       if (info->status & CACHE_INVALID)
+               label_read(info->dev, &label, UINT64_C(0));
+}
+
+static int _scan_invalid(void)
+{
+       dm_hash_iter(_pvid_hash, (dm_hash_iterate_fn) _rescan_entry);
+
+       return 1;
+}
+
+int lvmcache_label_scan(struct cmd_context *cmd, int full_scan)
+{
+       struct label *label;
+       struct dev_iter *iter;
+       struct device *dev;
+       struct format_type *fmt;
+
+       int r = 0;
+
+       /* Avoid recursion when a PVID can't be found! */
+       if (_scanning_in_progress)
+               return 0;
+
+       _scanning_in_progress = 1;
+
+       if (!_vgname_hash && !lvmcache_init()) {
+               log_error("Internal cache initialisation failed");
+               goto out;
+       }
+
+       if (_has_scanned && !full_scan) {
+               r = _scan_invalid();
+               goto out;
+       }
+
+       if (full_scan == 2 && !cmd->filter->use_count && !refresh_filters(cmd)) {
+               log_error("refresh filters failed");
+               goto out;
+       }
+
+       if (!(iter = dev_iter_create(cmd->filter, (full_scan == 2) ? 1 : 0))) {
+               log_error("dev_iter creation failed");
+               goto out;
+       }
+
+       while ((dev = dev_iter_get(iter)))
+               label_read(dev, &label, UINT64_C(0));
+
+       dev_iter_destroy(iter);
+
+       _has_scanned = 1;
+
+       /* Perform any format-specific scanning e.g. text files */
+       if (cmd->independent_metadata_areas)
+               dm_list_iterate_items(fmt, &cmd->formats)
+                       if (fmt->ops->scan && !fmt->ops->scan(fmt, NULL))
+                               goto out;
+
+       /*
+        * If we are a long-lived process, write out the updated persistent
+        * device cache for the benefit of short-lived processes.
+        */
+       if (full_scan == 2 && cmd->is_long_lived && cmd->dump_filter)
+               persistent_filter_dump(cmd->filter, 0);
+
+       r = 1;
+
+      out:
+       _scanning_in_progress = 0;
+
+       return r;
+}
+
+struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted)
+{
+       struct lvmcache_vginfo *vginfo;
+       struct volume_group *vg;
+       struct format_instance *fid;
+
+       if (!vgid || !(vginfo = vginfo_from_vgid(vgid)) || !vginfo->vgmetadata)
+               return NULL;
+
+       if (!_vginfo_is_valid(vginfo))
+               return NULL;
+
+       /*
+        * Don't return cached data if either:
+        * (i)  precommitted metadata is requested but we don't have it cached
+        *      - caller should read it off disk;
+        * (ii) live metadata is requested but we have precommitted metadata cached
+        *      and no devices are suspended so caller may read it off disk.
+        *
+        * If live metadata is requested but we have precommitted metadata cached
+        * and devices are suspended, we assume this precommitted metadata has
+        * already been preloaded and committed so it's OK to return it as live.
+        * Note that we do not clear the PRECOMMITTED flag.
+        */
+       if ((precommitted && !vginfo->precommitted) ||
+           (!precommitted && vginfo->precommitted && !memlock()))
+               return NULL;
+
+       if (!(fid = vginfo->fmt->ops->create_instance(vginfo->fmt,
+                                                     vginfo->vgname,
+                                                     vgid, NULL)))
+               return_NULL;
+
+       if (!(vg = import_vg_from_buffer(vginfo->vgmetadata, fid))) {
+               _free_cached_vgmetadata(vginfo);
+               free_vg(vg);
+               return_NULL;
+       }
+
+       log_debug("Using cached %smetadata for VG %s.",
+                 vginfo->precommitted ? "pre-committed" : "", vginfo->vgname);
+
+       return vg;
+}
+
+struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
+                                  int include_internal)
+{
+       struct dm_list *vgids;
+       struct lvmcache_vginfo *vginfo;
+
+       lvmcache_label_scan(cmd, 0);
+
+       if (!(vgids = str_list_create(cmd->mem))) {
+               log_error("vgids list allocation failed");
+               return NULL;
+       }
+
+       dm_list_iterate_items(vginfo, &_vginfos) {
+               if (!include_internal && is_orphan_vg(vginfo->vgname))
+                       continue;
+
+               if (!str_list_add(cmd->mem, vgids,
+                                 dm_pool_strdup(cmd->mem, vginfo->vgid))) {
+                       log_error("strlist allocation failed");
+                       return NULL;
+               }
+       }
+
+       return vgids;
+}
+
+struct dm_list *lvmcache_get_vgnames(struct cmd_context *cmd,
+                                    int include_internal)
+{
+       struct dm_list *vgnames;
+       struct lvmcache_vginfo *vginfo;
+
+       lvmcache_label_scan(cmd, 0);
+
+       if (!(vgnames = str_list_create(cmd->mem))) {
+               log_errno(ENOMEM, "vgnames list allocation failed");
+               return NULL;
+       }
+
+       dm_list_iterate_items(vginfo, &_vginfos) {
+               if (!include_internal && is_orphan_vg(vginfo->vgname))
+                       continue;
+
+               if (!str_list_add(cmd->mem, vgnames,
+                                 dm_pool_strdup(cmd->mem, vginfo->vgname))) {
+                       log_errno(ENOMEM, "strlist allocation failed");
+                       return NULL;
+               }
+       }
+
+       return vgnames;
+}
+
+struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
+                               const char *vgid)
+{
+       struct dm_list *pvids;
+       struct lvmcache_vginfo *vginfo;
+       struct lvmcache_info *info;
+
+       if (!(pvids = str_list_create(cmd->mem))) {
+               log_error("pvids list allocation failed");
+               return NULL;
+       }
+
+       if (!(vginfo = vginfo_from_vgname(vgname, vgid)))
+               return pvids;
+
+       dm_list_iterate_items(info, &vginfo->infos) {
+               if (!str_list_add(cmd->mem, pvids,
+                                 dm_pool_strdup(cmd->mem, info->dev->pvid))) {
+                       log_error("strlist allocation failed");
+                       return NULL;
+               }
+       }
+
+       return pvids;
+}
+
+struct device *device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
+                               unsigned *scan_done_once)
+{
+       struct label *label;
+       struct lvmcache_info *info;
+
+       /* Already cached ? */
+       if ((info = info_from_pvid((const char *) pvid, 0))) {
+               if (label_read(info->dev, &label, UINT64_C(0))) {
+                       info = (struct lvmcache_info *) label->info;
+                       if (id_equal(pvid, (struct id *) &info->dev->pvid))
+                               return info->dev;
+               }
+       }
+
+       lvmcache_label_scan(cmd, 0);
+
+       /* Try again */
+       if ((info = info_from_pvid((const char *) pvid, 0))) {
+               if (label_read(info->dev, &label, UINT64_C(0))) {
+                       info = (struct lvmcache_info *) label->info;
+                       if (id_equal(pvid, (struct id *) &info->dev->pvid))
+                               return info->dev;
+               }
+       }
+
+       if (memlock() || (scan_done_once && *scan_done_once))
+               return NULL;
+
+       lvmcache_label_scan(cmd, 2);
+       if (scan_done_once)
+               *scan_done_once = 1;
+
+       /* Try again */
+       if ((info = info_from_pvid((const char *) pvid, 0))) {
+               if (label_read(info->dev, &label, UINT64_C(0))) {
+                       info = (struct lvmcache_info *) label->info;
+                       if (id_equal(pvid, (struct id *) &info->dev->pvid))
+                               return info->dev;
+               }
+       }
+
+       return NULL;
+}
+
+const char *pvid_from_devname(struct cmd_context *cmd,
+                             const char *devname)
+{
+       struct device *dev;
+       struct label *label;
+
+       if (!(dev = dev_cache_get(devname, cmd->filter))) {
+               log_error("%s: Couldn't find device.  Check your filters?",
+                         devname);
+               return NULL;
+       }
+
+       if (!(label_read(dev, &label, UINT64_C(0))))
+               return NULL;
+
+       return dev->pvid;
+}
+
+
+static int _free_vginfo(struct lvmcache_vginfo *vginfo)
+{
+       struct lvmcache_vginfo *primary_vginfo, *vginfo2;
+       int r = 1;
+
+       _free_cached_vgmetadata(vginfo);
+
+       vginfo2 = primary_vginfo = vginfo_from_vgname(vginfo->vgname, NULL);
+
+       if (vginfo == primary_vginfo) {
+               dm_hash_remove(_vgname_hash, vginfo->vgname);
+               if (vginfo->next && !dm_hash_insert(_vgname_hash, vginfo->vgname,
+                                                   vginfo->next)) {
+                       log_error("_vgname_hash re-insertion for %s failed",
+                                 vginfo->vgname);
+                       r = 0;
+               }
+       } else do
+               if (vginfo2->next == vginfo) {
+                       vginfo2->next = vginfo->next;
+                       break;
+               }
+       while ((vginfo2 = primary_vginfo->next));
+
+       if (vginfo->vgname)
+               dm_free(vginfo->vgname);
+
+       if (vginfo->creation_host)
+               dm_free(vginfo->creation_host);
+
+       if (*vginfo->vgid && _vgid_hash &&
+           vginfo_from_vgid(vginfo->vgid) == vginfo)
+               dm_hash_remove(_vgid_hash, vginfo->vgid);
+
+       dm_list_del(&vginfo->list);
+
+       dm_free(vginfo);
+
+       return r;
+}
+
+/*
+ * vginfo must be info->vginfo unless info is NULL
+ */
+static int _drop_vginfo(struct lvmcache_info *info, struct lvmcache_vginfo *vginfo)
+{
+       if (info)
+               _vginfo_detach_info(info);
+
+       /* vginfo still referenced? */
+       if (!vginfo || is_orphan_vg(vginfo->vgname) ||
+           !dm_list_empty(&vginfo->infos))
+               return 1;
+
+       if (!_free_vginfo(vginfo))
+               return_0;
+
+       return 1;
+}
+
+/* Unused
+void lvmcache_del(struct lvmcache_info *info)
+{
+       if (info->dev->pvid[0] && _pvid_hash)
+               dm_hash_remove(_pvid_hash, info->dev->pvid);
+
+       _drop_vginfo(info, info->vginfo);
+
+       info->label->labeller->ops->destroy_label(info->label->labeller,
+                                               info->label);
+       dm_free(info);
+
+       return;
+} */
+
+static int _lvmcache_update_pvid(struct lvmcache_info *info, const char *pvid)
+{
+       /*
+        * Nothing to do if already stored with same pvid.
+        */
+       if (((dm_hash_lookup(_pvid_hash, pvid)) == info) &&
+           !strcmp(info->dev->pvid, pvid))
+               return 1;
+       if (*info->dev->pvid)
+               dm_hash_remove(_pvid_hash, info->dev->pvid);
+       strncpy(info->dev->pvid, pvid, sizeof(info->dev->pvid));
+       if (!dm_hash_insert(_pvid_hash, pvid, info)) {
+               log_error("_lvmcache_update: pvid insertion failed: %s", pvid);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * vginfo must be info->vginfo unless info is NULL (orphans)
+ */
+static int _lvmcache_update_vgid(struct lvmcache_info *info,
+                                struct lvmcache_vginfo *vginfo,
+                                const char *vgid)
+{
+       if (!vgid || !vginfo ||
+           !strncmp(vginfo->vgid, vgid, ID_LEN))
+               return 1;
+
+       if (vginfo && *vginfo->vgid)
+               dm_hash_remove(_vgid_hash, vginfo->vgid);
+       if (!vgid) {
+               log_debug("lvmcache: %s: clearing VGID", info ? dev_name(info->dev) : vginfo->vgname);
+               return 1;
+       }
+
+       strncpy(vginfo->vgid, vgid, ID_LEN);
+       vginfo->vgid[ID_LEN] = '\0';
+       if (!dm_hash_insert(_vgid_hash, vginfo->vgid, vginfo)) {
+               log_error("_lvmcache_update: vgid hash insertion failed: %s",
+                         vginfo->vgid);
+               return 0;
+       }
+
+       if (!is_orphan_vg(vginfo->vgname))
+               log_debug("lvmcache: %s: setting %s VGID to %s",
+                         dev_name(info->dev), vginfo->vgname,
+                         vginfo->vgid);
+
+       return 1;
+}
+
+static int _insert_vginfo(struct lvmcache_vginfo *new_vginfo, const char *vgid,
+                         uint32_t vgstatus, const char *creation_host,
+                         struct lvmcache_vginfo *primary_vginfo)
+{
+       struct lvmcache_vginfo *last_vginfo = primary_vginfo;
+       char uuid_primary[64] __attribute__((aligned(8)));
+       char uuid_new[64] __attribute__((aligned(8)));
+       int use_new = 0;
+
+       /* Pre-existing VG takes precedence. Unexported VG takes precedence. */
+       if (primary_vginfo) {
+               if (!id_write_format((const struct id *)vgid, uuid_new, sizeof(uuid_new)))
+                       return_0;
+
+               if (!id_write_format((const struct id *)&primary_vginfo->vgid, uuid_primary,
+                                    sizeof(uuid_primary)))
+                       return_0;
+
+               /*
+                * If   Primary not exported, new exported => keep
+                * Else Primary exported, new not exported => change
+                * Else Primary has hostname for this machine => keep
+                * Else Primary has no hostname, new has one => change
+                * Else New has hostname for this machine => change
+                * Else Keep primary.
+                */
+               if (!(primary_vginfo->status & EXPORTED_VG) &&
+                   (vgstatus & EXPORTED_VG))
+                       log_warn("WARNING: Duplicate VG name %s: "
+                                "Existing %s takes precedence over "
+                                "exported %s", new_vginfo->vgname,
+                                uuid_primary, uuid_new);
+               else if ((primary_vginfo->status & EXPORTED_VG) &&
+                          !(vgstatus & EXPORTED_VG)) {
+                       log_warn("WARNING: Duplicate VG name %s: "
+                                "%s takes precedence over exported %s",
+                                new_vginfo->vgname, uuid_new,
+                                uuid_primary);
+                       use_new = 1;
+               } else if (primary_vginfo->creation_host &&
+                          !strcmp(primary_vginfo->creation_host,
+                                  primary_vginfo->fmt->cmd->hostname))
+                       log_warn("WARNING: Duplicate VG name %s: "
+                                "Existing %s (created here) takes precedence "
+                                "over %s", new_vginfo->vgname, uuid_primary,
+                                uuid_new);
+               else if (!primary_vginfo->creation_host && creation_host) {
+                       log_warn("WARNING: Duplicate VG name %s: "
+                                "%s (with creation_host) takes precedence over %s",
+                                new_vginfo->vgname, uuid_new,
+                                uuid_primary);
+                       use_new = 1;
+               } else if (creation_host &&
+                          !strcmp(creation_host,
+                                  primary_vginfo->fmt->cmd->hostname)) {
+                       log_warn("WARNING: Duplicate VG name %s: "
+                                "%s (created here) takes precedence over %s",
+                                new_vginfo->vgname, uuid_new,
+                                uuid_primary);
+                       use_new = 1;
+               }
+
+               if (!use_new) {
+                       while (last_vginfo->next)
+                               last_vginfo = last_vginfo->next;
+                       last_vginfo->next = new_vginfo;
+                       return 1;
+               }
+
+               dm_hash_remove(_vgname_hash, primary_vginfo->vgname);
+       }
+
+       if (!dm_hash_insert(_vgname_hash, new_vginfo->vgname, new_vginfo)) {
+               log_error("cache_update: vg hash insertion failed: %s",
+                       new_vginfo->vgname);
+               return 0;
+       }
+
+       if (primary_vginfo)
+               new_vginfo->next = primary_vginfo;
+
+       return 1;
+}
+
+static int _lvmcache_update_vgname(struct lvmcache_info *info,
+                                  const char *vgname, const char *vgid,
+                                  uint32_t vgstatus, const char *creation_host,
+                                  const struct format_type *fmt)
+{
+       struct lvmcache_vginfo *vginfo, *primary_vginfo, *orphan_vginfo;
+       struct lvmcache_info *info2, *info3;
+       char mdabuf[32];
+       // struct lvmcache_vginfo  *old_vginfo, *next;
+
+       if (!vgname || (info && info->vginfo && !strcmp(info->vginfo->vgname, vgname)))
+               return 1;
+
+       /* Remove existing vginfo entry */
+       if (info)
+               _drop_vginfo(info, info->vginfo);
+
+       /* Get existing vginfo or create new one */
+       if (!(vginfo = vginfo_from_vgname(vgname, vgid))) {
+/*** FIXME - vginfo ends up duplicated instead of renamed.
+               // Renaming?  This lookup fails.
+               if ((vginfo = vginfo_from_vgid(vgid))) {
+                       next = vginfo->next;
+                       old_vginfo = vginfo_from_vgname(vginfo->vgname, NULL);
+                       if (old_vginfo == vginfo) {
+                               dm_hash_remove(_vgname_hash, old_vginfo->vgname);
+                               if (old_vginfo->next) {
+                                       if (!dm_hash_insert(_vgname_hash, old_vginfo->vgname, old_vginfo->next)) {
+                                               log_error("vg hash re-insertion failed: %s",
+                                                         old_vginfo->vgname);
+                                               return 0;
+                                       }
+                               }
+                       } else do {
+                               if (old_vginfo->next == vginfo) {
+                                       old_vginfo->next = vginfo->next;
+                                       break;
+                               }
+                       } while ((old_vginfo = old_vginfo->next));
+                       vginfo->next = NULL;
+
+                       dm_free(vginfo->vgname);
+                       if (!(vginfo->vgname = dm_strdup(vgname))) {
+                               log_error("cache vgname alloc failed for %s", vgname);
+                               return 0;
+                       }
+
+                       // Rename so can assume new name does not already exist
+                       if (!dm_hash_insert(_vgname_hash, vginfo->vgname, vginfo->next)) {
+                               log_error("vg hash re-insertion failed: %s",
+                                         vginfo->vgname);
+                               return 0;
+                       }
+               } else {
+***/
+               if (!(vginfo = dm_zalloc(sizeof(*vginfo)))) {
+                       log_error("lvmcache_update_vgname: list alloc failed");
+                       return 0;
+               }
+               if (!(vginfo->vgname = dm_strdup(vgname))) {
+                       dm_free(vginfo);
+                       log_error("cache vgname alloc failed for %s", vgname);
+                       return 0;
+               }
+               dm_list_init(&vginfo->infos);
+
+               /*
+                * If we're scanning and there's an invalidated entry, remove it.
+                * Otherwise we risk bogus warnings of duplicate VGs.
+                */
+               while ((primary_vginfo = vginfo_from_vgname(vgname, NULL)) &&
+                      _scanning_in_progress && _vginfo_is_invalid(primary_vginfo))
+                       dm_list_iterate_items_safe(info2, info3, &primary_vginfo->infos) {
+                               orphan_vginfo = vginfo_from_vgname(primary_vginfo->fmt->orphan_vg_name, NULL);
+                               if (!orphan_vginfo) {
+                                       log_error(INTERNAL_ERROR "Orphan vginfo %s lost from cache.",
+                                                 primary_vginfo->fmt->orphan_vg_name);
+                                       dm_free(vginfo->vgname);
+                                       dm_free(vginfo);
+                                       return 0;
+                               }
+                               _drop_vginfo(info2, primary_vginfo);    
+                               _vginfo_attach_info(orphan_vginfo, info2);
+                               if (info2->mdas.n)
+                                       sprintf(mdabuf, " with %u mdas",
+                                               dm_list_size(&info2->mdas));
+                               else
+                                       mdabuf[0] = '\0';
+                               log_debug("lvmcache: %s: now in VG %s%s%s%s%s",
+                                         dev_name(info2->dev),
+                                         vgname, orphan_vginfo->vgid[0] ? " (" : "",
+                                         orphan_vginfo->vgid[0] ? orphan_vginfo->vgid : "",
+                                         orphan_vginfo->vgid[0] ? ")" : "", mdabuf);
+               }
+
+               if (!_insert_vginfo(vginfo, vgid, vgstatus, creation_host,
+                                   primary_vginfo)) {
+                       dm_free(vginfo->vgname);
+                       dm_free(vginfo);
+                       return 0;
+               }
+               /* Ensure orphans appear last on list_iterate */
+               if (is_orphan_vg(vgname))
+                       dm_list_add(&_vginfos, &vginfo->list);
+               else
+                       dm_list_add_h(&_vginfos, &vginfo->list);
+/***
+               }
+***/
+       }
+
+       if (info)
+               _vginfo_attach_info(vginfo, info);
+       else if (!_lvmcache_update_vgid(NULL, vginfo, vgid)) /* Orphans */
+               return_0;
+
+       _update_cache_vginfo_lock_state(vginfo, vgname_is_locked(vgname));
+
+       /* FIXME Check consistency of list! */
+       vginfo->fmt = fmt;
+
+       if (info) {
+               if (info->mdas.n)
+                       sprintf(mdabuf, " with %u mdas", dm_list_size(&info->mdas));
+               else
+                       mdabuf[0] = '\0';
+               log_debug("lvmcache: %s: now in VG %s%s%s%s%s",
+                         dev_name(info->dev),
+                         vgname, vginfo->vgid[0] ? " (" : "",
+                         vginfo->vgid[0] ? vginfo->vgid : "",
+                         vginfo->vgid[0] ? ")" : "", mdabuf);
+       } else
+               log_debug("lvmcache: initialised VG %s", vgname);
+
+       return 1;
+}
+
+static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstatus,
+                                    const char *creation_host)
+{
+       if (!info || !info->vginfo)
+               return 1;
+
+       if ((info->vginfo->status & EXPORTED_VG) != (vgstatus & EXPORTED_VG))
+               log_debug("lvmcache: %s: VG %s %s exported",
+                         dev_name(info->dev), info->vginfo->vgname,
+                         vgstatus & EXPORTED_VG ? "now" : "no longer");
+
+       info->vginfo->status = vgstatus;
+
+       if (!creation_host)
+               return 1;
+
+       if (info->vginfo->creation_host && !strcmp(creation_host,
+                                                  info->vginfo->creation_host))
+               return 1;
+
+       if (info->vginfo->creation_host)
+               dm_free(info->vginfo->creation_host);
+
+       if (!(info->vginfo->creation_host = dm_strdup(creation_host))) {
+               log_error("cache creation host alloc failed for %s",
+                         creation_host);
+               return 0;
+       }
+
+       log_debug("lvmcache: %s: VG %s: Set creation host to %s.",
+                 dev_name(info->dev), info->vginfo->vgname, creation_host);
+
+       return 1;
+}
+
+int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt)
+{
+       if (!_lock_hash && !lvmcache_init()) {
+               log_error("Internal cache initialisation failed");
+               return 0;
+       }
+
+       return _lvmcache_update_vgname(NULL, vgname, vgname, 0, "", fmt);
+}
+
+int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
+                                 const char *vgname, const char *vgid,
+                                 uint32_t vgstatus, const char *creation_host)
+{
+       if (!vgname && !info->vginfo) {
+               log_error(INTERNAL_ERROR "NULL vgname handed to cache");
+               /* FIXME Remove this */
+               vgname = info->fmt->orphan_vg_name;
+               vgid = vgname;
+       }
+
+       /* If PV without mdas is already in a real VG, don't make it orphan */
+       if (is_orphan_vg(vgname) && info->vginfo &&
+           mdas_empty_or_ignored(&info->mdas) &&
+           !is_orphan_vg(info->vginfo->vgname) && memlock())
+               return 1;
+
+       /* If moving PV from orphan to real VG, always mark it valid */
+       if (!is_orphan_vg(vgname))
+               info->status &= ~CACHE_INVALID;
+
+       if (!_lvmcache_update_vgname(info, vgname, vgid, vgstatus,
+                                    creation_host, info->fmt) ||
+           !_lvmcache_update_vgid(info, info->vginfo, vgid) ||
+           !_lvmcache_update_vgstatus(info, vgstatus, creation_host))
+               return_0;
+
+       return 1;
+}
+
+int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
+{
+       struct pv_list *pvl;
+       struct lvmcache_info *info;
+       char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
+
+       pvid_s[sizeof(pvid_s) - 1] = '\0';
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               strncpy(pvid_s, (char *) &pvl->pv->id, sizeof(pvid_s) - 1);
+               /* FIXME Could pvl->pv->dev->pvid ever be different? */
+               if ((info = info_from_pvid(pvid_s, 0)) &&
+                   !lvmcache_update_vgname_and_id(info, vg->name,
+                                                  (char *) &vg->id,
+                                                  vg->status, NULL))
+                       return_0;
+       }
+
+       /* store text representation of vg to cache */
+       if (vg->cmd->current_settings.cache_vgmetadata)
+               _store_metadata(vg, precommitted);
+
+       return 1;
+}
+
+struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
+                                  struct device *dev,
+                                  const char *vgname, const char *vgid,
+                                  uint32_t vgstatus)
+{
+       struct label *label;
+       struct lvmcache_info *existing, *info;
+       char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
+
+       if (!_vgname_hash && !lvmcache_init()) {
+               log_error("Internal cache initialisation failed");
+               return NULL;
+       }
+
+       strncpy(pvid_s, pvid, sizeof(pvid_s));
+       pvid_s[sizeof(pvid_s) - 1] = '\0';
+
+       if (!(existing = info_from_pvid(pvid_s, 0)) &&
+           !(existing = info_from_pvid(dev->pvid, 0))) {
+               if (!(label = label_create(labeller)))
+                       return_NULL;
+               if (!(info = dm_zalloc(sizeof(*info)))) {
+                       log_error("lvmcache_info allocation failed");
+                       label_destroy(label);
+                       return NULL;
+               }
+
+               label->info = info;
+               info->label = label;
+               dm_list_init(&info->list);
+               info->dev = dev;
+       } else {
+               if (existing->dev != dev) {
+                       /* Is the existing entry a duplicate pvid e.g. md ? */
+                       if (dev_subsystem_part_major(existing->dev) &&
+                           !dev_subsystem_part_major(dev)) {
+                               log_very_verbose("Ignoring duplicate PV %s on "
+                                                "%s - using %s %s",
+                                                pvid, dev_name(dev),
+                                                dev_subsystem_name(existing->dev),
+                                                dev_name(existing->dev));
+                               return NULL;
+                       } else if (dm_is_dm_major(MAJOR(existing->dev->dev)) &&
+                                  !dm_is_dm_major(MAJOR(dev->dev))) {
+                               log_very_verbose("Ignoring duplicate PV %s on "
+                                                "%s - using dm %s",
+                                                pvid, dev_name(dev),
+                                                dev_name(existing->dev));
+                               return NULL;
+                       } else if (!dev_subsystem_part_major(existing->dev) &&
+                                  dev_subsystem_part_major(dev))
+                               log_very_verbose("Duplicate PV %s on %s - "
+                                                "using %s %s", pvid,
+                                                dev_name(existing->dev),
+                                                dev_subsystem_name(existing->dev),
+                                                dev_name(dev));
+                       else if (!dm_is_dm_major(MAJOR(existing->dev->dev)) &&
+                                dm_is_dm_major(MAJOR(dev->dev)))
+                               log_very_verbose("Duplicate PV %s on %s - "
+                                                "using dm %s", pvid,
+                                                dev_name(existing->dev),
+                                                dev_name(dev));
+                       /* FIXME If both dm, check dependencies */
+                       //else if (dm_is_dm_major(MAJOR(existing->dev->dev)) &&
+                                //dm_is_dm_major(MAJOR(dev->dev)))
+                                //
+                       else if (!strcmp(pvid_s, existing->dev->pvid)) 
+                               log_error("Found duplicate PV %s: using %s not "
+                                         "%s", pvid, dev_name(dev),
+                                         dev_name(existing->dev));
+               }
+               if (strcmp(pvid_s, existing->dev->pvid)) 
+                       log_debug("Updating pvid cache to %s (%s) from %s (%s)",
+                                 pvid_s, dev_name(dev),
+                                 existing->dev->pvid, dev_name(existing->dev));
+               /* Switch over to new preferred device */
+               existing->dev = dev;
+               info = existing;
+               /* Has labeller changed? */
+               if (info->label->labeller != labeller) {
+                       label_destroy(info->label);
+                       if (!(info->label = label_create(labeller)))
+                               /* FIXME leaves info without label! */
+                               return_NULL;
+                       info->label->info = info;
+               }
+               label = info->label;
+       }
+
+       info->fmt = (const struct format_type *) labeller->private;
+       info->status |= CACHE_INVALID;
+
+       if (!_lvmcache_update_pvid(info, pvid_s)) {
+               if (!existing) {
+                       dm_free(info);
+                       label_destroy(label);
+               }
+               return NULL;
+       }
+
+       if (!lvmcache_update_vgname_and_id(info, vgname, vgid, vgstatus, NULL)) {
+               if (!existing) {
+                       dm_hash_remove(_pvid_hash, pvid_s);
+                       strcpy(info->dev->pvid, "");
+                       dm_free(info);
+                       label_destroy(label);
+               }
+               return NULL;
+       }
+
+       return info;
+}
+
+static void _lvmcache_destroy_entry(struct lvmcache_info *info)
+{
+       _vginfo_detach_info(info);
+       strcpy(info->dev->pvid, "");
+       label_destroy(info->label);
+       dm_free(info);
+}
+
+static void _lvmcache_destroy_vgnamelist(struct lvmcache_vginfo *vginfo)
+{
+       struct lvmcache_vginfo *next;
+
+       do {
+               next = vginfo->next;
+               if (!_free_vginfo(vginfo))
+                       stack;
+       } while ((vginfo = next));
+}
+
+static void _lvmcache_destroy_lockname(struct dm_hash_node *n)
+{
+       char *vgname;
+
+       if (!dm_hash_get_data(_lock_hash, n))
+               return;
+
+       vgname = dm_hash_get_key(_lock_hash, n);
+
+       if (!strcmp(vgname, VG_GLOBAL))
+               _vg_global_lock_held = 1;
+       else
+               log_error(INTERNAL_ERROR "Volume Group %s was not unlocked",
+                         dm_hash_get_key(_lock_hash, n));
+}
+
+void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans)
+{
+       struct dm_hash_node *n;
+       log_verbose("Wiping internal VG cache");
+
+       _has_scanned = 0;
+
+       if (_vgid_hash) {
+               dm_hash_destroy(_vgid_hash);
+               _vgid_hash = NULL;
+       }
+
+       if (_pvid_hash) {
+               dm_hash_iter(_pvid_hash, (dm_hash_iterate_fn) _lvmcache_destroy_entry);
+               dm_hash_destroy(_pvid_hash);
+               _pvid_hash = NULL;
+       }
+
+       if (_vgname_hash) {
+               dm_hash_iter(_vgname_hash,
+                         (dm_hash_iterate_fn) _lvmcache_destroy_vgnamelist);
+               dm_hash_destroy(_vgname_hash);
+               _vgname_hash = NULL;
+       }
+
+       if (_lock_hash) {
+               dm_hash_iterate(n, _lock_hash)
+                       _lvmcache_destroy_lockname(n);
+               dm_hash_destroy(_lock_hash);
+               _lock_hash = NULL;
+       }
+
+       if (!dm_list_empty(&_vginfos))
+               log_error(INTERNAL_ERROR "_vginfos list should be empty");
+       dm_list_init(&_vginfos);
+
+       if (retain_orphans)
+               init_lvmcache_orphans(cmd);
+}
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
new file mode 100644 (file)
index 0000000..06838dc
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_CACHE_H
+#define _LVM_CACHE_H
+
+#include "dev-cache.h"
+#include "uuid.h"
+#include "label.h"
+#include "locking.h"
+
+#define ORPHAN_PREFIX VG_ORPHANS
+#define ORPHAN_VG_NAME(fmt) ORPHAN_PREFIX "_" fmt
+
+#define CACHE_INVALID  0x00000001
+#define CACHE_LOCKED   0x00000002
+
+/* LVM specific per-volume info */
+/* Eventual replacement for struct physical_volume perhaps? */
+
+struct cmd_context;
+struct format_type;
+struct volume_group;
+
+/* One per VG */
+struct lvmcache_vginfo {
+       struct dm_list list;    /* Join these vginfos together */
+       struct dm_list infos;   /* List head for lvmcache_infos */
+       const struct format_type *fmt;
+       char *vgname;           /* "" == orphan */
+       uint32_t status;
+       char vgid[ID_LEN + 1];
+       char _padding[7];
+       struct lvmcache_vginfo *next; /* Another VG with same name? */
+       char *creation_host;
+       char *vgmetadata;       /* Copy of VG metadata as format_text string */
+       unsigned precommitted;  /* Is vgmetadata live or precommitted? */
+};
+
+/* One per device */
+struct lvmcache_info {
+       struct dm_list list;    /* Join VG members together */
+       struct dm_list mdas;    /* list head for metadata areas */
+       struct dm_list das;     /* list head for data areas */
+       struct lvmcache_vginfo *vginfo; /* NULL == unknown */
+       struct label *label;
+       const struct format_type *fmt;
+       struct device *dev;
+       uint64_t device_size;   /* Bytes */
+       uint32_t status;
+};
+
+int lvmcache_init(void);
+void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans);
+
+/* Set full_scan to 1 to reread every filtered device label or
+ * 2 to rescan /dev for new devices */
+int lvmcache_label_scan(struct cmd_context *cmd, int full_scan);
+
+/* Add/delete a device */
+struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
+                                  struct device *dev,
+                                  const char *vgname, const char *vgid,
+                                  uint32_t vgstatus);
+int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt);
+void lvmcache_del(struct lvmcache_info *info);
+
+/* Update things */
+int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
+                                 const char *vgname, const char *vgid,
+                                 uint32_t vgstatus, const char *hostname);
+int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted);
+
+void lvmcache_lock_vgname(const char *vgname, int read_only);
+void lvmcache_unlock_vgname(const char *vgname);
+int lvmcache_verify_lock_order(const char *vgname);
+
+/* Queries */
+const struct format_type *fmt_from_vgname(const char *vgname, const char *vgid, unsigned revalidate_labels);
+struct lvmcache_vginfo *vginfo_from_vgname(const char *vgname,
+                                          const char *vgid);
+struct lvmcache_vginfo *vginfo_from_vgid(const char *vgid);
+struct lvmcache_info *info_from_pvid(const char *pvid, int valid_only);
+const char *vgname_from_vgid(struct dm_pool *mem, const char *vgid);
+struct device *device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
+                               unsigned *scan_done_once);
+const char *pvid_from_devname(struct cmd_context *cmd,
+                             const char *dev_name);
+char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid);
+int vgs_locked(void);
+int vgname_is_locked(const char *vgname);
+
+/* Returns list of struct str_lists containing pool-allocated copy of vgnames */
+/* If include_internal is not set, return only proper vg names. */
+struct dm_list *lvmcache_get_vgnames(struct cmd_context *cmd,
+                                    int include_internal);
+
+/* Returns list of struct str_lists containing pool-allocated copy of vgids */
+/* If include_internal is not set, return only proper vg ids. */
+struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
+                                  int include_internal);
+
+/* Returns list of struct str_lists containing pool-allocated copy of pvids */
+struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
+                               const char *vgid);
+
+/* Returns cached volume group metadata. */
+struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted);
+void lvmcache_drop_metadata(const char *vgname, int drop_precommitted);
+void lvmcache_commit_metadata(const char *vgname);
+
+#endif
diff --git a/lib/commands/errors.h b/lib/commands/errors.h
new file mode 100644 (file)
index 0000000..a644fc7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_ERRORS_H
+#define _LVM_ERRORS_H
+
+#define ECMD_PROCESSED         1
+#define ENO_SUCH_CMD           2
+#define EINVALID_CMD_LINE      3
+#define ECMD_FAILED            5
+
+/* FIXME Also returned by cmdlib. */
+
+#endif
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
new file mode 100644 (file)
index 0000000..c4da38a
--- /dev/null
@@ -0,0 +1,1397 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "defaults.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "filter.h"
+#include "filter-composite.h"
+#include "filter-md.h"
+#include "filter-persistent.h"
+#include "filter-regex.h"
+#include "filter-sysfs.h"
+#include "label.h"
+#include "lvm-file.h"
+#include "format-text.h"
+#include "display.h"
+#include "memlock.h"
+#include "str_list.h"
+#include "segtype.h"
+#include "lvmcache.h"
+#include "dev-cache.h"
+#include "archiver.h"
+
+#ifdef HAVE_LIBDL
+#include "sharedlib.h"
+#endif
+
+#ifdef LVM1_INTERNAL
+#include "format1.h"
+#endif
+
+#ifdef POOL_INTERNAL
+#include "format_pool.h"
+#endif
+
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <syslog.h>
+#include <time.h>
+
+#ifdef linux
+#  include <malloc.h>
+#endif
+
+static int _get_env_vars(struct cmd_context *cmd)
+{
+       const char *e;
+
+       /* Set to "" to avoid using any system directory */
+       if ((e = getenv("LVM_SYSTEM_DIR"))) {
+               if (dm_snprintf(cmd->system_dir, sizeof(cmd->system_dir),
+                                "%s", e) < 0) {
+                       log_error("LVM_SYSTEM_DIR environment variable "
+                                 "is too long.");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static void _get_sysfs_dir(struct cmd_context *cmd)
+{
+       static char proc_mounts[PATH_MAX];
+       static char *split[4], buffer[PATH_MAX + 16];
+       FILE *fp;
+       char *sys_mnt = NULL;
+
+       cmd->sysfs_dir[0] = '\0';
+       if (!*cmd->proc_dir) {
+               log_debug("No proc filesystem found: skipping sysfs detection");
+               return;
+       }
+
+       if (dm_snprintf(proc_mounts, sizeof(proc_mounts),
+                        "%s/mounts", cmd->proc_dir) < 0) {
+               log_error("Failed to create /proc/mounts string for sysfs detection");
+               return;
+       }
+
+       if (!(fp = fopen(proc_mounts, "r"))) {
+               log_sys_error("_get_sysfs_dir: fopen %s", proc_mounts);
+               return;
+       }
+
+       while (fgets(buffer, sizeof(buffer), fp)) {
+               if (dm_split_words(buffer, 4, 0, split) == 4 &&
+                   !strcmp(split[2], "sysfs")) {
+                       sys_mnt = split[1];
+                       break;
+               }
+       }
+
+       if (fclose(fp))
+               log_sys_error("fclose", proc_mounts);
+
+       if (!sys_mnt) {
+               log_error("Failed to find sysfs mount point");
+               return;
+       }
+
+       strncpy(cmd->sysfs_dir, sys_mnt, sizeof(cmd->sysfs_dir));
+}
+
+static void _init_logging(struct cmd_context *cmd)
+{
+       int append = 1;
+       time_t t;
+
+       const char *log_file;
+       char timebuf[26];
+
+       /* Syslog */
+       cmd->default_settings.syslog =
+           find_config_tree_int(cmd, "log/syslog", DEFAULT_SYSLOG);
+       if (cmd->default_settings.syslog != 1)
+               fin_syslog();
+
+       if (cmd->default_settings.syslog > 1)
+               init_syslog(cmd->default_settings.syslog);
+
+       /* Debug level for log file output */
+       cmd->default_settings.debug =
+           find_config_tree_int(cmd, "log/level", DEFAULT_LOGLEVEL);
+       init_debug(cmd->default_settings.debug);
+
+       /* Verbose level for tty output */
+       cmd->default_settings.verbose =
+           find_config_tree_int(cmd, "log/verbose", DEFAULT_VERBOSE);
+       init_verbose(cmd->default_settings.verbose + VERBOSE_BASE_LEVEL);
+
+       /* Log message formatting */
+       init_indent(find_config_tree_int(cmd, "log/indent",
+                                        DEFAULT_INDENT));
+       init_abort_on_internal_errors(find_config_tree_int(cmd, "global/abort_on_internal_errors",
+                                                          DEFAULT_ABORT_ON_INTERNAL_ERRORS));
+
+       cmd->default_settings.msg_prefix = find_config_tree_str(cmd,
+                                                          "log/prefix",
+                                                          DEFAULT_MSG_PREFIX);
+       init_msg_prefix(cmd->default_settings.msg_prefix);
+
+       cmd->default_settings.cmd_name = find_config_tree_int(cmd,
+                                                        "log/command_names",
+                                                        DEFAULT_CMD_NAME);
+       init_cmd_name(cmd->default_settings.cmd_name);
+
+       /* Test mode */
+       cmd->default_settings.test =
+           find_config_tree_int(cmd, "global/test", 0);
+       init_test(cmd->default_settings.test);
+
+       /* Settings for logging to file */
+       if (find_config_tree_int(cmd, "log/overwrite", DEFAULT_OVERWRITE))
+               append = 0;
+
+       log_file = find_config_tree_str(cmd, "log/file", 0);
+
+       if (log_file) {
+               release_log_memory();
+               fin_log();
+               init_log_file(log_file, append);
+       }
+
+       log_file = find_config_tree_str(cmd, "log/activate_file", 0);
+       if (log_file)
+               init_log_direct(log_file, append);
+
+       init_log_while_suspended(find_config_tree_int(cmd,
+                                                "log/activation", 0));
+
+       t = time(NULL);
+       ctime_r(&t, &timebuf[0]);
+       timebuf[24] = '\0';
+       log_verbose("Logging initialised at %s", timebuf);
+
+       /* Tell device-mapper about our logging */
+#ifdef DEVMAPPER_SUPPORT
+       dm_log_with_errno_init(print_log);
+#endif
+       reset_log_duplicated();
+       reset_lvm_errno(1);
+}
+
+static int _process_config(struct cmd_context *cmd)
+{
+       mode_t old_umask;
+       const char *read_ahead;
+       struct stat st;
+       const struct config_node *cn;
+       const struct config_value *cv;
+
+       /* umask */
+       cmd->default_settings.umask = find_config_tree_int(cmd,
+                                                     "global/umask",
+                                                     DEFAULT_UMASK);
+
+       if ((old_umask = umask((mode_t) cmd->default_settings.umask)) !=
+           (mode_t) cmd->default_settings.umask)
+               log_verbose("Set umask from %04o to %04o",
+                            old_umask, cmd->default_settings.umask);
+
+       /* dev dir */
+       if (dm_snprintf(cmd->dev_dir, sizeof(cmd->dev_dir), "%s/",
+                        find_config_tree_str(cmd, "devices/dir",
+                                        DEFAULT_DEV_DIR)) < 0) {
+               log_error("Device directory given in config file too long");
+               return 0;
+       }
+#ifdef DEVMAPPER_SUPPORT
+       dm_set_dev_dir(cmd->dev_dir);
+#endif
+
+       /* proc dir */
+       if (dm_snprintf(cmd->proc_dir, sizeof(cmd->proc_dir), "%s",
+                        find_config_tree_str(cmd, "global/proc",
+                                        DEFAULT_PROC_DIR)) < 0) {
+               log_error("Device directory given in config file too long");
+               return 0;
+       }
+
+       if (*cmd->proc_dir && !dir_exists(cmd->proc_dir)) {
+               log_warn("WARNING: proc dir %s not found - some checks will be bypassed",
+                        cmd->proc_dir);
+               cmd->proc_dir[0] = '\0';
+       }
+
+       /* FIXME Use global value of sysfs_dir everywhere instead cmd->sysfs_dir. */
+       _get_sysfs_dir(cmd);
+       set_sysfs_dir_path(cmd->sysfs_dir);
+
+       /* activation? */
+       cmd->default_settings.activation = find_config_tree_int(cmd,
+                                                          "global/activation",
+                                                          DEFAULT_ACTIVATION);
+       set_activation(cmd->default_settings.activation);
+
+       cmd->default_settings.suffix = find_config_tree_int(cmd,
+                                                      "global/suffix",
+                                                      DEFAULT_SUFFIX);
+
+       if (!(cmd->default_settings.unit_factor =
+             units_to_bytes(find_config_tree_str(cmd,
+                                            "global/units",
+                                            DEFAULT_UNITS),
+                            &cmd->default_settings.unit_type))) {
+               log_error("Invalid units specification");
+               return 0;
+       }
+
+       read_ahead = find_config_tree_str(cmd, "activation/readahead", DEFAULT_READ_AHEAD);
+       if (!strcasecmp(read_ahead, "auto"))
+               cmd->default_settings.read_ahead = DM_READ_AHEAD_AUTO;
+       else if (!strcasecmp(read_ahead, "none"))
+               cmd->default_settings.read_ahead = DM_READ_AHEAD_NONE;
+       else {
+               log_error("Invalid readahead specification");
+               return 0;
+       }
+
+       cmd->default_settings.udev_rules = find_config_tree_int(cmd,
+                                                               "activation/udev_rules",
+                                                               DEFAULT_UDEV_RULES);
+
+       cmd->default_settings.udev_sync = find_config_tree_int(cmd,
+                                                               "activation/udev_sync",
+                                                               DEFAULT_UDEV_SYNC);
+
+       cmd->stripe_filler = find_config_tree_str(cmd,
+                                                 "activation/missing_stripe_filler",
+                                                 DEFAULT_STRIPE_FILLER);
+
+       /* FIXME Missing error code checks from the stats, not log_warn?, notify if setting overridden, delay message/check till it is actually used (eg consider if lvm shell - file could appear later after this check)? */
+       if (!strcmp(cmd->stripe_filler, "/dev/ioerror") &&
+           stat(cmd->stripe_filler, &st))
+               cmd->stripe_filler = "error";
+
+       if (strcmp(cmd->stripe_filler, "error")) {
+               if (stat(cmd->stripe_filler, &st)) {
+                       log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+                                "is invalid,", cmd->stripe_filler);
+                       log_warn("         stat failed: %s", strerror(errno));
+                       log_warn("Falling back to \"error\" missing_stripe_filler.");
+                       cmd->stripe_filler = "error";
+               } else if (!S_ISBLK(st.st_mode)) {
+                       log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+                                "is not a block device.", cmd->stripe_filler);
+                       log_warn("Falling back to \"error\" missing_stripe_filler.");
+                       cmd->stripe_filler = "error";
+               }
+       }
+
+       cmd->si_unit_consistency = find_config_tree_int(cmd,
+                                                 "global/si_unit_consistency",
+                                                 DEFAULT_SI_UNIT_CONSISTENCY);
+
+       if ((cn = find_config_tree_node(cmd, "activation/mlock_filter")))
+               for (cv = cn->v; cv; cv = cv->next) 
+                       if ((cv->type != CFG_STRING) || !cv->v.str[0]) 
+                               log_error("Ignoring invalid activation/mlock_filter entry in config file");
+
+       cmd->metadata_read_only = find_config_tree_int(cmd, "global/metadata_read_only",
+                                                      DEFAULT_METADATA_READ_ONLY);
+
+       return 1;
+}
+
+static int _set_tag(struct cmd_context *cmd, const char *tag)
+{
+       log_very_verbose("Setting host tag: %s", dm_pool_strdup(cmd->libmem, tag));
+
+       if (!str_list_add(cmd->libmem, &cmd->tags, tag)) {
+               log_error("_set_tag: str_list_add %s failed", tag);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _check_host_filters(struct cmd_context *cmd, const struct config_node *hn,
+                              int *passes)
+{
+       const struct config_node *cn;
+       const struct config_value *cv;
+
+       *passes = 1;
+
+       for (cn = hn; cn; cn = cn->sib) {
+               if (!cn->v)
+                       continue;
+               if (!strcmp(cn->key, "host_list")) {
+                       *passes = 0;
+                       if (cn->v->type == CFG_EMPTY_ARRAY)
+                               continue;
+                       for (cv = cn->v; cv; cv = cv->next) {
+                               if (cv->type != CFG_STRING) {
+                                       log_error("Invalid hostname string "
+                                                 "for tag %s", cn->key);
+                                       return 0;
+                               }
+                               if (!strcmp(cv->v.str, cmd->hostname)) {
+                                       *passes = 1;
+                                       return 1;
+                               }
+                       }
+               }
+               if (!strcmp(cn->key, "host_filter")) {
+                       log_error("host_filter not supported yet");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _init_tags(struct cmd_context *cmd, struct config_tree *cft)
+{
+       const struct config_node *tn, *cn;
+       const char *tag;
+       int passes;
+
+       if (!(tn = find_config_node(cft->root, "tags")) || !tn->child)
+               return 1;
+
+       /* NB hosttags 0 when already 1 intentionally does not delete the tag */
+       if (!cmd->hosttags && find_config_int(cft->root, "tags/hosttags",
+                                             DEFAULT_HOSTTAGS)) {
+               /* FIXME Strip out invalid chars: only A-Za-z0-9_+.- */
+               if (!_set_tag(cmd, cmd->hostname))
+                       return_0;
+               cmd->hosttags = 1;
+       }
+
+       for (cn = tn->child; cn; cn = cn->sib) {
+               if (cn->v)
+                       continue;
+               tag = cn->key;
+               if (*tag == '@')
+                       tag++;
+               if (!validate_name(tag)) {
+                       log_error("Invalid tag in config file: %s", cn->key);
+                       return 0;
+               }
+               if (cn->child) {
+                       passes = 0;
+                       if (!_check_host_filters(cmd, cn->child, &passes))
+                               return_0;
+                       if (!passes)
+                               continue;
+               }
+               if (!_set_tag(cmd, tag))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _load_config_file(struct cmd_context *cmd, const char *tag)
+{
+       char config_file[PATH_MAX] = "";
+       const char *filler = "";
+       struct stat info;
+       struct config_tree_list *cfl;
+
+       if (*tag)
+               filler = "_";
+
+       if (dm_snprintf(config_file, sizeof(config_file), "%s/lvm%s%s.conf",
+                        cmd->system_dir, filler, tag) < 0) {
+               log_error("LVM_SYSTEM_DIR or tag was too long");
+               return 0;
+       }
+
+       if (!(cfl = dm_pool_alloc(cmd->libmem, sizeof(*cfl)))) {
+               log_error("config_tree_list allocation failed");
+               return 0;
+       }
+
+       if (!(cfl->cft = create_config_tree(config_file, 0))) {
+               log_error("config_tree allocation failed");
+               return 0;
+       }
+
+       /* Is there a config file? */
+       if (stat(config_file, &info) == -1) {
+               if (errno == ENOENT) {
+                       dm_list_add(&cmd->config_files, &cfl->list);
+                       goto out;
+               }
+               log_sys_error("stat", config_file);
+               destroy_config_tree(cfl->cft);
+               return 0;
+       }
+
+       log_very_verbose("Loading config file: %s", config_file);
+       if (!read_config_file(cfl->cft)) {
+               log_error("Failed to load config file %s", config_file);
+               destroy_config_tree(cfl->cft);
+               return 0;
+       }
+
+       dm_list_add(&cmd->config_files, &cfl->list);
+
+      out:
+       if (*tag)
+               _init_tags(cmd, cfl->cft);
+       else
+               /* Use temporary copy of lvm.conf while loading other files */
+               cmd->cft = cfl->cft;
+
+       return 1;
+}
+
+/* Find and read first config file */
+static int _init_lvm_conf(struct cmd_context *cmd)
+{
+       /* No config file if LVM_SYSTEM_DIR is empty */
+       if (!*cmd->system_dir) {
+               if (!(cmd->cft = create_config_tree(NULL, 0))) {
+                       log_error("Failed to create config tree");
+                       return 0;
+               }
+               return 1;
+       }
+
+       if (!_load_config_file(cmd, ""))
+               return_0;
+
+       return 1;
+}
+
+/* Read any additional config files */
+static int _init_tag_configs(struct cmd_context *cmd)
+{
+       struct str_list *sl;
+
+       /* Tag list may grow while inside this loop */
+       dm_list_iterate_items(sl, &cmd->tags) {
+               if (!_load_config_file(cmd, sl->str))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _merge_config_files(struct cmd_context *cmd)
+{
+       struct config_tree_list *cfl;
+
+       /* Replace temporary duplicate copy of lvm.conf */
+       if (cmd->cft->root) {
+               if (!(cmd->cft = create_config_tree(NULL, 0))) {
+                       log_error("Failed to create config tree");
+                       return 0;
+               }
+       }
+
+       dm_list_iterate_items(cfl, &cmd->config_files) {
+               /* Merge all config trees into cmd->cft using merge/tag rules */
+               if (!merge_config_tree(cmd, cmd->cft, cfl->cft))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static void _destroy_tags(struct cmd_context *cmd)
+{
+       struct dm_list *slh, *slht;
+
+       dm_list_iterate_safe(slh, slht, &cmd->tags) {
+               dm_list_del(slh);
+       }
+}
+
+int config_files_changed(struct cmd_context *cmd)
+{
+       struct config_tree_list *cfl;
+
+       dm_list_iterate_items(cfl, &cmd->config_files) {
+               if (config_file_changed(cfl->cft))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static void _destroy_tag_configs(struct cmd_context *cmd)
+{
+       struct config_tree_list *cfl;
+
+       dm_list_iterate_items(cfl, &cmd->config_files) {
+               if (cfl->cft == cmd->cft)
+                       cmd->cft = NULL;
+               destroy_config_tree(cfl->cft);
+       }
+
+       if (cmd->cft) {
+               destroy_config_tree(cmd->cft);
+               cmd->cft = NULL;
+       }
+
+       dm_list_init(&cmd->config_files);
+}
+
+static int _init_dev_cache(struct cmd_context *cmd)
+{
+       const struct config_node *cn;
+       const struct config_value *cv;
+
+       init_dev_disable_after_error_count(
+               find_config_tree_int(cmd, "devices/disable_after_error_count",
+                                    DEFAULT_DISABLE_AFTER_ERROR_COUNT));
+
+       if (!dev_cache_init(cmd))
+               return_0;
+
+       if (!(cn = find_config_tree_node(cmd, "devices/scan"))) {
+               if (!dev_cache_add_dir("/dev")) {
+                       log_error("Failed to add /dev to internal "
+                                 "device cache");
+                       return 0;
+               }
+               log_verbose("device/scan not in config file: "
+                           "Defaulting to /dev");
+               return 1;
+       }
+
+       for (cv = cn->v; cv; cv = cv->next) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Invalid string in config file: "
+                                 "devices/scan");
+                       return 0;
+               }
+
+               if (!dev_cache_add_dir(cv->v.str)) {
+                       log_error("Failed to add %s to internal device cache",
+                                 cv->v.str);
+                       return 0;
+               }
+       }
+
+       if (!(cn = find_config_tree_node(cmd, "devices/loopfiles")))
+               return 1;
+
+       for (cv = cn->v; cv; cv = cv->next) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Invalid string in config file: "
+                                 "devices/loopfiles");
+                       return 0;
+               }
+
+               if (!dev_cache_add_loopfile(cv->v.str)) {
+                       log_error("Failed to add loopfile %s to internal "
+                                 "device cache", cv->v.str);
+                       return 0;
+               }
+       }
+
+
+       return 1;
+}
+
+#define MAX_FILTERS 4
+
+static struct dev_filter *_init_filter_components(struct cmd_context *cmd)
+{
+       unsigned nr_filt = 0;
+       const struct config_node *cn;
+       struct dev_filter *filters[MAX_FILTERS];
+
+       memset(filters, 0, sizeof(filters));
+
+       /*
+        * Filters listed in order: top one gets applied first.
+        * Failure to initialise some filters is not fatal.
+        * Update MAX_FILTERS definition above when adding new filters.
+        */
+
+       /*
+        * sysfs filter. Only available on 2.6 kernels.  Non-critical.
+        * Listed first because it's very efficient at eliminating
+        * unavailable devices.
+        */
+       if (find_config_tree_bool(cmd, "devices/sysfs_scan",
+                            DEFAULT_SYSFS_SCAN)) {
+               if ((filters[nr_filt] = sysfs_filter_create(cmd->sysfs_dir)))
+                       nr_filt++;
+       }
+
+       /* regex filter. Optional. */
+       if (!(cn = find_config_tree_node(cmd, "devices/filter")))
+               log_very_verbose("devices/filter not found in config file: "
+                                "no regex filter installed");
+
+       else if (!(filters[nr_filt++] = regex_filter_create(cn->v))) {
+               log_error("Failed to create regex device filter");
+               goto err;
+       }
+
+       /* device type filter. Required. */
+       cn = find_config_tree_node(cmd, "devices/types");
+       if (!(filters[nr_filt++] = lvm_type_filter_create(cmd->proc_dir, cn))) {
+               log_error("Failed to create lvm type filter");
+               goto err;
+       }
+
+       /* md component filter. Optional, non-critical. */
+       if (find_config_tree_bool(cmd, "devices/md_component_detection",
+                            DEFAULT_MD_COMPONENT_DETECTION)) {
+               init_md_filtering(1);
+               if ((filters[nr_filt] = md_filter_create()))
+                       nr_filt++;
+       }
+
+       /* Only build a composite filter if we really need it. */
+       return (nr_filt == 1) ?
+           filters[0] : composite_filter_create(nr_filt, filters);
+err:
+       nr_filt--; /* skip NULL */
+       while (nr_filt-- > 0)
+                filters[nr_filt]->destroy(filters[nr_filt]);
+       return NULL;
+}
+
+static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
+{
+       const char *dev_cache = NULL, *cache_dir, *cache_file_prefix;
+       struct dev_filter *f3, *f4;
+       struct stat st;
+       char cache_file[PATH_MAX];
+
+       cmd->dump_filter = 0;
+
+       if (!(f3 = _init_filter_components(cmd)))
+               return 0;
+
+       init_ignore_suspended_devices(find_config_tree_int(cmd,
+           "devices/ignore_suspended_devices", DEFAULT_IGNORE_SUSPENDED_DEVICES));
+
+       /*
+        * If 'cache_dir' or 'cache_file_prefix' is set, ignore 'cache'.
+        */
+       cache_dir = find_config_tree_str(cmd, "devices/cache_dir", NULL);
+       cache_file_prefix = find_config_tree_str(cmd, "devices/cache_file_prefix", NULL);
+
+       if (cache_dir || cache_file_prefix) {
+               if (dm_snprintf(cache_file, sizeof(cache_file),
+                   "%s%s%s/%s.cache",
+                   cache_dir ? "" : cmd->system_dir,
+                   cache_dir ? "" : "/",
+                   cache_dir ? : DEFAULT_CACHE_SUBDIR,
+                   cache_file_prefix ? : DEFAULT_CACHE_FILE_PREFIX) < 0) {
+                       log_error("Persistent cache filename too long.");
+                       return 0;
+               }
+       } else if (!(dev_cache = find_config_tree_str(cmd, "devices/cache", NULL)) &&
+                  (dm_snprintf(cache_file, sizeof(cache_file),
+                               "%s/%s/%s.cache",
+                               cmd->system_dir, DEFAULT_CACHE_SUBDIR,
+                               DEFAULT_CACHE_FILE_PREFIX) < 0)) {
+               log_error("Persistent cache filename too long.");
+               return 0;
+       }
+
+       if (!dev_cache)
+               dev_cache = cache_file;
+
+       if (!(f4 = persistent_filter_create(f3, dev_cache))) {
+               log_error("Failed to create persistent device filter");
+               return 0;
+       }
+
+       /* Should we ever dump persistent filter state? */
+       if (find_config_tree_int(cmd, "devices/write_cache_state", 1))
+               cmd->dump_filter = 1;
+
+       if (!*cmd->system_dir)
+               cmd->dump_filter = 0;
+
+       /*
+        * Only load persistent filter device cache on startup if it is newer
+        * than the config file and this is not a long-lived process.
+        */
+       if (load_persistent_cache && !cmd->is_long_lived &&
+           !stat(dev_cache, &st) &&
+           (st.st_ctime > config_file_timestamp(cmd->cft)) &&
+           !persistent_filter_load(f4, NULL))
+               log_verbose("Failed to load existing device cache from %s",
+                           dev_cache);
+
+       cmd->filter = f4;
+
+       return 1;
+}
+
+struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format)
+{
+        struct format_type *fmt;
+
+        dm_list_iterate_items(fmt, &cmd->formats)
+                if (!strcasecmp(fmt->name, format) ||
+                    !strcasecmp(fmt->name + 3, format) ||
+                    (fmt->alias && !strcasecmp(fmt->alias, format)))
+                        return fmt;
+
+        return NULL;
+}
+
+static int _init_formats(struct cmd_context *cmd)
+{
+       const char *format;
+
+       struct format_type *fmt;
+
+#ifdef HAVE_LIBDL
+       const struct config_node *cn;
+#endif
+
+       label_init();
+
+#ifdef LVM1_INTERNAL
+       if (!(fmt = init_lvm1_format(cmd)))
+               return 0;
+       fmt->library = NULL;
+       dm_list_add(&cmd->formats, &fmt->list);
+#endif
+
+#ifdef POOL_INTERNAL
+       if (!(fmt = init_pool_format(cmd)))
+               return 0;
+       fmt->library = NULL;
+       dm_list_add(&cmd->formats, &fmt->list);
+#endif
+
+#ifdef HAVE_LIBDL
+       /* Load any formats in shared libs if not static */
+       if (!is_static() &&
+           (cn = find_config_tree_node(cmd, "global/format_libraries"))) {
+
+               const struct config_value *cv;
+               struct format_type *(*init_format_fn) (struct cmd_context *);
+               void *lib;
+
+               for (cv = cn->v; cv; cv = cv->next) {
+                       if (cv->type != CFG_STRING) {
+                               log_error("Invalid string in config file: "
+                                         "global/format_libraries");
+                               return 0;
+                       }
+                       if (!(lib = load_shared_library(cmd, cv->v.str,
+                                                       "format", 0)))
+                               return_0;
+
+                       if (!(init_format_fn = dlsym(lib, "init_format"))) {
+                               log_error("Shared library %s does not contain "
+                                         "format functions", cv->v.str);
+                               dlclose(lib);
+                               return 0;
+                       }
+
+                       if (!(fmt = init_format_fn(cmd))) {
+                               dlclose(lib);
+                               return_0;
+                       }
+
+                       fmt->library = lib;
+                       dm_list_add(&cmd->formats, &fmt->list);
+               }
+       }
+#endif
+
+       if (!(fmt = create_text_format(cmd)))
+               return 0;
+       fmt->library = NULL;
+       dm_list_add(&cmd->formats, &fmt->list);
+
+       cmd->fmt_backup = fmt;
+
+       format = find_config_tree_str(cmd, "global/format",
+                                DEFAULT_FORMAT);
+
+       dm_list_iterate_items(fmt, &cmd->formats) {
+               if (!strcasecmp(fmt->name, format) ||
+                   (fmt->alias && !strcasecmp(fmt->alias, format))) {
+                       cmd->default_settings.fmt_name = fmt->name;
+                       cmd->fmt = fmt;
+                       return 1;
+               }
+       }
+
+       log_error("_init_formats: Default format (%s) not found", format);
+       return 0;
+}
+
+int init_lvmcache_orphans(struct cmd_context *cmd)
+{
+       struct format_type *fmt;
+
+       dm_list_iterate_items(fmt, &cmd->formats)
+               if (!lvmcache_add_orphan_vginfo(fmt->orphan_vg_name, fmt))
+                       return_0;
+
+       return 1;
+}
+
+struct segtype_library {
+       struct cmd_context *cmd;
+       void *lib;
+       const char *libname;
+};
+
+int lvm_register_segtype(struct segtype_library *seglib,
+                        struct segment_type *segtype)
+{
+       struct segment_type *segtype2;
+
+       segtype->library = seglib->lib;
+       segtype->cmd = seglib->cmd;
+
+       dm_list_iterate_items(segtype2, &seglib->cmd->segtypes) {
+               if (strcmp(segtype2->name, segtype->name))
+                       continue;
+               log_error("Duplicate segment type %s: "
+                         "unloading shared library %s",
+                         segtype->name, seglib->libname);
+               segtype->ops->destroy(segtype);
+               return 0;
+       }
+
+       dm_list_add(&seglib->cmd->segtypes, &segtype->list);
+
+       return 1;
+}
+
+static int _init_single_segtype(struct cmd_context *cmd,
+                               struct segtype_library *seglib)
+{
+       struct segment_type *(*init_segtype_fn) (struct cmd_context *);
+       struct segment_type *segtype;
+
+       if (!(init_segtype_fn = dlsym(seglib->lib, "init_segtype"))) {
+               log_error("Shared library %s does not contain segment type "
+                         "functions", seglib->libname);
+               return 0;
+       }
+
+       if (!(segtype = init_segtype_fn(seglib->cmd)))
+               return_0;
+
+       return lvm_register_segtype(seglib, segtype);
+}
+
+static int _init_segtypes(struct cmd_context *cmd)
+{
+       struct segment_type *segtype;
+       struct segtype_library seglib = { .cmd = cmd };
+
+#ifdef HAVE_LIBDL
+       const struct config_node *cn;
+#endif
+
+       if (!(segtype = init_striped_segtype(cmd)))
+               return 0;
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+
+       if (!(segtype = init_zero_segtype(cmd)))
+               return 0;
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+
+       if (!(segtype = init_error_segtype(cmd)))
+               return 0;
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+
+       if (!(segtype = init_free_segtype(cmd)))
+               return 0;
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+
+#ifdef SNAPSHOT_INTERNAL
+       if (!(segtype = init_snapshot_segtype(cmd)))
+               return 0;
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+#endif
+
+#ifdef MIRRORED_INTERNAL
+       if (!(segtype = init_mirrored_segtype(cmd)))
+               return 0;
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+#endif
+
+#ifdef REPLICATOR_INTERNAL
+       if (!init_replicator_segtype(&seglib))
+               return 0;
+#endif
+
+#ifdef HAVE_LIBDL
+       /* Load any formats in shared libs unless static */
+       if (!is_static() &&
+           (cn = find_config_tree_node(cmd, "global/segment_libraries"))) {
+
+               const struct config_value *cv;
+               int (*init_multiple_segtypes_fn) (struct cmd_context *,
+                                                 struct segtype_library *);
+
+               for (cv = cn->v; cv; cv = cv->next) {
+                       if (cv->type != CFG_STRING) {
+                               log_error("Invalid string in config file: "
+                                         "global/segment_libraries");
+                               return 0;
+                       }
+                       seglib.libname = cv->v.str;
+                       if (!(seglib.lib = load_shared_library(cmd,
+                                                       seglib.libname,
+                                                       "segment type", 0)))
+                               return_0;
+
+                       if ((init_multiple_segtypes_fn =
+                           dlsym(seglib.lib, "init_multiple_segtypes"))) {
+                               if (dlsym(seglib.lib, "init_segtype"))
+                                       log_warn("WARNING: Shared lib %s has "
+                                                "conflicting init fns.  Using"
+                                                " init_multiple_segtypes().",
+                                                seglib.libname);
+                       } else
+                               init_multiple_segtypes_fn =
+                                   _init_single_segtype;
+                       if (!init_multiple_segtypes_fn(cmd, &seglib)) {
+                               struct dm_list *sgtl, *tmp;
+                               log_error("init_multiple_segtypes() failed: "
+                                         "Unloading shared library %s",
+                                         seglib.libname);
+                               dm_list_iterate_safe(sgtl, tmp, &cmd->segtypes) {
+                                       segtype = dm_list_item(sgtl, struct segment_type);
+                                       if (segtype->library == seglib.lib) {
+                                               dm_list_del(&segtype->list);
+                                               segtype->ops->destroy(segtype);
+                                       }
+                               }
+                               dlclose(seglib.lib);
+                               return_0;
+                       }
+               }
+       }
+#endif
+
+       return 1;
+}
+
+static int _init_hostname(struct cmd_context *cmd)
+{
+       struct utsname uts;
+
+       if (uname(&uts)) {
+               log_sys_error("uname", "_init_hostname");
+               return 0;
+       }
+
+       if (!(cmd->hostname = dm_pool_strdup(cmd->libmem, uts.nodename))) {
+               log_error("_init_hostname: dm_pool_strdup failed");
+               return 0;
+       }
+
+       if (!(cmd->kernel_vsn = dm_pool_strdup(cmd->libmem, uts.release))) {
+               log_error("_init_hostname: dm_pool_strdup kernel_vsn failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _init_backup(struct cmd_context *cmd)
+{
+       uint32_t days, min;
+       char default_dir[PATH_MAX];
+       const char *dir;
+
+       if (!cmd->system_dir[0]) {
+               log_warn("WARNING: Metadata changes will NOT be backed up");
+               backup_init(cmd, "", 0);
+               archive_init(cmd, "", 0, 0, 0);
+               return 1;
+       }
+
+       /* set up archiving */
+       cmd->default_settings.archive =
+           find_config_tree_bool(cmd, "backup/archive",
+                            DEFAULT_ARCHIVE_ENABLED);
+
+       days = (uint32_t) find_config_tree_int(cmd, "backup/retain_days",
+                                         DEFAULT_ARCHIVE_DAYS);
+
+       min = (uint32_t) find_config_tree_int(cmd, "backup/retain_min",
+                                        DEFAULT_ARCHIVE_NUMBER);
+
+       if (dm_snprintf
+           (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
+            DEFAULT_ARCHIVE_SUBDIR) == -1) {
+               log_error("Couldn't create default archive path '%s/%s'.",
+                         cmd->system_dir, DEFAULT_ARCHIVE_SUBDIR);
+               return 0;
+       }
+
+       dir = find_config_tree_str(cmd, "backup/archive_dir",
+                             default_dir);
+
+       if (!archive_init(cmd, dir, days, min,
+                         cmd->default_settings.archive)) {
+               log_debug("archive_init failed.");
+               return 0;
+       }
+
+       /* set up the backup */
+       cmd->default_settings.backup =
+           find_config_tree_bool(cmd, "backup/backup",
+                            DEFAULT_BACKUP_ENABLED);
+
+       if (dm_snprintf
+           (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
+            DEFAULT_BACKUP_SUBDIR) == -1) {
+               log_error("Couldn't create default backup path '%s/%s'.",
+                         cmd->system_dir, DEFAULT_BACKUP_SUBDIR);
+               return 0;
+       }
+
+       dir = find_config_tree_str(cmd, "backup/backup_dir", default_dir);
+
+       if (!backup_init(cmd, dir, cmd->default_settings.backup)) {
+               log_debug("backup_init failed.");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _init_rand(struct cmd_context *cmd)
+{
+       if (read_urandom(&cmd->rand_seed, sizeof(cmd->rand_seed))) {
+               reset_lvm_errno(1);
+               return;
+       }
+
+       cmd->rand_seed = (unsigned) time(NULL) + (unsigned) getpid();
+       reset_lvm_errno(1);
+}
+
+static void _init_globals(struct cmd_context *cmd)
+{
+       init_full_scan_done(0);
+       init_mirror_in_sync(0);
+
+}
+
+/* Entry point */
+struct cmd_context *create_toolcontext(unsigned is_long_lived,
+                                      const char *system_dir)
+{
+       struct cmd_context *cmd;
+
+#ifdef M_MMAP_MAX
+       mallopt(M_MMAP_MAX, 0);
+#endif
+
+       if (!setlocale(LC_ALL, ""))
+               log_very_verbose("setlocale failed");
+
+#ifdef INTL_PACKAGE
+       bindtextdomain(INTL_PACKAGE, LOCALEDIR);
+#endif
+
+       init_syslog(DEFAULT_LOG_FACILITY);
+
+       if (!(cmd = dm_zalloc(sizeof(*cmd)))) {
+               log_error("Failed to allocate command context");
+               return NULL;
+       }
+       cmd->is_long_lived = is_long_lived;
+       cmd->handles_missing_pvs = 0;
+       cmd->handles_unknown_segments = 0;
+       cmd->independent_metadata_areas = 0;
+       cmd->hosttags = 0;
+       dm_list_init(&cmd->arg_value_groups);
+       dm_list_init(&cmd->formats);
+       dm_list_init(&cmd->segtypes);
+       dm_list_init(&cmd->tags);
+       dm_list_init(&cmd->config_files);
+
+       /* FIXME Make this configurable? */
+       reset_lvm_errno(1);
+
+       /*
+        * Environment variable LVM_SYSTEM_DIR overrides this below.
+        */
+        if (system_dir)
+               strncpy(cmd->system_dir, system_dir, sizeof(cmd->system_dir) - 1);
+       else
+               strcpy(cmd->system_dir, DEFAULT_SYS_DIR);
+
+       if (!_get_env_vars(cmd))
+               goto_out;
+
+       /* Create system directory if it doesn't already exist */
+       if (*cmd->system_dir && !dm_create_dir(cmd->system_dir)) {
+               log_error("Failed to create LVM2 system dir for metadata backups, config "
+                         "files and internal cache.");
+               log_error("Set environment variable LVM_SYSTEM_DIR to alternative location "
+                         "or empty string.");
+               goto out;
+       }
+
+       if (!(cmd->libmem = dm_pool_create("library", 4 * 1024))) {
+               log_error("Library memory pool creation failed");
+               goto out;
+       }
+
+       if (!_init_lvm_conf(cmd))
+               goto_out;
+
+       _init_logging(cmd);
+
+       if (!_init_hostname(cmd))
+               goto_out;
+
+       if (!_init_tags(cmd, cmd->cft))
+               goto_out;
+
+       if (!_init_tag_configs(cmd))
+               goto_out;
+
+       if (!_merge_config_files(cmd))
+               goto_out;
+
+       if (!_process_config(cmd))
+               goto_out;
+
+       if (!_init_dev_cache(cmd))
+               goto_out;
+
+       if (!_init_filters(cmd, 1))
+               goto_out;
+
+       if (!(cmd->mem = dm_pool_create("command", 4 * 1024))) {
+               log_error("Command memory pool creation failed");
+               goto out;
+       }
+
+       memlock_init(cmd);
+
+       if (!_init_formats(cmd))
+               goto_out;
+
+       if (!init_lvmcache_orphans(cmd))
+               goto_out;
+
+       if (!_init_segtypes(cmd))
+               goto_out;
+
+       if (!_init_backup(cmd))
+               goto_out;
+
+       _init_rand(cmd);
+
+       _init_globals(cmd);
+
+       cmd->default_settings.cache_vgmetadata = 1;
+       cmd->current_settings = cmd->default_settings;
+
+       cmd->config_valid = 1;
+out:
+       return cmd;
+}
+
+static void _destroy_formats(struct cmd_context *cmd, struct dm_list *formats)
+{
+       struct dm_list *fmtl, *tmp;
+       struct format_type *fmt;
+       void *lib;
+
+       dm_list_iterate_safe(fmtl, tmp, formats) {
+               fmt = dm_list_item(fmtl, struct format_type);
+               dm_list_del(&fmt->list);
+               lib = fmt->library;
+               fmt->ops->destroy(fmt);
+#ifdef HAVE_LIBDL
+               if (lib)
+                       dlclose(lib);
+#endif
+       }
+
+       cmd->independent_metadata_areas = 0;
+}
+
+static void _destroy_segtypes(struct dm_list *segtypes)
+{
+       struct dm_list *sgtl, *tmp;
+       struct segment_type *segtype;
+       void *lib;
+
+       dm_list_iterate_safe(sgtl, tmp, segtypes) {
+               segtype = dm_list_item(sgtl, struct segment_type);
+               dm_list_del(&segtype->list);
+               lib = segtype->library;
+               segtype->ops->destroy(segtype);
+#ifdef HAVE_LIBDL
+               /*
+                * If no segtypes remain from this library, close it.
+                */
+               if (lib) {
+                       struct segment_type *segtype2;
+                       dm_list_iterate_items(segtype2, segtypes)
+                               if (segtype2->library == lib)
+                                       goto skip_dlclose;
+                       dlclose(lib);
+skip_dlclose:
+                       ;
+               }
+#endif
+       }
+}
+
+int refresh_filters(struct cmd_context *cmd)
+{
+       int r, saved_ignore_suspended_devices = ignore_suspended_devices();
+
+       if (cmd->filter) {
+               cmd->filter->destroy(cmd->filter);
+               cmd->filter = NULL;
+       }
+
+       r = _init_filters(cmd, 0);
+
+       /*
+        * During repair code must not reset suspended flag.
+        */
+       init_ignore_suspended_devices(saved_ignore_suspended_devices);
+
+       return r;
+}
+
+int refresh_toolcontext(struct cmd_context *cmd)
+{
+       log_verbose("Reloading config files");
+
+       /*
+        * Don't update the persistent filter cache as we will
+        * perform a full rescan.
+        */
+
+       activation_release();
+       lvmcache_destroy(cmd, 0);
+       label_exit();
+       _destroy_segtypes(&cmd->segtypes);
+       _destroy_formats(cmd, &cmd->formats);
+       if (cmd->filter) {
+               cmd->filter->destroy(cmd->filter);
+               cmd->filter = NULL;
+       }
+       dev_cache_exit();
+       _destroy_tags(cmd);
+       _destroy_tag_configs(cmd);
+
+       cmd->config_valid = 0;
+
+       cmd->hosttags = 0;
+
+       if (!_init_lvm_conf(cmd))
+               return 0;
+
+       _init_logging(cmd);
+
+       if (!_init_tags(cmd, cmd->cft))
+               return 0;
+
+       if (!_init_tag_configs(cmd))
+               return 0;
+
+       if (!_merge_config_files(cmd))
+               return 0;
+
+       if (!_process_config(cmd))
+               return 0;
+
+       if (!_init_dev_cache(cmd))
+               return 0;
+
+       if (!_init_filters(cmd, 0))
+               return 0;
+
+       if (!_init_formats(cmd))
+               return 0;
+
+       if (!init_lvmcache_orphans(cmd))
+               return 0;
+
+       if (!_init_segtypes(cmd))
+               return 0;
+
+       if (!_init_backup(cmd))
+               return 0;
+
+       cmd->config_valid = 1;
+
+       reset_lvm_errno(1);
+       return 1;
+}
+
+void destroy_toolcontext(struct cmd_context *cmd)
+{
+       if (cmd->dump_filter)
+               persistent_filter_dump(cmd->filter, 1);
+
+       archive_exit(cmd);
+       backup_exit(cmd);
+       lvmcache_destroy(cmd, 0);
+       label_exit();
+       _destroy_segtypes(&cmd->segtypes);
+       _destroy_formats(cmd, &cmd->formats);
+       if (cmd->filter)
+               cmd->filter->destroy(cmd->filter);
+       if (cmd->mem)
+               dm_pool_destroy(cmd->mem);
+       dev_cache_exit();
+       _destroy_tags(cmd);
+       _destroy_tag_configs(cmd);
+       if (cmd->libmem)
+               dm_pool_destroy(cmd->libmem);
+       dm_free(cmd);
+
+       release_log_memory();
+       activation_exit();
+       reset_log_duplicated();
+       fin_log();
+       fin_syslog();
+       reset_lvm_errno(0);
+}
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
new file mode 100644 (file)
index 0000000..4628c7c
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TOOLCONTEXT_H
+#define _LVM_TOOLCONTEXT_H
+
+#include "dev-cache.h"
+
+#include <stdio.h>
+#include <limits.h>
+
+/*
+ * Config options that can be changed while commands are processed
+ */
+struct config_info {
+       int debug;
+       int verbose;
+       int test;
+       int syslog;
+       int activation;
+       int suffix;
+       int archive;            /* should we archive ? */
+       int backup;             /* should we backup ? */
+       int read_ahead;         /* DM_READ_AHEAD_NONE or _AUTO */
+       int udev_rules;
+       int udev_sync;
+       int cache_vgmetadata;
+       const char *msg_prefix;
+       const char *fmt_name;
+       uint64_t unit_factor;
+       int cmd_name;           /* Show command name? */
+       mode_t umask;
+       char unit_type;
+       char _padding[1];
+};
+
+struct config_tree;
+struct archive_params;
+struct backup_params;
+struct arg_values;
+
+/* FIXME Split into tool & library contexts */
+/* command-instance-related variables needed by library */
+struct cmd_context {
+       struct dm_pool *libmem; /* For permanent config data */
+       struct dm_pool *mem;    /* Transient: Cleared between each command */
+
+       const struct format_type *fmt;  /* Current format to use by default */
+       struct format_type *fmt_backup; /* Format to use for backups */
+
+       struct dm_list formats; /* Available formats */
+       struct dm_list segtypes;        /* Available segment types */
+       const char *hostname;
+       const char *kernel_vsn;
+
+       unsigned rand_seed;
+       const char *cmd_line;
+       struct command *command;
+       char **argv;
+       struct arg_values *arg_values;
+       struct dm_list arg_value_groups;
+       unsigned is_long_lived:1;       /* Optimises persistent_filter handling */
+       unsigned handles_missing_pvs:1;
+       unsigned handles_unknown_segments:1;
+       unsigned partial_activation:1;
+       unsigned si_unit_consistency:1;
+       unsigned metadata_read_only:1;
+
+       unsigned independent_metadata_areas:1;  /* Active formats have MDAs outside PVs */
+
+       struct dev_filter *filter;
+       int dump_filter;        /* Dump filter when exiting? */
+
+       struct dm_list config_files;
+       int config_valid;
+       struct config_tree *cft;
+       struct config_tree *cft_override;
+       struct config_info default_settings;
+       struct config_info current_settings;
+
+       struct archive_params *archive_params;
+       struct backup_params *backup_params;
+       const char *stripe_filler;
+
+       /* List of defined tags */
+       struct dm_list tags;
+       int hosttags;
+
+       char system_dir[PATH_MAX];
+       char dev_dir[PATH_MAX];
+       char proc_dir[PATH_MAX];
+       char sysfs_dir[PATH_MAX]; /* FIXME Use global value instead. */
+};
+
+/*
+ * system_dir may be NULL to use the default value.
+ * The environment variable LVM_SYSTEM_DIR always takes precedence.
+ */
+struct cmd_context *create_toolcontext(unsigned is_long_lived,
+                                      const char *system_dir);
+void destroy_toolcontext(struct cmd_context *cmd);
+int refresh_toolcontext(struct cmd_context *cmd);
+int refresh_filters(struct cmd_context *cmd);
+int config_files_changed(struct cmd_context *cmd);
+int init_lvmcache_orphans(struct cmd_context *cmd);
+
+struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format);
+
+#endif
diff --git a/lib/config/config.c b/lib/config/config.c
new file mode 100644 (file)
index 0000000..72908f2
--- /dev/null
@@ -0,0 +1,1382 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "config.h"
+#include "crc.h"
+#include "device.h"
+#include "str_list.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#define SECTION_B_CHAR '{'
+#define SECTION_E_CHAR '}'
+
+enum {
+       TOK_INT,
+       TOK_FLOAT,
+       TOK_STRING,             /* Single quotes */
+       TOK_STRING_ESCAPED,     /* Double quotes */
+       TOK_EQ,
+       TOK_SECTION_B,
+       TOK_SECTION_E,
+       TOK_ARRAY_B,
+       TOK_ARRAY_E,
+       TOK_IDENTIFIER,
+       TOK_COMMA,
+       TOK_EOF
+};
+
+struct parser {
+       const char *fb, *fe;            /* file limits */
+
+       int t;                  /* token limits and type */
+       const char *tb, *te;
+
+       int fd;                 /* descriptor for file being parsed */
+       int line;               /* line number we are on */
+
+       struct dm_pool *mem;
+};
+
+struct cs {
+       struct config_tree cft;
+       struct dm_pool *mem;
+       time_t timestamp;
+       char *filename;
+       int exists;
+       int keep_open;
+       struct device *dev;
+};
+
+struct output_line {
+       FILE *fp;
+       struct dm_pool *mem;
+       putline_fn putline;
+       void *putline_baton;
+};
+
+static void _get_token(struct parser *p, int tok_prev);
+static void _eat_space(struct parser *p);
+static struct config_node *_file(struct parser *p);
+static struct config_node *_section(struct parser *p);
+static struct config_value *_value(struct parser *p);
+static struct config_value *_type(struct parser *p);
+static int _match_aux(struct parser *p, int t);
+static struct config_value *_create_value(struct dm_pool *mem);
+static struct config_node *_create_node(struct dm_pool *mem);
+static char *_dup_tok(struct parser *p);
+
+static const int sep = '/';
+
+#define MAX_INDENT 32
+
+#define match(t) do {\
+   if (!_match_aux(p, (t))) {\
+       log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
+                 p->tb - p->fb + 1, p->line); \
+      return 0;\
+   } \
+} while(0);
+
+static int _tok_match(const char *str, const char *b, const char *e)
+{
+       while (*str && (b != e)) {
+               if (*str++ != *b++)
+                       return 0;
+       }
+
+       return !(*str || (b != e));
+}
+
+/*
+ * public interface
+ */
+struct config_tree *create_config_tree(const char *filename, int keep_open)
+{
+       struct cs *c;
+       struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
+
+       if (!mem) {
+               log_error("Failed to allocate config pool.");
+               return 0;
+       }
+
+       if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
+               log_error("Failed to allocate config tree.");
+               dm_pool_destroy(mem);
+               return 0;
+       }
+
+       c->mem = mem;
+       c->cft.root = (struct config_node *) NULL;
+       c->timestamp = 0;
+       c->exists = 0;
+       c->keep_open = keep_open;
+       c->dev = 0;
+       if (filename)
+               c->filename = dm_pool_strdup(c->mem, filename);
+       return &c->cft;
+}
+
+void destroy_config_tree(struct config_tree *cft)
+{
+       struct cs *c = (struct cs *) cft;
+
+       if (c->dev)
+               dev_close(c->dev);
+
+       dm_pool_destroy(c->mem);
+}
+
+static int _parse_config_file(struct parser *p, struct config_tree *cft)
+{
+       p->tb = p->te = p->fb;
+       p->line = 1;
+       _get_token(p, TOK_SECTION_E);
+       if (!(cft->root = _file(p)))
+               return_0;
+
+       return 1;
+}
+
+struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute__((unused)),
+                                                  const char *config_settings)
+{
+       struct cs *c;
+       struct config_tree *cft;
+       struct parser *p;
+
+       if (!(cft = create_config_tree(NULL, 0)))
+               return_NULL;
+
+       c = (struct cs *) cft;
+       if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
+               log_error("Failed to allocate config tree parser.");
+               destroy_config_tree(cft);
+               return NULL;
+       }
+
+       p->mem = c->mem;
+       p->fb = config_settings;
+       p->fe = config_settings + strlen(config_settings);
+
+       if (!_parse_config_file(p, cft)) {
+               destroy_config_tree(cft);
+               return_NULL;
+       }
+
+       return cft;
+}
+
+int override_config_tree_from_string(struct cmd_context *cmd,
+                                    const char *config_settings)
+{
+       if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) {
+               log_error("Failed to set overridden configuration entries.");
+               return 1;
+       }
+
+       return 0;
+}
+
+int read_config_fd(struct config_tree *cft, struct device *dev,
+                  off_t offset, size_t size, off_t offset2, size_t size2,
+                  checksum_fn_t checksum_fn, uint32_t checksum)
+{
+       struct cs *c = (struct cs *) cft;
+       struct parser *p;
+       int r = 0;
+       int use_mmap = 1;
+       off_t mmap_offset = 0;
+       char *buf = NULL;
+
+       if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
+               return_0;
+       p->mem = c->mem;
+
+       /* Only use mmap with regular files */
+       if (!(dev->flags & DEV_REGULAR) || size2)
+               use_mmap = 0;
+
+       if (use_mmap) {
+               mmap_offset = offset % lvm_getpagesize();
+               /* memory map the file */
+               p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
+                            MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
+               if (p->fb == (caddr_t) (-1)) {
+                       log_sys_error("mmap", dev_name(dev));
+                       goto out;
+               }
+               p->fb = p->fb + mmap_offset;
+       } else {
+               if (!(buf = dm_malloc(size + size2)))
+                       return_0;
+               if (!dev_read_circular(dev, (uint64_t) offset, size,
+                                      (uint64_t) offset2, size2, buf)) {
+                       goto out;
+               }
+               p->fb = buf;
+       }
+
+       if (checksum_fn && checksum !=
+           (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)p->fb, size),
+                        (const uint8_t *)(p->fb + size), size2))) {
+               log_error("%s: Checksum error", dev_name(dev));
+               goto out;
+       }
+
+       p->fe = p->fb + size + size2;
+
+       if (!_parse_config_file(p, cft))
+               goto_out;
+
+       r = 1;
+
+      out:
+       if (!use_mmap)
+               dm_free(buf);
+       else {
+               /* unmap the file */
+               if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
+                       log_sys_error("munmap", dev_name(dev));
+                       r = 0;
+               }
+       }
+
+       return r;
+}
+
+int read_config_file(struct config_tree *cft)
+{
+       struct cs *c = (struct cs *) cft;
+       struct stat info;
+       int r = 1;
+
+       if (stat(c->filename, &info)) {
+               log_sys_error("stat", c->filename);
+               c->exists = 0;
+               return 0;
+       }
+
+       if (!S_ISREG(info.st_mode)) {
+               log_error("%s is not a regular file", c->filename);
+               c->exists = 0;
+               return 0;
+       }
+
+       c->exists = 1;
+
+       if (info.st_size == 0) {
+               log_verbose("%s is empty", c->filename);
+               return 1;
+       }
+
+       if (!c->dev) {
+               if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1)))
+                       return_0;
+
+               if (!dev_open_flags(c->dev, O_RDONLY, 0, 0)) {
+                       c->dev = 0;
+                       return_0;
+               }
+       }
+
+       r = read_config_fd(cft, c->dev, 0, (size_t) info.st_size, 0, 0,
+                          (checksum_fn_t) NULL, 0);
+
+       if (!c->keep_open) {
+               dev_close(c->dev);
+               c->dev = 0;
+       }
+
+       c->timestamp = info.st_ctime;
+
+       return r;
+}
+
+time_t config_file_timestamp(struct config_tree *cft)
+{
+       struct cs *c = (struct cs *) cft;
+
+       return c->timestamp;
+}
+
+/*
+ * Return 1 if config files ought to be reloaded
+ */
+int config_file_changed(struct config_tree *cft)
+{
+       struct cs *c = (struct cs *) cft;
+       struct stat info;
+
+       if (!c->filename)
+               return 0;
+
+       if (stat(c->filename, &info) == -1) {
+               /* Ignore a deleted config file: still use original data */
+               if (errno == ENOENT) {
+                       if (!c->exists)
+                               return 0;
+                       log_very_verbose("Config file %s has disappeared!",
+                                        c->filename);
+                       goto reload;
+               }
+               log_sys_error("stat", c->filename);
+               log_error("Failed to reload configuration files");
+               return 0;
+       }
+
+       if (!S_ISREG(info.st_mode)) {
+               log_error("Configuration file %s is not a regular file",
+                         c->filename);
+               goto reload;
+       }
+
+       /* Unchanged? */
+       if (c->timestamp == info.st_ctime)
+               return 0;
+
+      reload:
+       log_verbose("Detected config file change to %s", c->filename);
+       return 1;
+}
+
+static int _line_start(struct output_line *outline)
+{
+       if (!dm_pool_begin_object(outline->mem, 128)) {
+               log_error("dm_pool_begin_object failed for config line");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _line_append(struct output_line *outline, const char *fmt, ...)
+  __attribute__ ((format(printf, 2, 3)));
+static int _line_append(struct output_line *outline, const char *fmt, ...)
+{
+       char buf[4096];
+       va_list ap;
+       int n;
+
+       va_start(ap, fmt);
+       n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap);
+       va_end(ap);
+
+       if (n < 0 || n > (int) sizeof buf - 1) {
+               log_error("vsnprintf failed for config line");
+               return 0;
+       }
+
+       if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
+               log_error("dm_pool_grow_object failed for config line");
+               return 0;
+       }
+
+       return 1;
+}
+
+#define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
+
+static int _line_end(struct output_line *outline)
+{
+       const char *line;
+
+       if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
+               log_error("dm_pool_grow_object failed for config line");
+               return 0;
+       }
+
+       line = dm_pool_end_object(outline->mem);
+       if (outline->putline)
+               outline->putline(line, outline->putline_baton);
+       else {
+               if (!outline->fp)
+                       log_print("%s", line);
+               else
+                       fprintf(outline->fp, "%s\n", line);
+       }
+
+       return 1;
+}
+
+static int _write_value(struct output_line *outline, const struct config_value *v)
+{
+       char *buf;
+
+       switch (v->type) {
+       case CFG_STRING:
+               if (!(buf = alloca(escaped_len(v->v.str)))) {
+                       log_error("temporary stack allocation for a config "
+                                 "string failed");
+                       return 0;
+               }
+               line_append("\"%s\"", escape_double_quotes(buf, v->v.str));
+               break;
+
+       case CFG_FLOAT:
+               line_append("%f", v->v.r);
+               break;
+
+       case CFG_INT:
+               line_append("%" PRId64, v->v.i);
+               break;
+
+       case CFG_EMPTY_ARRAY:
+               line_append("[]");
+               break;
+
+       default:
+               log_error("_write_value: Unknown value type: %d", v->type);
+
+       }
+
+       return 1;
+}
+
+static int _write_config(const struct config_node *n, int only_one,
+                        struct output_line *outline, int level)
+{
+       char space[MAX_INDENT + 1];
+       int l = (level < MAX_INDENT) ? level : MAX_INDENT;
+       int i;
+
+       if (!n)
+               return 1;
+
+       for (i = 0; i < l; i++)
+               space[i] = '\t';
+       space[i] = '\0';
+
+       do {
+               if (!_line_start(outline))
+                       return_0;
+               line_append("%s%s", space, n->key);
+               if (!n->v) {
+                       /* it's a sub section */
+                       line_append(" {");
+                       if (!_line_end(outline))
+                               return_0;
+                       _write_config(n->child, 0, outline, level + 1);
+                       if (!_line_start(outline))
+                               return_0;
+                       line_append("%s}", space);
+               } else {
+                       /* it's a value */
+                       const struct config_value *v = n->v;
+                       line_append("=");
+                       if (v->next) {
+                               line_append("[");
+                               while (v) {
+                                       if (!_write_value(outline, v))
+                                               return_0;
+                                       v = v->next;
+                                       if (v)
+                                               line_append(", ");
+                               }
+                               line_append("]");
+                       } else
+                               if (!_write_value(outline, v))
+                                       return_0;
+               }
+               if (!_line_end(outline))
+                       return_0;
+               n = n->sib;
+       } while (n && !only_one);
+       /* FIXME: add error checking */
+       return 1;
+}
+
+int write_config_node(const struct config_node *cn, putline_fn putline, void *baton)
+{
+       struct output_line outline;
+       outline.fp = NULL;
+       if (!(outline.mem = dm_pool_create("config_line", 1024)))
+               return_0;
+       outline.putline = putline;
+       outline.putline_baton = baton;
+       if (!_write_config(cn, 0, &outline, 0)) {
+               dm_pool_destroy(outline.mem);
+               return_0;
+       }
+       dm_pool_destroy(outline.mem);
+       return 1;
+}
+
+int write_config_file(struct config_tree *cft, const char *file,
+                     int argc, char **argv)
+{
+       const struct config_node *cn;
+       int r = 1;
+       struct output_line outline;
+       outline.fp = NULL;
+       outline.putline = NULL;
+
+       if (!file)
+               file = "stdout";
+       else if (!(outline.fp = fopen(file, "w"))) {
+               log_sys_error("open", file);
+               return 0;
+       }
+
+       if (!(outline.mem = dm_pool_create("config_line", 1024))) {
+               r = 0;
+               goto_out;
+       }
+
+       log_verbose("Dumping configuration to %s", file);
+       if (!argc) {
+               if (!_write_config(cft->root, 0, &outline, 0)) {
+                       log_error("Failure while writing to %s", file);
+                       r = 0;
+               }
+       } else while (argc--) {
+               if ((cn = find_config_node(cft->root, *argv))) {
+                       if (!_write_config(cn, 1, &outline, 0)) {
+                               log_error("Failure while writing to %s", file);
+                               r = 0;
+                       }
+               } else {
+                       log_error("Configuration node %s not found", *argv);
+                       r = 0;
+               }
+               argv++;
+       }
+
+       dm_pool_destroy(outline.mem);
+
+out:
+       if (outline.fp && lvm_fclose(outline.fp, file)) {
+               stack;
+               r = 0;
+       }
+
+       return r;
+}
+
+/*
+ * parser
+ */
+static struct config_node *_file(struct parser *p)
+{
+       struct config_node *root = NULL, *n, *l = NULL;
+       while (p->t != TOK_EOF) {
+               if (!(n = _section(p)))
+                       return_0;
+
+               if (!root)
+                       root = n;
+               else
+                       l->sib = n;
+               n->parent = root;
+               l = n;
+       }
+       return root;
+}
+
+static struct config_node *_section(struct parser *p)
+{
+       /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
+       struct config_node *root, *n, *l = NULL;
+       if (!(root = _create_node(p->mem)))
+               return_0;
+
+       if (!(root->key = _dup_tok(p)))
+               return_0;
+
+       match(TOK_IDENTIFIER);
+
+       if (p->t == TOK_SECTION_B) {
+               match(TOK_SECTION_B);
+               while (p->t != TOK_SECTION_E) {
+                       if (!(n = _section(p)))
+                               return_0;
+
+                       if (!root->child)
+                               root->child = n;
+                       else
+                               l->sib = n;
+                       n->parent = root;
+                       l = n;
+               }
+               match(TOK_SECTION_E);
+       } else {
+               match(TOK_EQ);
+               if (!(root->v = _value(p)))
+                       return_0;
+       }
+
+       return root;
+}
+
+static struct config_value *_value(struct parser *p)
+{
+       /* '[' TYPE* ']' | TYPE */
+       struct config_value *h = NULL, *l, *ll = NULL;
+       if (p->t == TOK_ARRAY_B) {
+               match(TOK_ARRAY_B);
+               while (p->t != TOK_ARRAY_E) {
+                       if (!(l = _type(p)))
+                               return_0;
+
+                       if (!h)
+                               h = l;
+                       else
+                               ll->next = l;
+                       ll = l;
+
+                       if (p->t == TOK_COMMA)
+                               match(TOK_COMMA);
+               }
+               match(TOK_ARRAY_E);
+               /*
+                * Special case for an empty array.
+                */
+               if (!h) {
+                       if (!(h = _create_value(p->mem)))
+                               return NULL;
+
+                       h->type = CFG_EMPTY_ARRAY;
+               }
+
+       } else
+               h = _type(p);
+
+       return h;
+}
+
+static struct config_value *_type(struct parser *p)
+{
+       /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
+       struct config_value *v = _create_value(p->mem);
+       char *str;
+
+       if (!v)
+               return NULL;
+
+       switch (p->t) {
+       case TOK_INT:
+               v->type = CFG_INT;
+               v->v.i = strtoll(p->tb, NULL, 0);       /* FIXME: check error */
+               match(TOK_INT);
+               break;
+
+       case TOK_FLOAT:
+               v->type = CFG_FLOAT;
+               v->v.r = strtod(p->tb, NULL);   /* FIXME: check error */
+               match(TOK_FLOAT);
+               break;
+
+       case TOK_STRING:
+               v->type = CFG_STRING;
+
+               p->tb++, p->te--;       /* strip "'s */
+               if (!(v->v.str = _dup_tok(p)))
+                       return_0;
+               p->te++;
+               match(TOK_STRING);
+               break;
+
+       case TOK_STRING_ESCAPED:
+               v->type = CFG_STRING;
+
+               p->tb++, p->te--;       /* strip "'s */
+               if (!(str = _dup_tok(p)))
+                       return_0;
+               unescape_double_quotes(str);
+               v->v.str = str;
+               p->te++;
+               match(TOK_STRING_ESCAPED);
+               break;
+
+       default:
+               log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
+                         p->tb - p->fb + 1, p->line);
+               return 0;
+       }
+       return v;
+}
+
+static int _match_aux(struct parser *p, int t)
+{
+       if (p->t != t)
+               return 0;
+
+       _get_token(p, t);
+       return 1;
+}
+
+/*
+ * tokeniser
+ */
+static void _get_token(struct parser *p, int tok_prev)
+{
+       int values_allowed = 0;
+
+       const char *te;
+
+       p->tb = p->te;
+       _eat_space(p);
+       if (p->tb == p->fe || !*p->tb) {
+               p->t = TOK_EOF;
+               return;
+       }
+
+       /* Should next token be interpreted as value instead of identifier? */
+       if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
+           tok_prev == TOK_COMMA)
+               values_allowed = 1;
+
+       p->t = TOK_INT;         /* fudge so the fall through for
+                                  floats works */
+
+       te = p->te;
+       switch (*te) {
+       case SECTION_B_CHAR:
+               p->t = TOK_SECTION_B;
+               te++;
+               break;
+
+       case SECTION_E_CHAR:
+               p->t = TOK_SECTION_E;
+               te++;
+               break;
+
+       case '[':
+               p->t = TOK_ARRAY_B;
+               te++;
+               break;
+
+       case ']':
+               p->t = TOK_ARRAY_E;
+               te++;
+               break;
+
+       case ',':
+               p->t = TOK_COMMA;
+               te++;
+               break;
+
+       case '=':
+               p->t = TOK_EQ;
+               te++;
+               break;
+
+       case '"':
+               p->t = TOK_STRING_ESCAPED;
+               te++;
+               while ((te != p->fe) && (*te) && (*te != '"')) {
+                       if ((*te == '\\') && (te + 1 != p->fe) &&
+                           *(te + 1))
+                               te++;
+                       te++;
+               }
+
+               if ((te != p->fe) && (*te))
+                       te++;
+               break;
+
+       case '\'':
+               p->t = TOK_STRING;
+               te++;
+               while ((te != p->fe) && (*te) && (*te != '\''))
+                       te++;
+
+               if ((te != p->fe) && (*te))
+                       te++;
+               break;
+
+       case '.':
+               p->t = TOK_FLOAT;
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+       case '+':
+       case '-':
+               if (values_allowed) {
+                       te++;
+                       while ((te != p->fe) && (*te)) {
+                               if (*te == '.') {
+                                       if (p->t == TOK_FLOAT)
+                                               break;
+                                       p->t = TOK_FLOAT;
+                               } else if (!isdigit((int) *te))
+                                       break;
+                               te++;
+                       }
+                       break;
+               }
+
+       default:
+               p->t = TOK_IDENTIFIER;
+               while ((te != p->fe) && (*te) && !isspace(*te) &&
+                      (*te != '#') && (*te != '=') &&
+                      (*te != SECTION_B_CHAR) &&
+                      (*te != SECTION_E_CHAR))
+                       te++;
+               break;
+       }
+
+       p->te = te;
+}
+
+static void _eat_space(struct parser *p)
+{
+       while ((p->tb != p->fe) && (*p->tb)) {
+               if (*p->te == '#')
+                       while ((p->te != p->fe) && (*p->te) && (*p->te != '\n'))
+                               p->te++;
+
+               else if (isspace(*p->te)) {
+                       while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) {
+                               if (*p->te == '\n')
+                                       p->line++;
+                               p->te++;
+                       }
+               }
+
+               else
+                       return;
+
+               p->tb = p->te;
+       }
+}
+
+/*
+ * memory management
+ */
+static struct config_value *_create_value(struct dm_pool *mem)
+{
+       return dm_pool_zalloc(mem, sizeof(struct config_value));
+}
+
+static struct config_node *_create_node(struct dm_pool *mem)
+{
+       return dm_pool_zalloc(mem, sizeof(struct config_node));
+}
+
+static char *_dup_tok(struct parser *p)
+{
+       size_t len = p->te - p->tb;
+       char *str = dm_pool_alloc(p->mem, len + 1);
+       if (!str)
+               return_0;
+       strncpy(str, p->tb, len);
+       str[len] = '\0';
+       return str;
+}
+
+/*
+ * utility functions
+ */
+static const struct config_node *_find_config_node(const struct config_node *cn,
+                                                  const char *path)
+{
+       const char *e;
+       const struct config_node *cn_found = NULL;
+
+       while (cn) {
+               /* trim any leading slashes */
+               while (*path && (*path == sep))
+                       path++;
+
+               /* find the end of this segment */
+               for (e = path; *e && (*e != sep); e++) ;
+
+               /* hunt for the node */
+               cn_found = NULL;
+               while (cn) {
+                       if (_tok_match(cn->key, path, e)) {
+                               /* Inefficient */
+                               if (!cn_found)
+                                       cn_found = cn;
+                               else
+                                       log_warn("WARNING: Ignoring duplicate"
+                                                " config node: %s ("
+                                                "seeking %s)", cn->key, path);
+                       }
+
+                       cn = cn->sib;
+               }
+
+               if (cn_found && *e)
+                       cn = cn_found->child;
+               else
+                       break;  /* don't move into the last node */
+
+               path = e;
+       }
+
+       return cn_found;
+}
+
+static const struct config_node *_find_first_config_node(const struct config_node *cn1,
+                                                        const struct config_node *cn2,
+                                                        const char *path)
+{
+       const struct config_node *cn;
+
+       if (cn1 && (cn = _find_config_node(cn1, path)))
+               return cn;
+
+       if (cn2 && (cn = _find_config_node(cn2, path)))
+               return cn;
+
+       return NULL;
+}
+
+const struct config_node *find_config_node(const struct config_node *cn,
+                                          const char *path)
+{
+       return _find_config_node(cn, path);
+}
+
+static const char *_find_config_str(const struct config_node *cn1,
+                                   const struct config_node *cn2,
+                                   const char *path, const char *fail)
+{
+       const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+
+       /* Empty strings are ignored */
+       if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) {
+               log_very_verbose("Setting %s to %s", path, n->v->v.str);
+               return n->v->v.str;
+       }
+
+       if (fail)
+               log_very_verbose("%s not found in config: defaulting to %s",
+                                path, fail);
+       return fail;
+}
+
+const char *find_config_str(const struct config_node *cn,
+                           const char *path, const char *fail)
+{
+       return _find_config_str(cn, NULL, path, fail);
+}
+
+static int64_t _find_config_int64(const struct config_node *cn1,
+                                 const struct config_node *cn2,
+                                 const char *path, int64_t fail)
+{
+       const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+
+       if (n && n->v && n->v->type == CFG_INT) {
+               log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
+               return n->v->v.i;
+       }
+
+       log_very_verbose("%s not found in config: defaulting to %" PRId64,
+                        path, fail);
+       return fail;
+}
+
+int find_config_int(const struct config_node *cn, const char *path, int fail)
+{
+       /* FIXME Add log_error message on overflow */
+       return (int) _find_config_int64(cn, NULL, path, (int64_t) fail);
+}
+
+static float _find_config_float(const struct config_node *cn1,
+                               const struct config_node *cn2,
+                               const char *path, float fail)
+{
+       const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+
+       if (n && n->v && n->v->type == CFG_FLOAT) {
+               log_very_verbose("Setting %s to %f", path, n->v->v.r);
+               return n->v->v.r;
+       }
+
+       log_very_verbose("%s not found in config: defaulting to %f",
+                        path, fail);
+
+       return fail;
+
+}
+
+float find_config_float(const struct config_node *cn, const char *path,
+                       float fail)
+{
+       return _find_config_float(cn, NULL, path, fail);
+}
+
+const struct config_node *find_config_tree_node(struct cmd_context *cmd,
+                                         const char *path)
+{
+       return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path);
+}
+
+const char *find_config_tree_str(struct cmd_context *cmd,
+                                const char *path, const char *fail)
+{
+       return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
+}
+
+int find_config_tree_int(struct cmd_context *cmd, const char *path,
+                        int fail)
+{
+       /* FIXME Add log_error message on overflow */
+       return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail);
+}
+
+float find_config_tree_float(struct cmd_context *cmd, const char *path,
+                            float fail)
+{
+       return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
+}
+
+static int _str_in_array(const char *str, const char * const values[])
+{
+       int i;
+
+       for (i = 0; values[i]; i++)
+               if (!strcasecmp(str, values[i]))
+                       return 1;
+
+       return 0;
+}
+
+static int _str_to_bool(const char *str, int fail)
+{
+       const char * const _true_values[]  = { "y", "yes", "on", "true", NULL };
+       const char * const _false_values[] = { "n", "no", "off", "false", NULL };
+
+       if (_str_in_array(str, _true_values))
+               return 1;
+
+       if (_str_in_array(str, _false_values))
+               return 0;
+
+       return fail;
+}
+
+static int _find_config_bool(const struct config_node *cn1,
+                            const struct config_node *cn2,
+                            const char *path, int fail)
+{
+       const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+       const struct config_value *v;
+
+       if (!n)
+               return fail;
+
+       v = n->v;
+
+       switch (v->type) {
+       case CFG_INT:
+               return v->v.i ? 1 : 0;
+
+       case CFG_STRING:
+               return _str_to_bool(v->v.str, fail);
+       }
+
+       return fail;
+}
+
+int find_config_bool(const struct config_node *cn, const char *path, int fail)
+{
+       return _find_config_bool(cn, NULL, path, fail);
+}
+
+int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail)
+{
+       return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
+}
+
+int get_config_uint32(const struct config_node *cn, const char *path,
+                     uint32_t *result)
+{
+       const struct config_node *n;
+
+       n = find_config_node(cn, path);
+
+       if (!n || !n->v || n->v->type != CFG_INT)
+               return 0;
+
+       *result = n->v->v.i;
+       return 1;
+}
+
+int get_config_uint64(const struct config_node *cn, const char *path,
+                     uint64_t *result)
+{
+       const struct config_node *n;
+
+       n = find_config_node(cn, path);
+
+       if (!n || !n->v || n->v->type != CFG_INT)
+               return 0;
+
+       *result = (uint64_t) n->v->v.i;
+       return 1;
+}
+
+int get_config_str(const struct config_node *cn, const char *path,
+                  const char **result)
+{
+       const struct config_node *n;
+
+       n = find_config_node(cn, path);
+
+       if (!n || !n->v || n->v->type != CFG_STRING)
+               return 0;
+
+       *result = n->v->v.str;
+       return 1;
+}
+
+/* Insert cn2 after cn1 */
+static void _insert_config_node(struct config_node **cn1,
+                               struct config_node *cn2)
+{
+       if (!*cn1) {
+               *cn1 = cn2;
+               cn2->sib = NULL;
+       } else {
+               cn2->sib = (*cn1)->sib;
+               (*cn1)->sib = cn2;
+       }
+}
+
+/*
+ * Merge section cn2 into section cn1 (which has the same name)
+ * overwriting any existing cn1 nodes with matching names.
+ */
+static void _merge_section(struct config_node *cn1, struct config_node *cn2)
+{
+       struct config_node *cn, *nextn, *oldn;
+       struct config_value *cv;
+
+       for (cn = cn2->child; cn; cn = nextn) {
+               nextn = cn->sib;
+
+               /* Skip "tags" */
+               if (!strcmp(cn->key, "tags"))
+                       continue;
+
+               /* Subsection? */
+               if (!cn->v)
+                       /* Ignore - we don't have any of these yet */
+                       continue;
+               /* Not already present? */
+               if (!(oldn = (struct config_node*)find_config_node(cn1->child, cn->key))) {
+                       _insert_config_node(&cn1->child, cn);
+                       continue;
+               }
+               /* Merge certain value lists */
+               if ((!strcmp(cn1->key, "activation") &&
+                    !strcmp(cn->key, "volume_list")) ||
+                   (!strcmp(cn1->key, "devices") &&
+                    (!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
+                       cv = cn->v;
+                       while (cv->next)
+                               cv = cv->next;
+                       cv->next = oldn->v;
+               }
+
+               /* Replace values */
+               oldn->v = cn->v;
+       }
+}
+
+static int _match_host_tags(struct dm_list *tags, const struct config_node *tn)
+{
+       const struct config_value *tv;
+       const char *str;
+
+       for (tv = tn->v; tv; tv = tv->next) {
+               if (tv->type != CFG_STRING)
+                       continue;
+               str = tv->v.str;
+               if (*str == '@')
+                       str++;
+               if (!*str)
+                       continue;
+               if (str_list_match_item(tags, str))
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Destructively merge a new config tree into an existing one */
+int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
+                     struct config_tree *newdata)
+{
+       const struct config_node *root = cft->root;
+       struct config_node *cn, *nextn, *oldn, *cn2;
+       const struct config_node *tn;
+
+       for (cn = newdata->root; cn; cn = nextn) {
+               nextn = cn->sib;
+               /* Ignore tags section */
+               if (!strcmp(cn->key, "tags"))
+                       continue;
+               /* If there's a tags node, skip if host tags don't match */
+               if ((tn = find_config_node(cn->child, "tags"))) {
+                       if (!_match_host_tags(&cmd->tags, tn))
+                               continue;
+               }
+               if (!(oldn = (struct config_node *)find_config_node(root, cn->key))) {
+                       _insert_config_node(&cft->root, cn);
+                       /* Remove any "tags" nodes */
+                       for (cn2 = cn->child; cn2; cn2 = cn2->sib) {
+                               if (!strcmp(cn2->key, "tags")) {
+                                       cn->child = cn2->sib;
+                                       continue;
+                               }
+                               if (cn2->sib && !strcmp(cn2->sib->key, "tags")) {
+                                       cn2->sib = cn2->sib->sib;
+                                       continue;
+                               }
+                       }
+                       continue;
+               }
+               _merge_section(oldn, cn);
+       }
+
+       return 1;
+}
+
+/*
+ * Convert a token type to the char it represents.
+ */
+static char _token_type_to_char(int type)
+{
+       switch (type) {
+               case TOK_SECTION_B:
+                       return SECTION_B_CHAR;
+               case TOK_SECTION_E:
+                       return SECTION_E_CHAR;
+               default:
+                       return 0;
+       }
+}
+
+/*
+ * Returns:
+ *  # of 'type' tokens in 'str'.
+ */
+static unsigned _count_tokens(const char *str, unsigned len, int type)
+{
+       char c;
+
+       c = _token_type_to_char(type);
+
+       return count_chars(str, len, c);
+}
+
+const char *config_parent_name(const struct config_node *n)
+{
+       return (n->parent ? n->parent->key : "(root)");
+}
+/*
+ * Heuristic function to make a quick guess as to whether a text
+ * region probably contains a valid config "section".  (Useful for
+ * scanning areas of the disk for old metadata.)
+ * Config sections contain various tokens, may contain other sections
+ * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
+ * end (type 'TOK_SECTION_E') tokens.  As a quick heuristic, we just
+ * count the number of begin and end tokens, and see if they are
+ * non-zero and the counts match.
+ * Full validation of the section should be done with another function
+ * (for example, read_config_fd).
+ *
+ * Returns:
+ *  0 - probably is not a valid config section
+ *  1 - probably _is_ a valid config section
+ */
+unsigned maybe_config_section(const char *str, unsigned len)
+{
+       int begin_count;
+       int end_count;
+
+       begin_count = _count_tokens(str, len, TOK_SECTION_B);
+       end_count = _count_tokens(str, len, TOK_SECTION_E);
+
+       if (begin_count && end_count && (begin_count == end_count))
+               return 1;
+       else
+               return 0;
+}
+
+static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
+{
+       struct config_value *new_cv;
+
+       if (!v)
+               return NULL;
+
+       if (!(new_cv = _create_value(mem))) {
+               log_error("Failed to clone config value.");
+               return NULL;
+       }
+
+       new_cv->type = v->type;
+       if (v->type == CFG_STRING) {
+               if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) {
+                       log_error("Failed to clone config string value.");
+                       return NULL;
+               }
+       } else
+               new_cv->v = v->v;
+
+       if (v->next && !(new_cv->next = _clone_config_value(mem, v->next)))
+               return_NULL;
+
+       return new_cv;
+}
+
+struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
+                                     int siblings)
+{
+       struct config_node *new_cn;
+
+       if (!cn)
+               return NULL;
+
+       if (!(new_cn = _create_node(mem))) {
+               log_error("Failed to clone config node.");
+               return NULL;
+       }
+
+       if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) {
+               log_error("Failed to clone config node key.");
+               return NULL;
+       }
+
+       if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) ||
+           (cn->child && !(new_cn->child = clone_config_node(mem, cn->child, 1))) ||
+           (siblings && cn->sib && !(new_cn->sib = clone_config_node(mem, cn->sib, siblings))))
+               return_NULL; /* 'new_cn' released with mem pool */
+
+       return new_cn;
+}
diff --git a/lib/config/config.h b/lib/config/config.h
new file mode 100644 (file)
index 0000000..f70deb0
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_CONFIG_H
+#define _LVM_CONFIG_H
+
+#include "lvm-types.h"
+
+struct device;
+struct cmd_context;
+
+enum {
+       CFG_STRING,
+       CFG_FLOAT,
+       CFG_INT,
+       CFG_EMPTY_ARRAY
+};
+
+struct config_value {
+       int type;
+       union {
+               int64_t i;
+               float r;
+               const char *str;
+       } v;
+       struct config_value *next;      /* for arrays */
+};
+
+struct config_node {
+       const char *key;
+       struct config_node *parent, *sib, *child;
+       struct config_value *v;
+};
+
+struct config_tree {
+       struct config_node *root;
+};
+
+struct config_tree_list {
+       struct dm_list list;
+       struct config_tree *cft;
+};
+
+struct config_tree *create_config_tree(const char *filename, int keep_open);
+struct config_tree *create_config_tree_from_string(struct cmd_context *cmd,
+                                                  const char *config_settings);
+int override_config_tree_from_string(struct cmd_context *cmd,
+                                    const char *config_settings);
+void destroy_config_tree(struct config_tree *cft);
+
+typedef uint32_t (*checksum_fn_t) (uint32_t initial, const uint8_t *buf, uint32_t size);
+
+int read_config_fd(struct config_tree *cft, struct device *dev,
+                  off_t offset, size_t size, off_t offset2, size_t size2,
+                  checksum_fn_t checksum_fn, uint32_t checksum);
+
+int read_config_file(struct config_tree *cft);
+int write_config_file(struct config_tree *cft, const char *file,
+                     int argc, char **argv);
+
+typedef int (*putline_fn)(const char *line, void *baton);
+int write_config_node(const struct config_node *cn, putline_fn putline, void *baton);
+
+time_t config_file_timestamp(struct config_tree *cft);
+int config_file_changed(struct config_tree *cft);
+int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
+                     struct config_tree *newdata);
+
+const struct config_node *find_config_node(const struct config_node *cn,
+                                          const char *path);
+const char *find_config_str(const struct config_node *cn, const char *path,
+                           const char *fail);
+int find_config_int(const struct config_node *cn, const char *path, int fail);
+float find_config_float(const struct config_node *cn, const char *path,
+                       float fail);
+
+/*
+ * These versions check an override tree, if present, first.
+ */
+const struct config_node *find_config_tree_node(struct cmd_context *cmd,
+                                               const char *path);
+const char *find_config_tree_str(struct cmd_context *cmd,
+                                const char *path, const char *fail);
+int find_config_tree_int(struct cmd_context *cmd, const char *path,
+                        int fail);
+float find_config_tree_float(struct cmd_context *cmd, const char *path,
+                            float fail);
+
+/*
+ * Understands (0, ~0), (y, n), (yes, no), (on,
+ * off), (true, false).
+ */
+int find_config_bool(const struct config_node *cn, const char *path, int fail);
+int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail);
+
+int get_config_uint32(const struct config_node *cn, const char *path,
+                     uint32_t *result);
+
+int get_config_uint64(const struct config_node *cn, const char *path,
+                     uint64_t *result);
+
+int get_config_str(const struct config_node *cn, const char *path,
+                  const char **result);
+
+unsigned maybe_config_section(const char *str, unsigned len);
+
+const char *config_parent_name(const struct config_node *n);
+
+struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
+                                     int siblings);
+#endif
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
new file mode 100644 (file)
index 0000000..a640112
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_DEFAULTS_H
+#define _LVM_DEFAULTS_H
+
+#define DEFAULT_PE_ALIGN 2048
+#define DEFAULT_PE_ALIGN_OLD 128
+
+#define DEFAULT_ARCHIVE_ENABLED 1
+#define DEFAULT_BACKUP_ENABLED 1
+
+#define DEFAULT_CACHE_FILE_PREFIX ""
+
+#define DEFAULT_ARCHIVE_DAYS 30
+#define DEFAULT_ARCHIVE_NUMBER 10
+
+#define DEFAULT_DEV_DIR "/dev"
+#define DEFAULT_PROC_DIR "/proc"
+#define DEFAULT_SYSFS_SCAN 1
+#define DEFAULT_MD_COMPONENT_DETECTION 1
+#define DEFAULT_MD_CHUNK_ALIGNMENT 1
+#define DEFAULT_IGNORE_SUSPENDED_DEVICES 1
+#define DEFAULT_DISABLE_AFTER_ERROR_COUNT 0
+#define DEFAULT_REQUIRE_RESTOREFILE_WITH_UUID 1
+#define DEFAULT_DATA_ALIGNMENT_OFFSET_DETECTION 1
+#define DEFAULT_DATA_ALIGNMENT_DETECTION 1
+
+#define DEFAULT_LOCKING_LIB "liblvm2clusterlock.so"
+#define DEFAULT_FALLBACK_TO_LOCAL_LOCKING 1
+#define DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING 1
+#define DEFAULT_WAIT_FOR_LOCKS 1
+#define DEFAULT_PRIORITISE_WRITE_LOCKS 1
+#define DEFAULT_USE_MLOCKALL 0
+#define DEFAULT_METADATA_READ_ONLY 0
+
+#define DEFAULT_MIRRORLOG "disk"
+#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
+#define DEFAULT_MIRROR_IMAGE_FAULT_POLICY "remove"
+#define DEFAULT_MIRROR_MAX_IMAGES 8 /* limited by kernel DM_KCOPYD_MAX_REGIONS */
+#define DEFAULT_DMEVENTD_MIRROR_LIB "libdevmapper-event-lvm2mirror.so"
+#define DEFAULT_DMEVENTD_SNAPSHOT_LIB "libdevmapper-event-lvm2snapshot.so"
+#define DEFAULT_DMEVENTD_MONITOR 1
+#define DEFAULT_BACKGROUND_POLLING 1
+
+#define DEFAULT_UMASK 0077
+
+#ifdef LVM1_FALLBACK
+#  define DEFAULT_FALLBACK_TO_LVM1 1
+#else
+#  define DEFAULT_FALLBACK_TO_LVM1 0
+#endif
+
+#define DEFAULT_FORMAT "lvm2"
+
+#define DEFAULT_STRIPESIZE 64  /* KB */
+#define DEFAULT_PVMETADATAIGNORE 0
+#define DEFAULT_PVMETADATAIGNORE_STR "n"
+#define DEFAULT_PVMETADATASIZE 255
+#define DEFAULT_PVMETADATACOPIES 1
+#define DEFAULT_VGMETADATACOPIES 0
+#define DEFAULT_LABELSECTOR UINT64_C(1)
+#define DEFAULT_READ_AHEAD "auto"
+#define DEFAULT_UDEV_RULES 1
+#define DEFAULT_UDEV_SYNC 0
+#define DEFAULT_EXTENT_SIZE 4096       /* In KB */
+#define DEFAULT_MAX_PV 0
+#define DEFAULT_MAX_LV 0
+#define DEFAULT_ALLOC_POLICY ALLOC_NORMAL
+#define DEFAULT_CLUSTERED 0
+
+#define DEFAULT_MSG_PREFIX "  "
+#define DEFAULT_CMD_NAME 0
+#define DEFAULT_OVERWRITE 0
+
+#ifndef DEFAULT_LOG_FACILITY
+#  define DEFAULT_LOG_FACILITY LOG_USER
+#endif
+
+#define DEFAULT_SYSLOG 1
+#define DEFAULT_VERBOSE 0
+#define DEFAULT_LOGLEVEL 0
+#define DEFAULT_INDENT 1
+#define DEFAULT_ABORT_ON_INTERNAL_ERRORS 0
+#define DEFAULT_UNITS "h"
+#define DEFAULT_SUFFIX 1
+#define DEFAULT_HOSTTAGS 0
+
+#ifndef DEFAULT_SI_UNIT_CONSISTENCY
+#  define DEFAULT_SI_UNIT_CONSISTENCY 1
+#endif
+
+#ifdef DEVMAPPER_SUPPORT
+#  define DEFAULT_ACTIVATION 1
+#  define DEFAULT_RESERVED_MEMORY 8192
+#  define DEFAULT_RESERVED_STACK 256
+#  define DEFAULT_PROCESS_PRIORITY -18
+#else
+#  define DEFAULT_ACTIVATION 0
+#endif
+
+#define DEFAULT_STRIPE_FILLER "error"
+#define DEFAULT_MIRROR_REGION_SIZE 512 /* KB */
+#define DEFAULT_INTERVAL 15
+
+#ifdef READLINE_SUPPORT
+#  define DEFAULT_MAX_HISTORY 100
+#endif
+
+#define DEFAULT_MAX_ERROR_COUNT        NO_DEV_ERROR_COUNT_LIMIT
+
+#define DEFAULT_REP_ALIGNED 1
+#define DEFAULT_REP_BUFFERED 1
+#define DEFAULT_REP_COLUMNS_AS_ROWS 0
+#define DEFAULT_REP_HEADINGS 1
+#define DEFAULT_REP_PREFIXES 0
+#define DEFAULT_REP_QUOTED 1
+#define DEFAULT_REP_SEPARATOR " "
+
+#define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,origin,snap_percent,move_pv,mirror_log,copy_percent,convert_lv"
+#define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
+#define DEFAULT_PVS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free"
+#define DEFAULT_SEGS_COLS "lv_name,vg_name,lv_attr,stripes,segtype,seg_size"
+#define DEFAULT_PVSEGS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
+
+#define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,origin,snap_percent,move_pv,copy_percent,mirror_log,convert_lv,lv_uuid"
+#define DEFAULT_VGS_COLS_VERB "vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid"
+#define DEFAULT_PVS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,dev_size,pv_uuid"
+#define DEFAULT_SEGS_COLS_VERB "lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize"
+#define DEFAULT_PVSEGS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
+
+#define DEFAULT_LVS_SORT "vg_name,lv_name"
+#define DEFAULT_VGS_SORT "vg_name"
+#define DEFAULT_PVS_SORT "pv_name"
+#define DEFAULT_SEGS_SORT "vg_name,lv_name,seg_start"
+#define DEFAULT_PVSEGS_SORT "pv_name,pvseg_start"
+
+#define DEFAULT_MIRROR_DEVICE_FAULT_POLICY "remove"
+#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
+#define DEFAULT_SNAPSHOT_AUTOEXTEND_THRESHOLD 100
+#define DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT 20
+
+#endif                         /* _LVM_DEFAULTS_H */
diff --git a/lib/datastruct/btree.c b/lib/datastruct/btree.c
new file mode 100644 (file)
index 0000000..bfcdca6
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "btree.h"
+
+struct node {
+       uint32_t key;
+       struct node *l, *r, *p;
+
+       void *data;
+};
+
+struct btree {
+       struct dm_pool *mem;
+       struct node *root;
+};
+
+struct btree *btree_create(struct dm_pool *mem)
+{
+       struct btree *t = dm_pool_alloc(mem, sizeof(*t));
+
+       if (t) {
+               t->mem = mem;
+               t->root = NULL;
+       }
+
+       return t;
+}
+
+/*
+ * Shuffle the bits in a key, to try and remove
+ * any ordering.
+ */
+static uint32_t _shuffle(uint32_t k)
+{
+#if 1
+       return ((k & 0xff) << 24 |
+               (k & 0xff00) << 8 |
+               (k & 0xff0000) >> 8 | (k & 0xff000000) >> 24);
+#else
+       return k;
+#endif
+}
+
+static struct node **_lookup(struct node *const *c, uint32_t key,
+                            struct node **p)
+{
+       *p = NULL;
+       while (*c) {
+               *p = *c;
+               if ((*c)->key == key)
+                       break;
+
+               if (key < (*c)->key)
+                       c = &(*c)->l;
+
+               else
+                       c = &(*c)->r;
+       }
+
+       return (struct node **)c;
+}
+
+void *btree_lookup(const struct btree *t, uint32_t k)
+{
+       uint32_t key = _shuffle(k);
+       struct node *p, **c = _lookup(&t->root, key, &p);
+       return (*c) ? (*c)->data : NULL;
+}
+
+int btree_insert(struct btree *t, uint32_t k, void *data)
+{
+       uint32_t key = _shuffle(k);
+       struct node *p, **c = _lookup(&t->root, key, &p), *n;
+
+       if (!*c) {
+               if (!(n = dm_pool_alloc(t->mem, sizeof(*n))))
+                       return_0;
+
+               n->key = key;
+               n->data = data;
+               n->l = n->r = NULL;
+               n->p = p;
+
+               *c = n;
+       }
+
+       return 1;
+}
+
+void *btree_get_data(const struct btree_iter *it)
+{
+       return ((const struct node *) it)->data;
+}
+
+static struct node *_left(struct node *n)
+{
+       while (n->l)
+               n = n->l;
+       return n;
+}
+
+struct btree_iter *btree_first(const struct btree *t)
+{
+       if (!t->root)
+               return NULL;
+
+       return (struct btree_iter *) _left(t->root);
+}
+
+struct btree_iter *btree_next(const struct btree_iter *it)
+{
+       struct node *n = (struct node *) it;
+       uint32_t k = n->key;
+
+       if (n->r)
+               return (struct btree_iter *) _left(n->r);
+
+       do
+               n = n->p;
+       while (n && k > n->key);
+
+       return (struct btree_iter *) n;
+}
diff --git a/lib/datastruct/btree.h b/lib/datastruct/btree.h
new file mode 100644 (file)
index 0000000..b64f3c7
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_BTREE_H
+#define _LVM_BTREE_H
+
+struct btree;
+
+struct btree *btree_create(struct dm_pool *mem);
+
+void *btree_lookup(const struct btree *t, uint32_t k);
+int btree_insert(struct btree *t, uint32_t k, void *data);
+
+struct btree_iter;
+void *btree_get_data(const struct btree_iter *it);
+
+struct btree_iter *btree_first(const struct btree *t);
+struct btree_iter *btree_next(const struct btree_iter *it);
+
+#endif
diff --git a/lib/datastruct/lvm-types.h b/lib/datastruct/lvm-types.h
new file mode 100644 (file)
index 0000000..5358850
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TYPES_H
+#define _LVM_TYPES_H
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+/* Define some portable printing types */
+#define PRIsize_t "zu"
+#define PRIptrdiff_t "td"
+#define PRIpid_t PRId32
+
+struct str_list {
+       struct dm_list list;
+       const char *str;
+};
+
+#endif
diff --git a/lib/datastruct/str_list.c b/lib/datastruct/str_list.c
new file mode 100644 (file)
index 0000000..81575cb
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "str_list.h"
+
+struct dm_list *str_list_create(struct dm_pool *mem)
+{
+       struct dm_list *sl;
+
+       if (!(sl = dm_pool_alloc(mem, sizeof(struct dm_list)))) {
+               log_errno(ENOMEM, "str_list allocation failed");
+               return NULL;
+       }
+
+       dm_list_init(sl);
+
+       return sl;
+}
+
+int str_list_add(struct dm_pool *mem, struct dm_list *sll, const char *str)
+{
+       struct str_list *sln;
+
+       if (!str)
+               return_0;
+
+       /* Already in list? */
+       if (str_list_match_item(sll, str))
+               return 1;
+
+       if (!(sln = dm_pool_alloc(mem, sizeof(*sln))))
+               return_0;
+
+       sln->str = str;
+       dm_list_add(sll, &sln->list);
+
+       return 1;
+}
+
+int str_list_del(struct dm_list *sll, const char *str)
+{
+       struct dm_list *slh, *slht;
+
+       dm_list_iterate_safe(slh, slht, sll) {
+               if (!strcmp(str, dm_list_item(slh, struct str_list)->str))
+                        dm_list_del(slh);
+       }
+
+       return 1;
+}
+
+int str_list_dup(struct dm_pool *mem, struct dm_list *sllnew,
+                const struct dm_list *sllold)
+{
+       struct str_list *sl;
+
+       dm_list_init(sllnew);
+
+       dm_list_iterate_items(sl, sllold) {
+               if (!str_list_add(mem, sllnew, dm_pool_strdup(mem, sl->str)))
+                       return_0;
+       }
+
+       return 1;
+}
+
+/*
+ * Is item on list?
+ */
+int str_list_match_item(const struct dm_list *sll, const char *str)
+{
+       struct str_list *sl;
+
+       dm_list_iterate_items(sl, sll)
+           if (!strcmp(str, sl->str))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Is at least one item on both lists?
+ * If tag_matched is non-NULL, it is set to the tag that matched.
+ */
+int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, const char **tag_matched)
+{
+       struct str_list *sl;
+
+       dm_list_iterate_items(sl, sll)
+               if (str_list_match_item(sll2, sl->str)) {
+                       if (tag_matched)
+                               *tag_matched = sl->str;
+                       return 1;
+               }
+
+       return 0;
+}
+
+/*
+ * Do both lists contain the same set of items?
+ */
+int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2)
+{
+       struct str_list *sl;
+
+       if (dm_list_size(sll) != dm_list_size(sll2))
+               return 0;
+
+       dm_list_iterate_items(sl, sll)
+           if (!str_list_match_item(sll2, sl->str))
+               return 0;
+
+       return 1;
+}
diff --git a/lib/datastruct/str_list.h b/lib/datastruct/str_list.h
new file mode 100644 (file)
index 0000000..f7180e2
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_STR_LIST_H
+#define _LVM_STR_LIST_H
+
+struct dm_list *str_list_create(struct dm_pool *mem);
+int str_list_add(struct dm_pool *mem, struct dm_list *sll, const char *str);
+int str_list_del(struct dm_list *sll, const char *str);
+int str_list_match_item(const struct dm_list *sll, const char *str);
+int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, const char **tag_matched);
+int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2);
+int str_list_dup(struct dm_pool *mem, struct dm_list *sllnew,
+                const struct dm_list *sllold);
+
+#endif
diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
new file mode 100644 (file)
index 0000000..962aa1e
--- /dev/null
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "dev-cache.h"
+#include "lvm-types.h"
+#include "btree.h"
+#include "filter.h"
+#include "filter-persistent.h"
+#include "toolcontext.h"
+
+#include <unistd.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+struct dev_iter {
+       struct btree_iter *current;
+       struct dev_filter *filter;
+};
+
+struct dir_list {
+       struct dm_list list;
+       char dir[0];
+};
+
+static struct {
+       struct dm_pool *mem;
+       struct dm_hash_table *names;
+       struct btree *devices;
+       struct dm_regex *preferred_names_matcher;
+       const char *dev_dir;
+
+       int has_scanned;
+       struct dm_list dirs;
+       struct dm_list files;
+
+} _cache;
+
+#define _alloc(x) dm_pool_zalloc(_cache.mem, (x))
+#define _free(x) dm_pool_free(_cache.mem, (x))
+#define _strdup(x) dm_pool_strdup(_cache.mem, (x))
+
+static int _insert(const char *path, int rec);
+
+struct device *dev_create_file(const char *filename, struct device *dev,
+                              struct str_list *alias, int use_malloc)
+{
+       int allocate = !dev;
+
+       if (allocate) {
+               if (use_malloc) {
+                       if (!(dev = dm_malloc(sizeof(*dev)))) {
+                               log_error("struct device allocation failed");
+                               return NULL;
+                       }
+                       if (!(alias = dm_malloc(sizeof(*alias)))) {
+                               log_error("struct str_list allocation failed");
+                               dm_free(dev);
+                               return NULL;
+                       }
+                       if (!(alias->str = dm_strdup(filename))) {
+                               log_error("filename strdup failed");
+                               dm_free(dev);
+                               dm_free(alias);
+                               return NULL;
+                       }
+                       dev->flags = DEV_ALLOCED;
+               } else {
+                       if (!(dev = _alloc(sizeof(*dev)))) {
+                               log_error("struct device allocation failed");
+                               return NULL;
+                       }
+                       if (!(alias = _alloc(sizeof(*alias)))) {
+                               log_error("struct str_list allocation failed");
+                               _free(dev);
+                               return NULL;
+                       }
+                       if (!(alias->str = _strdup(filename))) {
+                               log_error("filename strdup failed");
+                               return NULL;
+                       }
+               }
+       } else if (!(alias->str = dm_strdup(filename))) {
+               log_error("filename strdup failed");
+               return NULL;
+       }
+
+       dev->flags |= DEV_REGULAR;
+       dm_list_init(&dev->aliases);
+       dm_list_add(&dev->aliases, &alias->list);
+       dev->end = UINT64_C(0);
+       dev->dev = 0;
+       dev->fd = -1;
+       dev->open_count = 0;
+       dev->error_count = 0;
+       dev->max_error_count = NO_DEV_ERROR_COUNT_LIMIT;
+       dev->block_size = -1;
+       dev->read_ahead = -1;
+       memset(dev->pvid, 0, sizeof(dev->pvid));
+       dm_list_init(&dev->open_list);
+
+       return dev;
+}
+
+static struct device *_dev_create(dev_t d)
+{
+       struct device *dev;
+
+       if (!(dev = _alloc(sizeof(*dev)))) {
+               log_error("struct device allocation failed");
+               return NULL;
+       }
+       dev->flags = 0;
+       dm_list_init(&dev->aliases);
+       dev->dev = d;
+       dev->fd = -1;
+       dev->open_count = 0;
+       dev->max_error_count = dev_disable_after_error_count();
+       dev->block_size = -1;
+       dev->read_ahead = -1;
+       dev->end = UINT64_C(0);
+       memset(dev->pvid, 0, sizeof(dev->pvid));
+       dm_list_init(&dev->open_list);
+
+       return dev;
+}
+
+void dev_set_preferred_name(struct str_list *sl, struct device *dev)
+{
+       /*
+        * Don't interfere with ordering specified in config file.
+        */
+       if (_cache.preferred_names_matcher)
+               return;
+
+       log_debug("%s: New preferred name", sl->str);
+       dm_list_del(&sl->list);
+       dm_list_add_h(&dev->aliases, &sl->list);
+}
+
+/*
+ * Check whether path0 or path1 contains the subpath. The path that
+ * *does not* contain the subpath wins (return 0 or 1). If both paths
+ * contain the subpath, return -1. If none of them contains the subpath,
+ * return -2.
+ */
+static int _builtin_preference(const char *path0, const char *path1,
+                              size_t skip_prefix_count, const char *subpath)
+{
+       size_t subpath_len;
+       int r0, r1;
+
+       subpath_len = strlen(subpath);
+
+       r0 = !strncmp(path0 + skip_prefix_count, subpath, subpath_len);
+       r1 = !strncmp(path1 + skip_prefix_count, subpath, subpath_len);
+
+       if (!r0 && r1)
+               /* path0 does not have the subpath - it wins */
+               return 0;
+       else if (r0 && !r1)
+               /* path1 does not have the subpath - it wins */
+               return 1;
+       else if (r0 && r1)
+               /* both of them have the subpath */
+               return -1;
+
+       /* no path has the subpath */
+       return -2;
+}
+
+static int _apply_builtin_path_preference_rules(const char *path0, const char *path1)
+{
+       size_t devdir_len;
+       int r;
+
+       devdir_len = strlen(_cache.dev_dir);
+
+       if (!strncmp(path0, _cache.dev_dir, devdir_len) &&
+           !strncmp(path1, _cache.dev_dir, devdir_len)) {
+               /*
+                * We're trying to achieve the ordering:
+                *      /dev/block/ < /dev/dm-* < /dev/disk/ < /dev/mapper/ < anything else
+                */
+
+               /* Prefer any other path over /dev/block/ path. */
+               if ((r = _builtin_preference(path0, path1, devdir_len, "block/")) >= -1)
+                       return r;
+
+               /* Prefer any other path over /dev/dm-* path. */
+               if ((r = _builtin_preference(path0, path1, devdir_len, "dm-")) >= -1)
+                       return r;
+
+               /* Prefer any other path over /dev/disk/ path. */
+               if ((r = _builtin_preference(path0, path1, devdir_len, "disk/")) >= -1)
+                       return r;
+
+               /* Prefer any other path over /dev/mapper/ path. */
+               if ((r = _builtin_preference(path0, path1, 0, dm_dir())) >= -1)
+                       return r;
+       }
+
+       return -1;
+}
+
+/* Return 1 if we prefer path1 else return 0 */
+static int _compare_paths(const char *path0, const char *path1)
+{
+       int slash0 = 0, slash1 = 0;
+       int m0, m1;
+       const char *p;
+       char p0[PATH_MAX], p1[PATH_MAX];
+       char *s0, *s1;
+       struct stat stat0, stat1;
+       int r;
+
+       /*
+        * FIXME Better to compare patterns one-at-a-time against all names.
+        */
+       if (_cache.preferred_names_matcher) {
+               m0 = dm_regex_match(_cache.preferred_names_matcher, path0);
+               m1 = dm_regex_match(_cache.preferred_names_matcher, path1);
+
+               if (m0 != m1) {
+                       if (m0 < 0)
+                               return 1;
+                       if (m1 < 0)
+                               return 0;
+                       if (m0 < m1)
+                               return 1;
+                       if (m1 < m0)
+                               return 0;
+               }
+       }
+
+       /* Apply built-in preference rules first. */
+       if ((r = _apply_builtin_path_preference_rules(path0, path1)) >= 0)
+               return r;
+
+       /* Return the path with fewer slashes */
+       for (p = path0; p++; p = (const char *) strchr(p, '/'))
+               slash0++;
+
+       for (p = path1; p++; p = (const char *) strchr(p, '/'))
+               slash1++;
+
+       if (slash0 < slash1)
+               return 0;
+       if (slash1 < slash0)
+               return 1;
+
+       strncpy(p0, path0, PATH_MAX);
+       strncpy(p1, path1, PATH_MAX);
+       s0 = &p0[0] + 1;
+       s1 = &p1[0] + 1;
+
+       /* We prefer symlinks - they exist for a reason!
+        * So we prefer a shorter path before the first symlink in the name.
+        * FIXME Configuration option to invert this? */
+       while (s0) {
+               s0 = strchr(s0, '/');
+               s1 = strchr(s1, '/');
+               if (s0) {
+                       *s0 = '\0';
+                       *s1 = '\0';
+               }
+               if (lstat(p0, &stat0)) {
+                       log_sys_very_verbose("lstat", p0);
+                       return 1;
+               }
+               if (lstat(p1, &stat1)) {
+                       log_sys_very_verbose("lstat", p1);
+                       return 0;
+               }
+               if (S_ISLNK(stat0.st_mode) && !S_ISLNK(stat1.st_mode))
+                       return 0;
+               if (!S_ISLNK(stat0.st_mode) && S_ISLNK(stat1.st_mode))
+                       return 1;
+               if (s0) {
+                       *s0++ = '/';
+                       *s1++ = '/';
+               }
+       }
+
+       /* ASCII comparison */
+       if (strcmp(path0, path1) < 0)
+               return 0;
+       else
+               return 1;
+}
+
+static int _add_alias(struct device *dev, const char *path)
+{
+       struct str_list *sl = _alloc(sizeof(*sl));
+       struct str_list *strl;
+       const char *oldpath;
+       int prefer_old = 1;
+
+       if (!sl)
+               return_0;
+
+       /* Is name already there? */
+       dm_list_iterate_items(strl, &dev->aliases) {
+               if (!strcmp(strl->str, path)) {
+                       log_debug("%s: Already in device cache", path);
+                       return 1;
+               }
+       }
+
+       if (!(sl->str = dm_pool_strdup(_cache.mem, path)))
+               return_0;
+
+       if (!dm_list_empty(&dev->aliases)) {
+               oldpath = dm_list_item(dev->aliases.n, struct str_list)->str;
+               prefer_old = _compare_paths(path, oldpath);
+               log_debug("%s: Aliased to %s in device cache%s",
+                         path, oldpath, prefer_old ? "" : " (preferred name)");
+
+       } else
+               log_debug("%s: Added to device cache", path);
+
+       if (prefer_old)
+               dm_list_add(&dev->aliases, &sl->list);
+       else
+               dm_list_add_h(&dev->aliases, &sl->list);
+
+       return 1;
+}
+
+/*
+ * Either creates a new dev, or adds an alias to
+ * an existing dev.
+ */
+static int _insert_dev(const char *path, dev_t d)
+{
+       struct device *dev;
+       static dev_t loopfile_count = 0;
+       int loopfile = 0;
+
+       /* Generate pretend device numbers for loopfiles */
+       if (!d) {
+               if (dm_hash_lookup(_cache.names, path))
+                       return 1;
+               d = ++loopfile_count;
+               loopfile = 1;
+       }
+
+       /* is this device already registered ? */
+       if (!(dev = (struct device *) btree_lookup(_cache.devices,
+                                                  (uint32_t) d))) {
+               /* create new device */
+               if (loopfile) {
+                       if (!(dev = dev_create_file(path, NULL, NULL, 0)))
+                               return_0;
+               } else if (!(dev = _dev_create(d)))
+                       return_0;
+
+               if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
+                       log_error("Couldn't insert device into binary tree.");
+                       _free(dev);
+                       return 0;
+               }
+       }
+
+       if (!loopfile && !_add_alias(dev, path)) {
+               log_error("Couldn't add alias to dev cache.");
+               return 0;
+       }
+
+       if (!dm_hash_insert(_cache.names, path, dev)) {
+               log_error("Couldn't add name to hash in dev cache.");
+               return 0;
+       }
+
+       return 1;
+}
+
+static char *_join(const char *dir, const char *name)
+{
+       size_t len = strlen(dir) + strlen(name) + 2;
+       char *r = dm_malloc(len);
+       if (r)
+               snprintf(r, len, "%s/%s", dir, name);
+
+       return r;
+}
+
+/*
+ * Get rid of extra slashes in the path string.
+ */
+static void _collapse_slashes(char *str)
+{
+       char *ptr;
+       int was_slash = 0;
+
+       for (ptr = str; *ptr; ptr++) {
+               if (*ptr == '/') {
+                       if (was_slash)
+                               continue;
+
+                       was_slash = 1;
+               } else
+                       was_slash = 0;
+               *str++ = *ptr;
+       }
+
+       *str = *ptr;
+}
+
+static int _insert_dir(const char *dir)
+{
+       int n, dirent_count, r = 1;
+       struct dirent **dirent;
+       char *path;
+
+       dirent_count = scandir(dir, &dirent, NULL, alphasort);
+       if (dirent_count > 0) {
+               for (n = 0; n < dirent_count; n++) {
+                       if (dirent[n]->d_name[0] == '.') {
+                               free(dirent[n]);
+                               continue;
+                       }
+
+                       if (!(path = _join(dir, dirent[n]->d_name)))
+                               return_0;
+
+                       _collapse_slashes(path);
+                       r &= _insert(path, 1);
+                       dm_free(path);
+
+                       free(dirent[n]);
+               }
+               free(dirent);
+       }
+
+       return r;
+}
+
+static int _insert_file(const char *path)
+{
+       struct stat info;
+
+       if (stat(path, &info) < 0) {
+               log_sys_very_verbose("stat", path);
+               return 0;
+       }
+
+       if (!S_ISREG(info.st_mode)) {
+               log_debug("%s: Not a regular file", path);
+               return 0;
+       }
+
+       if (!_insert_dev(path, 0))
+               return_0;
+
+       return 1;
+}
+
+static int _insert(const char *path, int rec)
+{
+       struct stat info;
+       int r = 0;
+
+       if (stat(path, &info) < 0) {
+               log_sys_very_verbose("stat", path);
+               return 0;
+       }
+
+       if (S_ISDIR(info.st_mode)) {    /* add a directory */
+               /* check it's not a symbolic link */
+               if (lstat(path, &info) < 0) {
+                       log_sys_very_verbose("lstat", path);
+                       return 0;
+               }
+
+               if (S_ISLNK(info.st_mode)) {
+                       log_debug("%s: Symbolic link to directory", path);
+                       return 0;
+               }
+
+               if (rec)
+                       r = _insert_dir(path);
+
+       } else {                /* add a device */
+               if (!S_ISBLK(info.st_mode)) {
+                       log_debug("%s: Not a block device", path);
+                       return 0;
+               }
+
+               if (!_insert_dev(path, info.st_rdev))
+                       return_0;
+
+               r = 1;
+       }
+
+       return r;
+}
+
+static void _full_scan(int dev_scan)
+{
+       struct dir_list *dl;
+
+       if (_cache.has_scanned && !dev_scan)
+               return;
+
+       dm_list_iterate_items(dl, &_cache.dirs)
+               _insert_dir(dl->dir);
+
+       dm_list_iterate_items(dl, &_cache.files)
+               _insert_file(dl->dir);
+
+       _cache.has_scanned = 1;
+       init_full_scan_done(1);
+}
+
+int dev_cache_has_scanned(void)
+{
+       return _cache.has_scanned;
+}
+
+void dev_cache_scan(int do_scan)
+{
+       if (!do_scan)
+               _cache.has_scanned = 1;
+       else
+               _full_scan(1);
+}
+
+static int _init_preferred_names(struct cmd_context *cmd)
+{
+       const struct config_node *cn;
+       const struct config_value *v;
+       struct dm_pool *scratch = NULL;
+       const char **regex;
+       unsigned count = 0;
+       int i, r = 0;
+
+       _cache.preferred_names_matcher = NULL;
+
+       if (!(cn = find_config_tree_node(cmd, "devices/preferred_names")) ||
+           cn->v->type == CFG_EMPTY_ARRAY) {
+               log_very_verbose("devices/preferred_names not found in config file: "
+                                "using built-in preferences");
+               return 1;
+       }
+
+       for (v = cn->v; v; v = v->next) {
+               if (v->type != CFG_STRING) {
+                       log_error("preferred_names patterns must be enclosed in quotes");
+                       return 0;
+               }
+
+               count++;
+       }
+
+       if (!(scratch = dm_pool_create("preferred device name matcher", 1024)))
+               return_0;
+
+       if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count))) {
+               log_error("Failed to allocate preferred device name "
+                         "pattern list.");
+               goto out;
+       }
+
+       for (v = cn->v, i = count - 1; v; v = v->next, i--) {
+               if (!(regex[i] = dm_pool_strdup(scratch, v->v.str))) {
+                       log_error("Failed to allocate a preferred device name "
+                                 "pattern.");
+                       goto out;
+               }
+       }
+
+       if (!(_cache.preferred_names_matcher =
+               dm_regex_create(_cache.mem, regex, count))) {
+               log_error("Preferred device name pattern matcher creation failed.");
+               goto out;
+       }
+
+       r = 1;
+
+out:
+       dm_pool_destroy(scratch);
+
+       return r;
+}
+
+int dev_cache_init(struct cmd_context *cmd)
+{
+       _cache.names = NULL;
+       _cache.has_scanned = 0;
+
+       if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
+               return_0;
+
+       if (!(_cache.names = dm_hash_create(128))) {
+               dm_pool_destroy(_cache.mem);
+               _cache.mem = 0;
+               return_0;
+       }
+
+       if (!(_cache.devices = btree_create(_cache.mem))) {
+               log_error("Couldn't create binary tree for dev-cache.");
+               goto bad;
+       }
+
+       if (!(_cache.dev_dir = _strdup(cmd->dev_dir))) {
+               log_error("strdup dev_dir failed.");
+               goto bad;
+       }
+
+       dm_list_init(&_cache.dirs);
+       dm_list_init(&_cache.files);
+
+       if (!_init_preferred_names(cmd))
+               goto_bad;
+
+       return 1;
+
+      bad:
+       dev_cache_exit();
+       return 0;
+}
+
+static void _check_closed(struct device *dev)
+{
+       if (dev->fd >= 0)
+               log_error("Device '%s' has been left open.", dev_name(dev));
+}
+
+static void _check_for_open_devices(void)
+{
+       dm_hash_iter(_cache.names, (dm_hash_iterate_fn) _check_closed);
+}
+
+void dev_cache_exit(void)
+{
+       if (_cache.names)
+               _check_for_open_devices();
+
+       if (_cache.preferred_names_matcher)
+               _cache.preferred_names_matcher = NULL;
+
+       if (_cache.mem) {
+               dm_pool_destroy(_cache.mem);
+               _cache.mem = NULL;
+       }
+
+       if (_cache.names) {
+               dm_hash_destroy(_cache.names);
+               _cache.names = NULL;
+       }
+
+       _cache.devices = NULL;
+       _cache.has_scanned = 0;
+       dm_list_init(&_cache.dirs);
+       dm_list_init(&_cache.files);
+}
+
+int dev_cache_add_dir(const char *path)
+{
+       struct dir_list *dl;
+       struct stat st;
+
+       if (stat(path, &st)) {
+               log_error("Ignoring %s: %s", path, strerror(errno));
+               /* But don't fail */
+               return 1;
+       }
+
+       if (!S_ISDIR(st.st_mode)) {
+               log_error("Ignoring %s: Not a directory", path);
+               return 1;
+       }
+
+       if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) {
+               log_error("dir_list allocation failed");
+               return 0;
+       }
+
+       strcpy(dl->dir, path);
+       dm_list_add(&_cache.dirs, &dl->list);
+       return 1;
+}
+
+int dev_cache_add_loopfile(const char *path)
+{
+       struct dir_list *dl;
+       struct stat st;
+
+       if (stat(path, &st)) {
+               log_error("Ignoring %s: %s", path, strerror(errno));
+               /* But don't fail */
+               return 1;
+       }
+
+       if (!S_ISREG(st.st_mode)) {
+               log_error("Ignoring %s: Not a regular file", path);
+               return 1;
+       }
+
+       if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) {
+               log_error("dir_list allocation failed for file");
+               return 0;
+       }
+
+       strcpy(dl->dir, path);
+       dm_list_add(&_cache.files, &dl->list);
+       return 1;
+}
+
+/* Check cached device name is still valid before returning it */
+/* This should be a rare occurrence */
+/* set quiet if the cache is expected to be out-of-date */
+/* FIXME Make rest of code pass/cache struct device instead of dev_name */
+const char *dev_name_confirmed(struct device *dev, int quiet)
+{
+       struct stat buf;
+       const char *name;
+       int r;
+
+       if ((dev->flags & DEV_REGULAR))
+               return dev_name(dev);
+
+       while ((r = stat(name = dm_list_item(dev->aliases.n,
+                                         struct str_list)->str, &buf)) ||
+              (buf.st_rdev != dev->dev)) {
+               if (r < 0) {
+                       if (quiet)
+                               log_sys_debug("stat", name);
+                       else
+                               log_sys_error("stat", name);
+               }
+               if (quiet)
+                       log_debug("Path %s no longer valid for device(%d,%d)",
+                                 name, (int) MAJOR(dev->dev),
+                                 (int) MINOR(dev->dev));
+               else
+                       log_error("Path %s no longer valid for device(%d,%d)",
+                                 name, (int) MAJOR(dev->dev),
+                                 (int) MINOR(dev->dev));
+
+               /* Remove the incorrect hash entry */
+               dm_hash_remove(_cache.names, name);
+
+               /* Leave list alone if there isn't an alternative name */
+               /* so dev_name will always find something to return. */
+               /* Otherwise add the name to the correct device. */
+               if (dm_list_size(&dev->aliases) > 1) {
+                       dm_list_del(dev->aliases.n);
+                       if (!r)
+                               _insert(name, 0);
+                       continue;
+               }
+
+               /* Scanning issues this inappropriately sometimes. */
+               log_debug("Aborting - please provide new pathname for what "
+                         "used to be %s", name);
+               return NULL;
+       }
+
+       return dev_name(dev);
+}
+
+struct device *dev_cache_get(const char *name, struct dev_filter *f)
+{
+       struct stat buf;
+       struct device *d = (struct device *) dm_hash_lookup(_cache.names, name);
+
+       if (d && (d->flags & DEV_REGULAR))
+               return d;
+
+       /* If the entry's wrong, remove it */
+       if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) {
+               dm_hash_remove(_cache.names, name);
+               d = NULL;
+       }
+
+       if (!d) {
+               _insert(name, 0);
+               d = (struct device *) dm_hash_lookup(_cache.names, name);
+               if (!d) {
+                       _full_scan(0);
+                       d = (struct device *) dm_hash_lookup(_cache.names, name);
+               }
+       }
+
+       return (d && (!f || (d->flags & DEV_REGULAR) ||
+                     f->passes_filter(f, d))) ? d : NULL;
+}
+
+struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan)
+{
+       struct dev_iter *di = dm_malloc(sizeof(*di));
+
+       if (!di) {
+               log_error("dev_iter allocation failed");
+               return NULL;
+       }
+
+       if (dev_scan && !trust_cache()) {
+               /* Flag gets reset between each command */
+               if (!full_scan_done())
+                       persistent_filter_wipe(f); /* Calls _full_scan(1) */
+       } else
+               _full_scan(0);
+
+       di->current = btree_first(_cache.devices);
+       di->filter = f;
+       di->filter->use_count++;
+
+       return di;
+}
+
+void dev_iter_destroy(struct dev_iter *iter)
+{
+       iter->filter->use_count--;
+       dm_free(iter);
+}
+
+static struct device *_iter_next(struct dev_iter *iter)
+{
+       struct device *d = btree_get_data(iter->current);
+       iter->current = btree_next(iter->current);
+       return d;
+}
+
+struct device *dev_iter_get(struct dev_iter *iter)
+{
+       while (iter->current) {
+               struct device *d = _iter_next(iter);
+               if (!iter->filter || (d->flags & DEV_REGULAR) ||
+                   iter->filter->passes_filter(iter->filter, d))
+                       return d;
+       }
+
+       return NULL;
+}
+
+void dev_reset_error_count(struct cmd_context *cmd)
+{
+       struct dev_iter *iter;
+       struct device *dev;
+
+       if (!(iter = dev_iter_create(cmd->filter, 0))) {
+               log_error("Resetting device error count failed");
+               return;
+       }
+
+       for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter))
+               dev->error_count = 0;
+
+       dev_iter_destroy(iter);
+}
+
+int dev_fd(struct device *dev)
+{
+       return dev->fd;
+}
+
+const char *dev_name(const struct device *dev)
+{
+       return (dev) ? dm_list_item(dev->aliases.n, struct str_list)->str :
+           "unknown device";
+}
diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
new file mode 100644 (file)
index 0000000..c1c86d6
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_DEV_CACHE_H
+#define _LVM_DEV_CACHE_H
+
+#include "device.h"
+
+/*
+ * predicate for devices.
+ */
+struct dev_filter {
+       int (*passes_filter) (struct dev_filter * f, struct device * dev);
+       void (*destroy) (struct dev_filter * f);
+       unsigned use_count;
+       void *private;
+};
+
+/*
+ * The global device cache.
+ */
+struct cmd_context;
+int dev_cache_init(struct cmd_context *cmd);
+void dev_cache_exit(void);
+
+/* Trigger(1) or avoid(0) a scan */
+void dev_cache_scan(int do_scan);
+int dev_cache_has_scanned(void);
+
+int dev_cache_add_dir(const char *path);
+int dev_cache_add_loopfile(const char *path);
+struct device *dev_cache_get(const char *name, struct dev_filter *f);
+
+void dev_set_preferred_name(struct str_list *sl, struct device *dev);
+
+/*
+ * Object for iterating through the cache.
+ */
+struct dev_iter;
+struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan);
+void dev_iter_destroy(struct dev_iter *iter);
+struct device *dev_iter_get(struct dev_iter *iter);
+
+void dev_reset_error_count(struct cmd_context *cmd);
+
+#endif
diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
new file mode 100644 (file)
index 0000000..eb80a89
--- /dev/null
@@ -0,0 +1,751 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "lvm-types.h"
+#include "device.h"
+#include "metadata.h"
+#include "lvmcache.h"
+#include "memlock.h"
+#include "locking.h"
+
+#include <limits.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef linux
+#  define u64 uint64_t         /* Missing without __KERNEL__ */
+#  undef WNOHANG               /* Avoid redefinition */
+#  undef WUNTRACED             /* Avoid redefinition */
+#  include <linux/fs.h>                /* For block ioctl definitions */
+#  define BLKSIZE_SHIFT SECTOR_SHIFT
+#  ifndef BLKGETSIZE64         /* fs.h out-of-date */
+#    define BLKGETSIZE64 _IOR(0x12, 114, size_t)
+#  endif /* BLKGETSIZE64 */
+#else
+#  include <sys/disk.h>
+#  define BLKBSZGET DKIOCGETBLOCKSIZE
+#  define BLKSSZGET DKIOCGETBLOCKSIZE
+#  define BLKGETSIZE64 DKIOCGETBLOCKCOUNT
+#  define BLKFLSBUF DKIOCSYNCHRONIZECACHE
+#  define BLKSIZE_SHIFT 0
+#endif
+
+#ifdef O_DIRECT_SUPPORT
+#  ifndef O_DIRECT
+#    error O_DIRECT support configured but O_DIRECT definition not found in headers
+#  endif
+#endif
+
+static DM_LIST_INIT(_open_devices);
+
+/*-----------------------------------------------------------------
+ * The standard io loop that keeps submitting an io until it's
+ * all gone.
+ *---------------------------------------------------------------*/
+static int _io(struct device_area *where, void *buffer, int should_write)
+{
+       int fd = dev_fd(where->dev);
+       ssize_t n = 0;
+       size_t total = 0;
+
+       if (fd < 0) {
+               log_error("Attempt to read an unopened device (%s).",
+                         dev_name(where->dev));
+               return 0;
+       }
+
+       /*
+        * Skip all writes in test mode.
+        */
+       if (should_write && test_mode())
+               return 1;
+
+       if (where->size > SSIZE_MAX) {
+               log_error("Read size too large: %" PRIu64, where->size);
+               return 0;
+       }
+
+       if (lseek(fd, (off_t) where->start, SEEK_SET) < 0) {
+               log_error("%s: lseek %" PRIu64 " failed: %s",
+                         dev_name(where->dev), (uint64_t) where->start,
+                         strerror(errno));
+               return 0;
+       }
+
+       while (total < (size_t) where->size) {
+               do
+                       n = should_write ?
+                           write(fd, buffer, (size_t) where->size - total) :
+                           read(fd, buffer, (size_t) where->size - total);
+               while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
+
+               if (n < 0)
+                       log_error_once("%s: %s failed after %" PRIu64 " of %" PRIu64
+                                      " at %" PRIu64 ": %s", dev_name(where->dev),
+                                      should_write ? "write" : "read",
+                                      (uint64_t) total,
+                                      (uint64_t) where->size,
+                                      (uint64_t) where->start, strerror(errno));
+
+               if (n <= 0)
+                       break;
+
+               total += n;
+               buffer += n;
+       }
+
+       return (total == (size_t) where->size);
+}
+
+/*-----------------------------------------------------------------
+ * LVM2 uses O_DIRECT when performing metadata io, which requires
+ * block size aligned accesses.  If any io is not aligned we have
+ * to perform the io via a bounce buffer, obviously this is quite
+ * inefficient.
+ *---------------------------------------------------------------*/
+
+/*
+ * Get the sector size from an _open_ device.
+ */
+static int _get_block_size(struct device *dev, unsigned int *size)
+{
+       const char *name = dev_name(dev);
+
+       if ((dev->block_size == -1)) {
+               if (ioctl(dev_fd(dev), BLKBSZGET, &dev->block_size) < 0) {
+                       log_sys_error("ioctl BLKBSZGET", name);
+                       return 0;
+               }
+               log_debug("%s: block size is %u bytes", name, dev->block_size);
+       }
+
+       *size = (unsigned int) dev->block_size;
+
+       return 1;
+}
+
+/*
+ * Widens a region to be an aligned region.
+ */
+static void _widen_region(unsigned int block_size, struct device_area *region,
+                         struct device_area *result)
+{
+       uint64_t mask = block_size - 1, delta;
+       memcpy(result, region, sizeof(*result));
+
+       /* adjust the start */
+       delta = result->start & mask;
+       if (delta) {
+               result->start -= delta;
+               result->size += delta;
+       }
+
+       /* adjust the end */
+       delta = (result->start + result->size) & mask;
+       if (delta)
+               result->size += block_size - delta;
+}
+
+static int _aligned_io(struct device_area *where, void *buffer,
+                      int should_write)
+{
+       void *bounce, *bounce_buf;
+       unsigned int block_size = 0;
+       uintptr_t mask;
+       struct device_area widened;
+       int r = 0;
+
+       if (!(where->dev->flags & DEV_REGULAR) &&
+           !_get_block_size(where->dev, &block_size))
+               return_0;
+
+       if (!block_size)
+               block_size = lvm_getpagesize();
+
+       _widen_region(block_size, where, &widened);
+
+       /* Do we need to use a bounce buffer? */
+       mask = block_size - 1;
+       if (!memcmp(where, &widened, sizeof(widened)) &&
+           !((uintptr_t) buffer & mask))
+               return _io(where, buffer, should_write);
+
+       /* Allocate a bounce buffer with an extra block */
+       if (!(bounce_buf = bounce = dm_malloc((size_t) widened.size + block_size))) {
+               log_error("Bounce buffer malloc failed");
+               return 0;
+       }
+
+       /*
+        * Realign start of bounce buffer (using the extra sector)
+        */
+       if (((uintptr_t) bounce) & mask)
+               bounce = (void *) ((((uintptr_t) bounce) + mask) & ~mask);
+
+       /* channel the io through the bounce buffer */
+       if (!_io(&widened, bounce, 0)) {
+               if (!should_write)
+                       goto_out;
+               /* FIXME pre-extend the file */
+               memset(bounce, '\n', widened.size);
+       }
+
+       if (should_write) {
+               memcpy(bounce + (where->start - widened.start), buffer,
+                      (size_t) where->size);
+
+               /* ... then we write */
+               if (!(r = _io(&widened, bounce, 1)))
+                       stack;
+                       
+               goto out;
+       }
+
+       memcpy(buffer, bounce + (where->start - widened.start),
+              (size_t) where->size);
+
+       r = 1;
+
+out:
+       dm_free(bounce_buf);
+       return r;
+}
+
+static int _dev_get_size_file(const struct device *dev, uint64_t *size)
+{
+       const char *name = dev_name(dev);
+       struct stat info;
+
+       if (stat(name, &info)) {
+               log_sys_error("stat", name);
+               return 0;
+       }
+
+       *size = info.st_size;
+       *size >>= SECTOR_SHIFT; /* Convert to sectors */
+
+       log_very_verbose("%s: size is %" PRIu64 " sectors", name, *size);
+
+       return 1;
+}
+
+static int _dev_get_size_dev(const struct device *dev, uint64_t *size)
+{
+       int fd;
+       const char *name = dev_name(dev);
+
+       if ((fd = open(name, O_RDONLY)) < 0) {
+               log_sys_error("open", name);
+               return 0;
+       }
+
+       if (ioctl(fd, BLKGETSIZE64, size) < 0) {
+               log_sys_error("ioctl BLKGETSIZE64", name);
+               if (close(fd))
+                       log_sys_error("close", name);
+               return 0;
+       }
+
+       *size >>= BLKSIZE_SHIFT;        /* Convert to sectors */
+       if (close(fd))
+               log_sys_error("close", name);
+
+       log_very_verbose("%s: size is %" PRIu64 " sectors", name, *size);
+
+       return 1;
+}
+
+static int _dev_read_ahead_dev(struct device *dev, uint32_t *read_ahead)
+{
+       long read_ahead_long;
+
+       if (dev->read_ahead != -1) {
+               *read_ahead = (uint32_t) dev->read_ahead;
+               return 1;
+       }
+
+       if (!dev_open(dev))
+               return_0;
+
+       if (ioctl(dev->fd, BLKRAGET, &read_ahead_long) < 0) {
+               log_sys_error("ioctl BLKRAGET", dev_name(dev));
+               if (!dev_close(dev))
+                       stack;
+               return 0;
+       }
+
+       if (!dev_close(dev))
+               stack;
+
+       *read_ahead = (uint32_t) read_ahead_long;
+       dev->read_ahead = read_ahead_long;
+
+       log_very_verbose("%s: read_ahead is %u sectors",
+                        dev_name(dev), *read_ahead);
+
+       return 1;
+}
+
+/*-----------------------------------------------------------------
+ * Public functions
+ *---------------------------------------------------------------*/
+
+int dev_get_size(const struct device *dev, uint64_t *size)
+{
+       if (!dev)
+               return 0;
+
+       if ((dev->flags & DEV_REGULAR))
+               return _dev_get_size_file(dev, size);
+       else
+               return _dev_get_size_dev(dev, size);
+}
+
+int dev_get_read_ahead(struct device *dev, uint32_t *read_ahead)
+{
+       if (!dev)
+               return 0;
+
+       if (dev->flags & DEV_REGULAR) {
+               *read_ahead = 0;
+               return 1;
+       }
+
+       return _dev_read_ahead_dev(dev, read_ahead);
+}
+
+/* FIXME Unused
+int dev_get_sectsize(struct device *dev, uint32_t *size)
+{
+       int fd;
+       int s;
+       const char *name = dev_name(dev);
+
+       if ((fd = open(name, O_RDONLY)) < 0) {
+               log_sys_error("open", name);
+               return 0;
+       }
+
+       if (ioctl(fd, BLKSSZGET, &s) < 0) {
+               log_sys_error("ioctl BLKSSZGET", name);
+               if (close(fd))
+                       log_sys_error("close", name);
+               return 0;
+       }
+
+       if (close(fd))
+               log_sys_error("close", name);
+
+       *size = (uint32_t) s;
+
+       log_very_verbose("%s: sector size is %" PRIu32 " bytes", name, *size);
+
+       return 1;
+}
+*/
+
+void dev_flush(struct device *dev)
+{
+       if (!(dev->flags & DEV_REGULAR) && ioctl(dev->fd, BLKFLSBUF, 0) >= 0)
+               return;
+
+       if (fsync(dev->fd) >= 0)
+               return;
+
+       sync();
+}
+
+int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+{
+       struct stat buf;
+       const char *name;
+       int need_excl = 0, need_rw = 0;
+
+       if ((flags & O_ACCMODE) == O_RDWR)
+               need_rw = 1;
+
+       if ((flags & O_EXCL))
+               need_excl = 1;
+
+       if (dev->fd >= 0) {
+               if (((dev->flags & DEV_OPENED_RW) || !need_rw) &&
+                   ((dev->flags & DEV_OPENED_EXCL) || !need_excl)) {
+                       dev->open_count++;
+                       return 1;
+               }
+
+               if (dev->open_count && !need_excl) {
+                       /* FIXME Ensure we never get here */
+                       log_error(INTERNAL_ERROR "%s already opened read-only",
+                                dev_name(dev));
+                       dev->open_count++;
+               }
+
+               dev_close_immediate(dev);
+       }
+
+       if (memlock())
+               /* FIXME Make this log_error */
+               log_verbose("dev_open(%s) called while suspended",
+                        dev_name(dev));
+
+       if (dev->flags & DEV_REGULAR)
+               name = dev_name(dev);
+       else if (!(name = dev_name_confirmed(dev, quiet)))
+               return_0;
+
+       if (!(dev->flags & DEV_REGULAR)) {
+               if (stat(name, &buf) < 0) {
+                       log_sys_error("%s: stat failed", name);
+                       return 0;
+               }
+               if (buf.st_rdev != dev->dev) {
+                       log_error("%s: device changed", name);
+                       return 0;
+               }
+       }
+
+#ifdef O_DIRECT_SUPPORT
+       if (direct) {
+               if (!(dev->flags & DEV_O_DIRECT_TESTED))
+                       dev->flags |= DEV_O_DIRECT;
+
+               if ((dev->flags & DEV_O_DIRECT))
+                       flags |= O_DIRECT;
+       }
+#endif
+
+#ifdef O_NOATIME
+       /* Don't update atime on device inodes */
+       if (!(dev->flags & DEV_REGULAR))
+               flags |= O_NOATIME;
+#endif
+
+       if ((dev->fd = open(name, flags, 0777)) < 0) {
+#ifdef O_DIRECT_SUPPORT
+               if (direct && !(dev->flags & DEV_O_DIRECT_TESTED)) {
+                       flags &= ~O_DIRECT;
+                       if ((dev->fd = open(name, flags, 0777)) >= 0) {
+                               dev->flags &= ~DEV_O_DIRECT;
+                               log_debug("%s: Not using O_DIRECT", name);
+                               goto opened;
+                       }
+               }
+#endif
+               if (quiet)
+                       log_sys_debug("open", name);
+               else
+                       log_sys_error("open", name);
+               return 0;
+       }
+
+#ifdef O_DIRECT_SUPPORT
+      opened:
+       if (direct)
+               dev->flags |= DEV_O_DIRECT_TESTED;
+#endif
+       dev->open_count++;
+       dev->flags &= ~DEV_ACCESSED_W;
+
+       if (need_rw)
+               dev->flags |= DEV_OPENED_RW;
+       else
+               dev->flags &= ~DEV_OPENED_RW;
+
+       if (need_excl)
+               dev->flags |= DEV_OPENED_EXCL;
+       else
+               dev->flags &= ~DEV_OPENED_EXCL;
+
+       if (!(dev->flags & DEV_REGULAR) &&
+           ((fstat(dev->fd, &buf) < 0) || (buf.st_rdev != dev->dev))) {
+               log_error("%s: fstat failed: Has device name changed?", name);
+               dev_close_immediate(dev);
+               return 0;
+       }
+
+#ifndef O_DIRECT_SUPPORT
+       if (!(dev->flags & DEV_REGULAR))
+               dev_flush(dev);
+#endif
+
+       if ((flags & O_CREAT) && !(flags & O_TRUNC))
+               dev->end = lseek(dev->fd, (off_t) 0, SEEK_END);
+
+       dm_list_add(&_open_devices, &dev->open_list);
+
+       log_debug("Opened %s %s%s%s", dev_name(dev),
+                 dev->flags & DEV_OPENED_RW ? "RW" : "RO",
+                 dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
+                 dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
+
+       return 1;
+}
+
+int dev_open_quiet(struct device *dev)
+{
+       int flags;
+
+       flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
+
+       return dev_open_flags(dev, flags, 1, 1);
+}
+
+int dev_open(struct device *dev)
+{
+       int flags;
+
+       flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
+
+       return dev_open_flags(dev, flags, 1, 0);
+}
+
+int dev_test_excl(struct device *dev)
+{
+       int flags;
+       int r;
+
+       flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
+       flags |= O_EXCL;
+
+       r = dev_open_flags(dev, flags, 1, 1);
+       if (r)
+               dev_close_immediate(dev);
+
+       return r;
+}
+
+static void _close(struct device *dev)
+{
+       if (close(dev->fd))
+               log_sys_error("close", dev_name(dev));
+       dev->fd = -1;
+       dev->block_size = -1;
+       dm_list_del(&dev->open_list);
+
+       log_debug("Closed %s", dev_name(dev));
+
+       if (dev->flags & DEV_ALLOCED) {
+               dm_free((void *) dm_list_item(dev->aliases.n, struct str_list)->
+                        str);
+               dm_free(dev->aliases.n);
+               dm_free(dev);
+       }
+}
+
+static int _dev_close(struct device *dev, int immediate)
+{
+       struct lvmcache_info *info;
+
+       if (dev->fd < 0) {
+               log_error("Attempt to close device '%s' "
+                         "which is not open.", dev_name(dev));
+               return 0;
+       }
+
+#ifndef O_DIRECT_SUPPORT
+       if (dev->flags & DEV_ACCESSED_W)
+               dev_flush(dev);
+#endif
+
+       if (dev->open_count > 0)
+               dev->open_count--;
+
+       if (immediate && dev->open_count)
+               log_debug("%s: Immediate close attempt while still referenced",
+                         dev_name(dev));
+
+       /* Close unless device is known to belong to a locked VG */
+       if (immediate ||
+           (dev->open_count < 1 &&
+            (!(info = info_from_pvid(dev->pvid, 0)) ||
+             !info->vginfo ||
+             !vgname_is_locked(info->vginfo->vgname))))
+               _close(dev);
+
+       return 1;
+}
+
+int dev_close(struct device *dev)
+{
+       return _dev_close(dev, 0);
+}
+
+int dev_close_immediate(struct device *dev)
+{
+       return _dev_close(dev, 1);
+}
+
+void dev_close_all(void)
+{
+       struct dm_list *doh, *doht;
+       struct device *dev;
+
+       dm_list_iterate_safe(doh, doht, &_open_devices) {
+               dev = dm_list_struct_base(doh, struct device, open_list);
+               if (dev->open_count < 1)
+                       _close(dev);
+       }
+}
+
+static inline int _dev_is_valid(struct device *dev)
+{
+       return (dev->max_error_count == NO_DEV_ERROR_COUNT_LIMIT ||
+               dev->error_count < dev->max_error_count);
+}
+
+static void _dev_inc_error_count(struct device *dev)
+{
+       if (++dev->error_count == dev->max_error_count)
+               log_warn("WARNING: Error counts reached a limit of %d. "
+                        "Device %s was disabled",
+                        dev->max_error_count, dev_name(dev));
+}
+
+int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer)
+{
+       struct device_area where;
+       int ret;
+
+       if (!dev->open_count)
+               return_0;
+
+       if (!_dev_is_valid(dev))
+               return 0;
+
+       where.dev = dev;
+       where.start = offset;
+       where.size = len;
+
+       ret = _aligned_io(&where, buffer, 0);
+       if (!ret)
+               _dev_inc_error_count(dev);
+
+       return ret;
+}
+
+/*
+ * Read from 'dev' into 'buf', possibly in 2 distinct regions, denoted
+ * by (offset,len) and (offset2,len2).  Thus, the total size of
+ * 'buf' should be len+len2.
+ */
+int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
+                     uint64_t offset2, size_t len2, void *buf)
+{
+       if (!dev_read(dev, offset, len, buf)) {
+               log_error("Read from %s failed", dev_name(dev));
+               return 0;
+       }
+
+       /*
+        * The second region is optional, and allows for
+        * a circular buffer on the device.
+        */
+       if (!len2)
+               return 1;
+
+       if (!dev_read(dev, offset2, len2, buf + len)) {
+               log_error("Circular read from %s failed",
+                         dev_name(dev));
+               return 0;
+       }
+
+       return 1;
+}
+
+/* FIXME If O_DIRECT can't extend file, dev_extend first; dev_truncate after.
+ *       But fails if concurrent processes writing
+ */
+
+/* FIXME pre-extend the file */
+int dev_append(struct device *dev, size_t len, void *buffer)
+{
+       int r;
+
+       if (!dev->open_count)
+               return_0;
+
+       r = dev_write(dev, dev->end, len, buffer);
+       dev->end += (uint64_t) len;
+
+#ifndef O_DIRECT_SUPPORT
+       dev_flush(dev);
+#endif
+       return r;
+}
+
+int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer)
+{
+       struct device_area where;
+       int ret;
+
+       if (!dev->open_count)
+               return_0;
+
+       if (!_dev_is_valid(dev))
+               return 0;
+
+       where.dev = dev;
+       where.start = offset;
+       where.size = len;
+
+       dev->flags |= DEV_ACCESSED_W;
+
+       ret = _aligned_io(&where, buffer, 1);
+       if (!ret)
+               _dev_inc_error_count(dev);
+
+       return ret;
+}
+
+int dev_set(struct device *dev, uint64_t offset, size_t len, int value)
+{
+       size_t s;
+       char buffer[4096] __attribute__((aligned(8)));
+
+       if (!dev_open(dev))
+               return_0;
+
+       if ((offset % SECTOR_SIZE) || (len % SECTOR_SIZE))
+               log_debug("Wiping %s at %" PRIu64 " length %" PRIsize_t,
+                         dev_name(dev), offset, len);
+       else
+               log_debug("Wiping %s at sector %" PRIu64 " length %" PRIsize_t
+                         " sectors", dev_name(dev), offset >> SECTOR_SHIFT,
+                         len >> SECTOR_SHIFT);
+
+       memset(buffer, value, sizeof(buffer));
+       while (1) {
+               s = len > sizeof(buffer) ? sizeof(buffer) : len;
+               if (!dev_write(dev, offset, s, buffer))
+                       break;
+
+               len -= s;
+               if (!len)
+                       break;
+
+               offset += s;
+       }
+
+       dev->flags |= DEV_ACCESSED_W;
+
+       if (!dev_close(dev))
+               stack;
+
+       return (len == 0);
+}
diff --git a/lib/device/dev-luks.c b/lib/device/dev-luks.c
new file mode 100644 (file)
index 0000000..6337992
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+
+#define LUKS_SIGNATURE "LUKS\xba\xbe"
+#define LUKS_SIGNATURE_SIZE 6
+
+int dev_is_luks(struct device *dev, uint64_t *signature)
+{
+       char buf[LUKS_SIGNATURE_SIZE];
+       int ret = -1;
+
+       if (!dev_open(dev)) {
+               stack;
+               return -1;
+       }
+
+       *signature = 0;
+
+       if (!dev_read(dev, 0, LUKS_SIGNATURE_SIZE, buf))
+               goto_out;
+
+       ret = memcmp(buf, LUKS_SIGNATURE, LUKS_SIGNATURE_SIZE) ? 0 : 1;
+
+out:
+       if (!dev_close(dev))
+               stack;
+
+       return ret;
+}
diff --git a/lib/device/dev-md.c b/lib/device/dev-md.c
new file mode 100644 (file)
index 0000000..89b9341
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2004 Luca Berra
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "filter.h"
+
+#ifdef linux
+
+/* Lifted from <linux/raid/md_p.h> because of difficulty including it */
+
+#define MD_SB_MAGIC 0xa92b4efc
+#define MD_RESERVED_BYTES (64 * 1024ULL)
+#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512)
+#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) \
+                               - MD_RESERVED_SECTORS)
+
+static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
+{
+       uint32_t md_magic;
+
+       /* Version 1 is little endian; version 0.90.0 is machine endian */
+       if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) &&
+           ((md_magic == xlate32(MD_SB_MAGIC)) ||
+            (md_magic == MD_SB_MAGIC)))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Calculate the position of the superblock.
+ * It is always aligned to a 4K boundary and
+ * depending on minor_version, it can be:
+ * 0: At least 8K, but less than 12K, from end of device
+ * 1: At start of device
+ * 2: 4K from start of device.
+ */
+typedef enum {
+       MD_MINOR_VERSION_MIN,
+       MD_MINOR_V0 = MD_MINOR_VERSION_MIN,
+       MD_MINOR_V1,
+       MD_MINOR_V2,
+       MD_MINOR_VERSION_MAX = MD_MINOR_V2
+} md_minor_version_t;
+
+static uint64_t _v1_sb_offset(uint64_t size, md_minor_version_t minor_version)
+{
+       uint64_t uninitialized_var(sb_offset);
+
+       switch(minor_version) {
+       case MD_MINOR_V0:
+               sb_offset = (size - 8 * 2) & ~(4 * 2 - 1ULL);
+               break;
+       case MD_MINOR_V1:
+               sb_offset = 0;
+               break;
+       case MD_MINOR_V2:
+               sb_offset = 4 * 2;
+               break;
+       }
+       sb_offset <<= SECTOR_SHIFT;
+
+       return sb_offset;
+}
+
+/*
+ * Returns -1 on error
+ */
+int dev_is_md(struct device *dev, uint64_t *sb)
+{
+       int ret = 1;
+       md_minor_version_t minor;
+       uint64_t size, sb_offset;
+
+       if (!dev_get_size(dev, &size)) {
+               stack;
+               return -1;
+       }
+
+       if (size < MD_RESERVED_SECTORS * 2)
+               return 0;
+
+       if (!dev_open(dev)) {
+               stack;
+               return -1;
+       }
+
+       /* Check if it is an md component device. */
+       /* Version 0.90.0 */
+       sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
+       if (_dev_has_md_magic(dev, sb_offset))
+               goto out;
+
+       minor = MD_MINOR_VERSION_MIN;
+       /* Version 1, try v1.0 -> v1.2 */
+       do {
+               sb_offset = _v1_sb_offset(size, minor);
+               if (_dev_has_md_magic(dev, sb_offset))
+                       goto out;
+       } while (++minor <= MD_MINOR_VERSION_MAX);
+
+       ret = 0;
+
+out:
+       if (!dev_close(dev))
+               stack;
+
+       if (ret && sb)
+               *sb = sb_offset;
+
+       return ret;
+}
+
+static int _md_sysfs_attribute_snprintf(char *path, size_t size,
+                                       const char *sysfs_dir,
+                                       struct device *blkdev,
+                                       const char *attribute)
+{
+       struct stat info;
+       dev_t dev = blkdev->dev;
+       int ret = -1;
+
+       if (!sysfs_dir || !*sysfs_dir)
+               return ret;
+
+       if (MAJOR(dev) == blkext_major()) {
+               /* lookup parent MD device from blkext partition */
+               if (!get_primary_dev(sysfs_dir, blkdev, &dev))
+                       return ret;
+       }
+
+       if (MAJOR(dev) != md_major())
+               return ret;
+
+       ret = dm_snprintf(path, size, "%s/dev/block/%d:%d/md/%s", sysfs_dir,
+                         (int)MAJOR(dev), (int)MINOR(dev), attribute);
+       if (ret < 0) {
+               log_error("dm_snprintf md %s failed", attribute);
+               return ret;
+       }
+
+       if (stat(path, &info) == -1) {
+               if (errno != ENOENT) {
+                       log_sys_error("stat", path);
+                       return ret;
+               }
+               /* old sysfs structure */
+               ret = dm_snprintf(path, size, "%s/block/md%d/md/%s",
+                                 sysfs_dir, (int)MINOR(dev), attribute);
+               if (ret < 0) {
+                       log_error("dm_snprintf old md %s failed", attribute);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+static int _md_sysfs_attribute_scanf(const char *sysfs_dir,
+                                    struct device *dev,
+                                    const char *attribute_name,
+                                    const char *attribute_fmt,
+                                    void *attribute_value)
+{
+       char path[PATH_MAX+1], buffer[64];
+       FILE *fp;
+       int ret = 0;
+
+       if (_md_sysfs_attribute_snprintf(path, PATH_MAX, sysfs_dir,
+                                        dev, attribute_name) < 0)
+               return ret;
+
+       if (!(fp = fopen(path, "r"))) {
+               log_sys_error("fopen", path);
+               return ret;
+       }
+
+       if (!fgets(buffer, sizeof(buffer), fp)) {
+               log_sys_error("fgets", path);
+               goto out;
+       }
+
+       if ((ret = sscanf(buffer, attribute_fmt, attribute_value)) != 1) {
+               log_error("%s sysfs attr %s not in expected format: %s",
+                         dev_name(dev), attribute_name, buffer);
+               goto out;
+       }
+
+out:
+       if (fclose(fp))
+               log_sys_error("fclose", path);
+
+       return ret;
+}
+
+/*
+ * Retrieve chunk size from md device using sysfs.
+ */
+static unsigned long dev_md_chunk_size(const char *sysfs_dir,
+                                      struct device *dev)
+{
+       const char *attribute = "chunk_size";
+       unsigned long chunk_size_bytes = 0UL;
+
+       if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
+                                     "%lu", &chunk_size_bytes) != 1)
+               return 0;
+
+       log_very_verbose("Device %s %s is %lu bytes.",
+                        dev_name(dev), attribute, chunk_size_bytes);
+
+       return chunk_size_bytes >> SECTOR_SHIFT;
+}
+
+/*
+ * Retrieve level from md device using sysfs.
+ */
+static int dev_md_level(const char *sysfs_dir, struct device *dev)
+{
+       const char *attribute = "level";
+       int level = -1;
+
+       if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
+                                     "raid%d", &level) != 1)
+               return -1;
+
+       log_very_verbose("Device %s %s is raid%d.",
+                        dev_name(dev), attribute, level);
+
+       return level;
+}
+
+/*
+ * Retrieve raid_disks from md device using sysfs.
+ */
+static int dev_md_raid_disks(const char *sysfs_dir, struct device *dev)
+{
+       const char *attribute = "raid_disks";
+       int raid_disks = 0;
+
+       if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
+                                     "%d", &raid_disks) != 1)
+               return 0;
+
+       log_very_verbose("Device %s %s is %d.",
+                        dev_name(dev), attribute, raid_disks);
+
+       return raid_disks;
+}
+
+/*
+ * Calculate stripe width of md device using its sysfs files.
+ */
+unsigned long dev_md_stripe_width(const char *sysfs_dir, struct device *dev)
+{
+       unsigned long chunk_size_sectors = 0UL;
+       unsigned long stripe_width_sectors = 0UL;
+       int level, raid_disks, data_disks;
+
+       chunk_size_sectors = dev_md_chunk_size(sysfs_dir, dev);
+       if (!chunk_size_sectors)
+               return 0;
+
+       level = dev_md_level(sysfs_dir, dev);
+       if (level < 0)
+               return 0;
+
+       raid_disks = dev_md_raid_disks(sysfs_dir, dev);
+       if (!raid_disks)
+               return 0;
+
+       /* The raid level governs the number of data disks. */
+       switch (level) {
+       case 0:
+               /* striped md does not have any parity disks */
+               data_disks = raid_disks;
+               break;
+       case 1:
+       case 10:
+               /* mirrored md effectively has 1 data disk */
+               data_disks = 1;
+               break;
+       case 4:
+       case 5:
+               /* both raid 4 and 5 have a single parity disk */
+               data_disks = raid_disks - 1;
+               break;
+       case 6:
+               /* raid 6 has 2 parity disks */
+               data_disks = raid_disks - 2;
+               break;
+       default:
+               log_error("Device %s has an unknown md raid level: %d",
+                         dev_name(dev), level);
+               return 0;
+       }
+
+       stripe_width_sectors = chunk_size_sectors * data_disks;
+
+       log_very_verbose("Device %s stripe-width is %lu bytes.",
+                        dev_name(dev),
+                        stripe_width_sectors << SECTOR_SHIFT);
+
+       return stripe_width_sectors;
+}
+
+#else
+
+int dev_is_md(struct device *dev __attribute__((unused)),
+             uint64_t *sb __attribute__((unused)))
+{
+       return 0;
+}
+
+unsigned long dev_md_stripe_width(const char *sysfs_dir __attribute__((unused)),
+                                 struct device *dev  __attribute__((unused)))
+{
+       return 0UL;
+}
+
+#endif
diff --git a/lib/device/dev-swap.c b/lib/device/dev-swap.c
new file mode 100644 (file)
index 0000000..b8ebcca
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "filter.h"
+
+#ifdef linux
+
+#define MAX_PAGESIZE   (64 * 1024)
+#define SIGNATURE_SIZE  10
+
+static int
+_swap_detect_signature(const char *buf)
+{
+       if (memcmp(buf, "SWAP-SPACE", 10) == 0 ||
+            memcmp(buf, "SWAPSPACE2", 10) == 0)
+               return 1;
+
+       if (memcmp(buf, "S1SUSPEND", 9) == 0 ||
+           memcmp(buf, "S2SUSPEND", 9) == 0 ||
+           memcmp(buf, "ULSUSPEND", 9) == 0 ||
+           memcmp(buf, "\xed\xc3\x02\xe9\x98\x56\xe5\x0c", 8) == 0)
+               return 1;
+
+       return 0;
+}
+
+int dev_is_swap(struct device *dev, uint64_t *signature)
+{
+       char buf[10];
+       uint64_t size;
+       int page, ret = 0;
+
+       if (!dev_get_size(dev, &size)) {
+               stack;
+               return -1;
+       }
+
+       if (!dev_open(dev)) {
+               stack;
+               return -1;
+       }
+
+       *signature = 0;
+
+       for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) {
+               /*
+                * skip 32k pagesize since this does not seem to be supported
+                */
+               if (page == 0x8000)
+                       continue;
+               if (size < page)
+                       break;
+               if (!dev_read(dev, page - SIGNATURE_SIZE,
+                             SIGNATURE_SIZE, buf)) {
+                       ret = -1;
+                       break;
+               }
+               if (_swap_detect_signature(buf)) {
+                       *signature = page - SIGNATURE_SIZE;
+                       ret = 1;
+                       break;
+               }
+       }
+
+       if (!dev_close(dev))
+               stack;
+
+       return ret;
+}
+
+#endif
diff --git a/lib/device/device.c b/lib/device/device.c
new file mode 100644 (file)
index 0000000..80b4479
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "lvm-types.h"
+#include "device.h"
+#include "metadata.h"
+#include "filter.h"
+#include "xlate.h"
+
+#include <libgen.h> /* dirname, basename */
+
+/* See linux/genhd.h and fs/partitions/msdos */
+
+#define PART_MAGIC 0xAA55
+#define PART_MAGIC_OFFSET UINT64_C(0x1FE)
+#define PART_OFFSET UINT64_C(0x1BE)
+
+struct partition {
+       uint8_t boot_ind;
+       uint8_t head;
+       uint8_t sector;
+       uint8_t cyl;
+       uint8_t sys_ind;        /* partition type */
+       uint8_t end_head;
+       uint8_t end_sector;
+       uint8_t end_cyl;
+       uint32_t start_sect;
+       uint32_t nr_sects;
+} __attribute__((packed));
+
+static int _is_partitionable(struct device *dev)
+{
+       int parts = max_partitions(MAJOR(dev->dev));
+
+       /* All MD devices are partitionable via blkext (as of 2.6.28) */
+       if (MAJOR(dev->dev) == md_major())
+               return 1;
+
+       if ((parts <= 1) || (MINOR(dev->dev) % parts))
+               return 0;
+
+       return 1;
+}
+
+static int _has_partition_table(struct device *dev)
+{
+       int ret = 0;
+       unsigned p;
+       struct {
+               uint8_t skip[PART_OFFSET];
+               struct partition part[4];
+               uint16_t magic;
+       } __attribute__((packed)) buf; /* sizeof() == SECTOR_SIZE */
+
+       if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
+               return_0;
+
+       /* FIXME Check for other types of partition table too */
+
+       /* Check for msdos partition table */
+       if (buf.magic == xlate16(PART_MAGIC)) {
+               for (p = 0; p < 4; ++p) {
+                       /* Table is invalid if boot indicator not 0 or 0x80 */
+                       if (buf.part[p].boot_ind & 0x7f) {
+                               ret = 0;
+                               break;
+                       }
+                       /* Must have at least one non-empty partition */
+                       if (buf.part[p].nr_sects)
+                               ret = 1;
+               }
+       }
+
+       return ret;
+}
+
+int is_partitioned_dev(struct device *dev)
+{
+       if (!_is_partitionable(dev))
+               return 0;
+
+       return _has_partition_table(dev);
+}
+
+#if 0
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/genhd.h>
+
+int _get_partition_type(struct dev_filter *filter, struct device *d);
+
+#define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
+
+int is_extended_partition(struct device *d)
+{
+       return (MINOR_PART(d) > 4) ? 1 : 0;
+}
+
+struct device *dev_primary(struct dev_mgr *dm, struct device *d)
+{
+       struct device *ret;
+
+       ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
+       /* FIXME: Needs replacing with a 'refresh' */
+       if (!ret) {
+               init_dev_scan(dm);
+               ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
+       }
+
+       return ret;
+
+}
+
+int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
+{
+       int pt;
+
+       pt = _get_partition_type(dm, d);
+
+       if (!pt) {
+               if (is_whole_disk(dm, d))
+                       /* FIXME: Overloaded pt=0 in error cases */
+                       return 1;
+               else {
+                       log_error
+                           ("%s: missing partition table "
+                            "on partitioned device", d->name);
+                       return 0;
+               }
+       }
+
+       if (is_whole_disk(dm, d)) {
+               log_error("%s: looks to possess partition table", d->name);
+               return 0;
+       }
+
+       /* check part type */
+       if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
+               log_error("%s: invalid partition type 0x%x "
+                         "(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
+               return 0;
+       }
+
+       if (pt == LVM_PARTITION) {
+               log_error
+                   ("%s: old LVM partition type found - please change to 0x%x",
+                    d->name, LVM_NEW_PARTITION);
+               return 0;
+       }
+
+       return 1;
+}
+
+int _get_partition_type(struct dev_mgr *dm, struct device *d)
+{
+       int pv_handle = -1;
+       struct device *primary;
+       ssize_t read_ret;
+       ssize_t bytes_read = 0;
+       char *buffer;
+       unsigned short *s_buffer;
+       struct partition *part;
+       loff_t offset = 0;
+       loff_t extended_offset = 0;
+       int part_sought;
+       int part_found = 0;
+       int first_partition = 1;
+       int extended_partition = 0;
+       int p;
+
+       if (!(primary = dev_primary(dm, d))) {
+               log_error
+                   ("Failed to find main device containing partition %s",
+                    d->name);
+               return 0;
+       }
+
+       if (!(buffer = dm_malloc(SECTOR_SIZE))) {
+               log_error("Failed to allocate partition table buffer");
+               return 0;
+       }
+
+       /* Get partition table */
+       if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
+               log_error("%s: open failed: %s", primary->name,
+                         strerror(errno));
+               return 0;
+       }
+
+       s_buffer = (unsigned short *) buffer;
+       part = (struct partition *) (buffer + 0x1be);
+       part_sought = MINOR_PART(dm, d);
+
+       do {
+               bytes_read = 0;
+
+               if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
+                       log_error("%s: llseek failed: %s",
+                                 primary->name, strerror(errno));
+                       return 0;
+               }
+
+               while ((bytes_read < SECTOR_SIZE) &&
+                      (read_ret =
+                       read(pv_handle, buffer + bytes_read,
+                            SECTOR_SIZE - bytes_read)) != -1)
+                       bytes_read += read_ret;
+
+               if (read_ret == -1) {
+                       log_error("%s: read failed: %s", primary->name,
+                                 strerror(errno));
+                       return 0;
+               }
+
+               if (s_buffer[255] == 0xAA55) {
+                       if (is_whole_disk(dm, d))
+                               return -1;
+               } else
+                       return 0;
+
+               extended_partition = 0;
+
+               /* Loop through primary partitions */
+               for (p = 0; p < 4; p++) {
+                       if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
+                           part[p].sys_ind == LINUX_EXTENDED_PARTITION
+                           || part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
+                               extended_partition = 1;
+                               offset = extended_offset + part[p].start_sect;
+                               if (extended_offset == 0)
+                                       extended_offset = part[p].start_sect;
+                               if (first_partition == 1)
+                                       part_found++;
+                       } else if (first_partition == 1) {
+                               if (p == part_sought) {
+                                       if (part[p].sys_ind == 0) {
+                                               /* missing primary? */
+                                               return 0;
+                                       }
+                               } else
+                                       part_found++;
+                       } else if (!part[p].sys_ind)
+                               part_found++;
+
+                       if (part_sought == part_found)
+                               return part[p].sys_ind;
+
+               }
+               first_partition = 0;
+       }
+       while (extended_partition == 1);
+
+       return 0;
+}
+#endif
+
+#ifdef linux
+
+int get_primary_dev(const char *sysfs_dir,
+                   const struct device *dev, dev_t *result)
+{
+       char path[PATH_MAX+1];
+       char temp_path[PATH_MAX+1];
+       char buffer[64];
+       struct stat info;
+       FILE *fp;
+       uint32_t pri_maj, pri_min;
+       int ret = 0;
+
+       /* check if dev is a partition */
+       if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/partition",
+                       sysfs_dir, (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
+               log_error("dm_snprintf partition failed");
+               return ret;
+       }
+
+       if (stat(path, &info) == -1) {
+               if (errno != ENOENT)
+                       log_sys_error("stat", path);
+               return ret;
+       }
+
+       /*
+        * extract parent's path from the partition's symlink, e.g.:
+        * - readlink /sys/dev/block/259:0 = ../../block/md0/md0p1
+        * - dirname ../../block/md0/md0p1 = ../../block/md0
+        * - basename ../../block/md0/md0  = md0
+        * Parent's 'dev' sysfs attribute  = /sys/block/md0/dev
+        */
+       if (readlink(dirname(path), temp_path, PATH_MAX) < 0) {
+               log_sys_error("readlink", path);
+               return ret;
+       }
+
+       if (dm_snprintf(path, PATH_MAX, "%s/block/%s/dev",
+                       sysfs_dir, basename(dirname(temp_path))) < 0) {
+               log_error("dm_snprintf dev failed");
+               return ret;
+       }
+
+       /* finally, parse 'dev' attribute and create corresponding dev_t */
+       if (stat(path, &info) == -1) {
+               if (errno == ENOENT)
+                       log_error("sysfs file %s does not exist", path);
+               else
+                       log_sys_error("stat", path);
+               return ret;
+       }
+
+       fp = fopen(path, "r");
+       if (!fp) {
+               log_sys_error("fopen", path);
+               return ret;
+       }
+
+       if (!fgets(buffer, sizeof(buffer), fp)) {
+               log_sys_error("fgets", path);
+               goto out;
+       }
+
+       if (sscanf(buffer, "%d:%d", &pri_maj, &pri_min) != 2) {
+               log_error("sysfs file %s not in expected MAJ:MIN format: %s",
+                         path, buffer);
+               goto out;
+       }
+       *result = MKDEV(pri_maj, pri_min);
+       ret = 1;
+
+out:
+       if (fclose(fp))
+               log_sys_error("fclose", path);
+
+       return ret;
+}
+
+static unsigned long _dev_topology_attribute(const char *attribute,
+                                            const char *sysfs_dir,
+                                            struct device *dev)
+{
+       const char *sysfs_fmt_str = "%s/dev/block/%d:%d/%s";
+       char path[PATH_MAX+1], buffer[64];
+       FILE *fp;
+       struct stat info;
+       dev_t uninitialized_var(primary);
+       unsigned long result = 0UL;
+
+       if (!attribute || !*attribute)
+               return_0;
+
+       if (!sysfs_dir || !*sysfs_dir)
+               return_0;
+
+       if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
+                       (int)MAJOR(dev->dev), (int)MINOR(dev->dev),
+                       attribute) < 0) {
+               log_error("dm_snprintf %s failed", attribute);
+               return 0;
+       }
+
+       /*
+        * check if the desired sysfs attribute exists
+        * - if not: either the kernel doesn't have topology support
+        *   or the device could be a partition
+        */
+       if (stat(path, &info) == -1) {
+               if (errno != ENOENT) {
+                       log_sys_error("stat", path);
+                       return 0;
+               }
+               if (!get_primary_dev(sysfs_dir, dev, &primary))
+                       return 0;
+
+               /* get attribute from partition's primary device */
+               if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
+                               (int)MAJOR(primary), (int)MINOR(primary),
+                               attribute) < 0) {
+                       log_error("primary dm_snprintf %s failed", attribute);
+                       return 0;
+               }
+               if (stat(path, &info) == -1) {
+                       if (errno != ENOENT)
+                               log_sys_error("stat", path);
+                       return 0;
+               }
+       }
+
+       if (!(fp = fopen(path, "r"))) {
+               log_sys_error("fopen", path);
+               return 0;
+       }
+
+       if (!fgets(buffer, sizeof(buffer), fp)) {
+               log_sys_error("fgets", path);
+               goto out;
+       }
+
+       if (sscanf(buffer, "%lu", &result) != 1) {
+               log_error("sysfs file %s not in expected format: %s", path,
+                         buffer);
+               goto out;
+       }
+
+       log_very_verbose("Device %s %s is %lu bytes.",
+                        dev_name(dev), attribute, result);
+
+out:
+       if (fclose(fp))
+               log_sys_error("fclose", path);
+
+       return result >> SECTOR_SHIFT;
+}
+
+unsigned long dev_alignment_offset(const char *sysfs_dir,
+                                  struct device *dev)
+{
+       return _dev_topology_attribute("alignment_offset",
+                                      sysfs_dir, dev);
+}
+
+unsigned long dev_minimum_io_size(const char *sysfs_dir,
+                                 struct device *dev)
+{
+       return _dev_topology_attribute("queue/minimum_io_size",
+                                      sysfs_dir, dev);
+}
+
+unsigned long dev_optimal_io_size(const char *sysfs_dir,
+                                 struct device *dev)
+{
+       return _dev_topology_attribute("queue/optimal_io_size",
+                                      sysfs_dir, dev);
+}
+
+#else
+
+int get_primary_dev(const char *sysfs_dir,
+                   struct device *dev, dev_t *result)
+{
+       return 0;
+}
+
+unsigned long dev_alignment_offset(const char *sysfs_dir,
+                                  struct device *dev)
+{
+       return 0UL;
+}
+
+unsigned long dev_minimum_io_size(const char *sysfs_dir,
+                                 struct device *dev)
+{
+       return 0UL;
+}
+
+unsigned long dev_optimal_io_size(const char *sysfs_dir,
+                                 struct device *dev)
+{
+       return 0UL;
+}
+
+#endif
diff --git a/lib/device/device.h b/lib/device/device.h
new file mode 100644 (file)
index 0000000..694f503
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_DEVICE_H
+#define _LVM_DEVICE_H
+
+#include "uuid.h"
+
+#include <fcntl.h>
+
+#define DEV_ACCESSED_W         0x00000001      /* Device written to? */
+#define DEV_REGULAR            0x00000002      /* Regular file? */
+#define DEV_ALLOCED            0x00000004      /* dm_malloc used */
+#define DEV_OPENED_RW          0x00000008      /* Opened RW */
+#define DEV_OPENED_EXCL                0x00000010      /* Opened EXCL */
+#define DEV_O_DIRECT           0x00000020      /* Use O_DIRECT */
+#define DEV_O_DIRECT_TESTED    0x00000040      /* DEV_O_DIRECT is reliable */
+
+/*
+ * All devices in LVM will be represented by one of these.
+ * pointer comparisons are valid.
+ */
+struct device {
+       struct dm_list aliases; /* struct str_list from lvm-types.h */
+       dev_t dev;
+
+       /* private */
+       int fd;
+       int open_count;
+       int error_count;
+       int max_error_count;
+       int block_size;
+       int read_ahead;
+       uint32_t flags;
+       uint64_t end;
+       struct dm_list open_list;
+
+       char pvid[ID_LEN + 1];
+       char _padding[7];
+};
+
+struct device_list {
+       struct dm_list list;
+       struct device *dev;
+};
+
+struct device_area {
+       struct device *dev;
+       uint64_t start;         /* Bytes */
+       uint64_t size;          /* Bytes */
+};
+
+/*
+ * All io should use these routines.
+ */
+int dev_get_size(const struct device *dev, uint64_t *size);
+int dev_get_sectsize(struct device *dev, uint32_t *size);
+int dev_get_read_ahead(struct device *dev, uint32_t *read_ahead);
+
+/* Use quiet version if device number could change e.g. when opening LV */
+int dev_open(struct device *dev);
+int dev_open_quiet(struct device *dev);
+int dev_open_flags(struct device *dev, int flags, int direct, int quiet);
+int dev_close(struct device *dev);
+int dev_close_immediate(struct device *dev);
+void dev_close_all(void);
+int dev_test_excl(struct device *dev);
+
+int dev_fd(struct device *dev);
+const char *dev_name(const struct device *dev);
+
+int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer);
+int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
+                     uint64_t offset2, size_t len2, void *buf);
+int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer);
+int dev_append(struct device *dev, size_t len, void *buffer);
+int dev_set(struct device *dev, uint64_t offset, size_t len, int value);
+void dev_flush(struct device *dev);
+
+struct device *dev_create_file(const char *filename, struct device *dev,
+                              struct str_list *alias, int use_malloc);
+
+/* Return a valid device name from the alias list; NULL otherwise */
+const char *dev_name_confirmed(struct device *dev, int quiet);
+
+/* Does device contain md superblock?  If so, where? */
+int dev_is_md(struct device *dev, uint64_t *sb);
+int dev_is_swap(struct device *dev, uint64_t *signature);
+int dev_is_luks(struct device *dev, uint64_t *signature);
+unsigned long dev_md_stripe_width(const char *sysfs_dir, struct device *dev);
+
+int is_partitioned_dev(struct device *dev);
+
+int get_primary_dev(const char *sysfs_dir,
+                   const struct device *dev, dev_t *result);
+
+unsigned long dev_alignment_offset(const char *sysfs_dir,
+                                  struct device *dev);
+
+unsigned long dev_minimum_io_size(const char *sysfs_dir,
+                                 struct device *dev);
+
+unsigned long dev_optimal_io_size(const char *sysfs_dir,
+                                 struct device *dev);
+
+#endif
diff --git a/lib/display/display.c b/lib/display/display.c
new file mode 100644 (file)
index 0000000..aae660a
--- /dev/null
@@ -0,0 +1,851 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "display.h"
+#include "activate.h"
+#include "toolcontext.h"
+#include "segtype.h"
+
+#define SIZE_BUF 128
+
+typedef enum { SIZE_LONG = 0, SIZE_SHORT = 1, SIZE_UNIT = 2 } size_len_t;
+
+static const struct {
+       alloc_policy_t alloc;
+       const char str[14]; /* must be changed when size extends 13 chars */
+       const char repchar;
+} _policies[] = {
+       {
+       ALLOC_CONTIGUOUS, "contiguous", 'c'}, {
+       ALLOC_CLING, "cling", 'l'}, {
+       ALLOC_CLING_BY_TAGS, "cling_by_tags", 't'}, {   /* Only used in log mesgs */
+       ALLOC_NORMAL, "normal", 'n'}, {
+       ALLOC_ANYWHERE, "anywhere", 'a'}, {
+       ALLOC_INHERIT, "inherit", 'i'}
+};
+
+static const int _num_policies = sizeof(_policies) / sizeof(*_policies);
+
+uint64_t units_to_bytes(const char *units, char *unit_type)
+{
+       char *ptr = NULL;
+       uint64_t v;
+
+       if (isdigit(*units)) {
+               v = (uint64_t) strtod(units, &ptr);
+               if (ptr == units)
+                       return 0;
+               units = ptr;
+       } else
+               v = 1;
+
+       if (v == 1)
+               *unit_type = *units;
+       else
+               *unit_type = 'U';
+
+       switch (*units) {
+       case 'h':
+       case 'H':
+               v = UINT64_C(1);
+               *unit_type = *units;
+               break;
+       case 'b':
+       case 'B':
+               v *= UINT64_C(1);
+               break;
+#define KILO UINT64_C(1024)
+       case 's':
+       case 'S':
+               v *= (KILO/2);
+               break;
+       case 'k':
+               v *= KILO;
+               break;
+       case 'm':
+               v *= KILO * KILO;
+               break;
+       case 'g':
+               v *= KILO * KILO * KILO;
+               break;
+       case 't':
+               v *= KILO * KILO * KILO * KILO;
+               break;
+       case 'p':
+               v *= KILO * KILO * KILO * KILO * KILO;
+               break;
+       case 'e':
+               v *= KILO * KILO * KILO * KILO * KILO * KILO;
+               break;
+#undef KILO
+#define KILO UINT64_C(1000)
+       case 'K':
+               v *= KILO;
+               break;
+       case 'M':
+               v *= KILO * KILO;
+               break;
+       case 'G':
+               v *= KILO * KILO * KILO;
+               break;
+       case 'T':
+               v *= KILO * KILO * KILO * KILO;
+               break;
+       case 'P':
+               v *= KILO * KILO * KILO * KILO * KILO;
+               break;
+       case 'E':
+               v *= KILO * KILO * KILO * KILO * KILO * KILO;
+               break;
+#undef KILO
+       default:
+               return 0;
+       }
+
+       if (*(units + 1))
+               return 0;
+
+       return v;
+}
+
+const char alloc_policy_char(alloc_policy_t alloc)
+{
+       int i;
+
+       for (i = 0; i < _num_policies; i++)
+               if (_policies[i].alloc == alloc)
+                       return _policies[i].repchar;
+
+       return '-';
+}
+
+const char *get_alloc_string(alloc_policy_t alloc)
+{
+       int i;
+
+       for (i = 0; i < _num_policies; i++)
+               if (_policies[i].alloc == alloc)
+                       return _policies[i].str;
+
+       return NULL;
+}
+
+alloc_policy_t get_alloc_from_string(const char *str)
+{
+       int i;
+
+       /* cling_by_tags is part of cling */
+       if (!strcmp("cling_by_tags", str))
+               return ALLOC_CLING;
+
+       for (i = 0; i < _num_policies; i++)
+               if (!strcmp(_policies[i].str, str))
+                       return _policies[i].alloc;
+
+       /* Special case for old metadata */
+       if (!strcmp("next free", str))
+               return ALLOC_NORMAL;
+
+       log_error("Unrecognised allocation policy %s", str);
+       return ALLOC_INVALID;
+}
+
+#define BASE_UNKNOWN 0
+#define BASE_SHARED 1
+#define BASE_1024 7
+#define BASE_1000 13
+#define BASE_SPECIAL 19
+#define NUM_UNIT_PREFIXES 6
+#define NUM_SPECIAL 3
+
+/* Size supplied in sectors */
+static const char *_display_size(const struct cmd_context *cmd,
+                                uint64_t size, size_len_t sl)
+{
+       unsigned base = BASE_UNKNOWN;
+       unsigned s;
+       int suffix = 1, precision;
+       uint64_t byte = UINT64_C(0);
+       uint64_t units = UINT64_C(1024);
+       char *size_buf = NULL;
+       const char * const size_str[][3] = {
+               /* BASE_UNKNOWN */
+               {"         ", "   ", " "},      /* [0] */
+
+               /* BASE_SHARED - Used if cmd->si_unit_consistency = 0 */
+               {" Exabyte", " EB", "E"},       /* [1] */
+               {" Petabyte", " PB", "P"},      /* [2] */
+               {" Terabyte", " TB", "T"},      /* [3] */
+               {" Gigabyte", " GB", "G"},      /* [4] */
+               {" Megabyte", " MB", "M"},      /* [5] */
+               {" Kilobyte", " KB", "K"},      /* [6] */
+
+               /* BASE_1024 - Used if cmd->si_unit_consistency = 1 */
+               {" Exbibyte", " EiB", "e"},     /* [7] */
+               {" Pebibyte", " PiB", "p"},     /* [8] */
+               {" Tebibyte", " TiB", "t"},     /* [9] */
+               {" Gibibyte", " GiB", "g"},     /* [10] */
+               {" Mebibyte", " MiB", "m"},     /* [11] */
+               {" Kibibyte", " KiB", "k"},     /* [12] */
+
+               /* BASE_1000 - Used if cmd->si_unit_consistency = 1 */
+               {" Exabyte",  " EB", "E"},      /* [13] */
+               {" Petabyte", " PB", "P"},      /* [14] */
+               {" Terabyte", " TB", "T"},      /* [15] */
+               {" Gigabyte", " GB", "G"},      /* [16] */
+               {" Megabyte", " MB", "M"},      /* [17] */
+               {" Kilobyte", " kB", "K"},      /* [18] */
+
+               /* BASE_SPECIAL */
+               {" Byte    ", " B ", "B"},      /* [19] */
+               {" Units   ", " Un", "U"},      /* [20] */
+               {" Sectors ", " Se", "S"},      /* [21] */
+       };
+
+       if (!(size_buf = dm_pool_alloc(cmd->mem, SIZE_BUF))) {
+               log_error("no memory for size display buffer");
+               return "";
+       }
+
+       suffix = cmd->current_settings.suffix;
+
+       if (!cmd->si_unit_consistency) {
+               /* Case-independent match */
+               for (s = 0; s < NUM_UNIT_PREFIXES; s++)
+                       if (toupper((int) cmd->current_settings.unit_type) ==
+                           *size_str[BASE_SHARED + s][2]) {
+                               base = BASE_SHARED;
+                               break;
+                       }
+       } else {
+               /* Case-dependent match for powers of 1000 */
+               for (s = 0; s < NUM_UNIT_PREFIXES; s++)
+                       if (cmd->current_settings.unit_type ==
+                           *size_str[BASE_1000 + s][2]) {
+                               base = BASE_1000;
+                               break;
+                       }
+
+               /* Case-dependent match for powers of 1024 */
+               if (base == BASE_UNKNOWN)
+                       for (s = 0; s < NUM_UNIT_PREFIXES; s++)
+                       if (cmd->current_settings.unit_type ==
+                           *size_str[BASE_1024 + s][2]) {
+                               base = BASE_1024;
+                               break;
+                       }
+       }
+
+       if (base == BASE_UNKNOWN)
+               /* Check for special units - s, b or u */
+               for (s = 0; s < NUM_SPECIAL; s++)
+                       if (toupper((int) cmd->current_settings.unit_type) ==
+                           *size_str[BASE_SPECIAL + s][2]) {
+                               base = BASE_SPECIAL;
+                               break;
+                       }
+
+       if (size == UINT64_C(0)) {
+               if (base == BASE_UNKNOWN)
+                       s = 0;
+               sprintf(size_buf, "0%s", suffix ? size_str[base + s][sl] : "");
+               return size_buf;
+       }
+
+       size *= UINT64_C(512);
+
+       if (base != BASE_UNKNOWN)
+               byte = cmd->current_settings.unit_factor;
+       else {
+               /* Human-readable style */
+               if (cmd->current_settings.unit_type == 'H') {
+                       units = UINT64_C(1000);
+                       base = BASE_1000;
+               } else {
+                       units = UINT64_C(1024);
+                       base = BASE_1024;
+               }
+
+               if (!cmd->si_unit_consistency)
+                       base = BASE_SHARED;
+
+               byte = units * units * units * units * units * units;
+
+               for (s = 0; s < NUM_UNIT_PREFIXES && size < byte; s++)
+                       byte /= units;
+
+               suffix = 1;
+       }
+
+       /* FIXME Make precision configurable */
+       switch(toupper((int) cmd->current_settings.unit_type)) {
+       case 'B':
+       case 'S':
+               precision = 0;
+               break;
+       default:
+               precision = 2;
+       }
+
+       snprintf(size_buf, SIZE_BUF - 1, "%.*f%s", precision,
+                (double) size / byte, suffix ? size_str[base + s][sl] : "");
+
+       return size_buf;
+}
+
+const char *display_size_long(const struct cmd_context *cmd, uint64_t size)
+{
+       return _display_size(cmd, size, SIZE_LONG);
+}
+
+const char *display_size_units(const struct cmd_context *cmd, uint64_t size)
+{
+       return _display_size(cmd, size, SIZE_UNIT);
+}
+
+const char *display_size(const struct cmd_context *cmd, uint64_t size)
+{
+       return _display_size(cmd, size, SIZE_SHORT);
+}
+
+void pvdisplay_colons(const struct physical_volume *pv)
+{
+       char uuid[64] __attribute__((aligned(8)));
+
+       if (!pv)
+               return;
+
+       if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+               stack;
+               return;
+       }
+
+       log_print("%s:%s:%" PRIu64 ":-1:%" PRIu64 ":%" PRIu64 ":-1:%" PRIu32 ":%u:%u:%u:%s",
+                 pv_dev_name(pv), pv->vg_name, pv->size,
+                 /* FIXME pv->pv_number, Derive or remove? */
+                 pv->status,   /* FIXME Support old or new format here? */
+                 pv->status & ALLOCATABLE_PV,  /* FIXME remove? */
+                 /* FIXME pv->lv_cur, Remove? */
+                 pv->pe_size / 2,
+                 pv->pe_count,
+                 pv->pe_count - pv->pe_alloc_count,
+                 pv->pe_alloc_count, *uuid ? uuid : "none");
+}
+
+void pvdisplay_segments(const struct physical_volume *pv)
+{
+       const struct pv_segment *pvseg;
+
+       if (pv->pe_size)
+               log_print("--- Physical Segments ---");
+
+       dm_list_iterate_items(pvseg, &pv->segments) {
+               log_print("Physical extent %u to %u:",
+                         pvseg->pe, pvseg->pe + pvseg->len - 1);
+
+               if (pvseg_is_allocated(pvseg)) {
+                       log_print("  Logical volume\t%s%s/%s",
+                                 pvseg->lvseg->lv->vg->cmd->dev_dir,
+                                 pvseg->lvseg->lv->vg->name,
+                                 pvseg->lvseg->lv->name);
+                       log_print("  Logical extents\t%d to %d",
+                                 pvseg->lvseg->le, pvseg->lvseg->le +
+                                 pvseg->lvseg->len - 1);
+               } else
+                       log_print("  FREE");
+       }
+
+       log_print(" ");
+}
+
+/* FIXME Include label fields */
+void pvdisplay_full(const struct cmd_context *cmd,
+                   const struct physical_volume *pv,
+                   void *handle __attribute__((unused)))
+{
+       char uuid[64] __attribute__((aligned(8)));
+       const char *size;
+
+       uint32_t pe_free;
+       uint64_t data_size, pvsize, unusable;
+
+       if (!pv)
+               return;
+
+       if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+               stack;
+               return;
+       }
+
+       log_print("--- %sPhysical volume ---", pv->pe_size ? "" : "NEW ");
+       log_print("PV Name               %s", pv_dev_name(pv));
+       log_print("VG Name               %s%s",
+                 is_orphan(pv) ? "" : pv->vg_name,
+                 pv->status & EXPORTED_VG ? " (exported)" : "");
+
+       data_size = (uint64_t) pv->pe_count * pv->pe_size;
+       if (pv->size > data_size + pv->pe_start) {
+               pvsize = pv->size;
+               unusable = pvsize - data_size;
+       } else {
+               pvsize = data_size + pv->pe_start;
+               unusable = pvsize - pv->size;
+       }
+
+       size = display_size(cmd, pvsize);
+       if (data_size)
+               log_print("PV Size               %s / not usable %s",   /*  [LVM: %s]", */
+                         size, display_size(cmd, unusable));
+       else
+               log_print("PV Size               %s", size);
+
+       /* PV number not part of LVM2 design
+          log_print("PV#                   %u", pv->pv_number);
+        */
+
+       pe_free = pv->pe_count - pv->pe_alloc_count;
+       if (pv->pe_count && (pv->status & ALLOCATABLE_PV))
+               log_print("Allocatable           yes %s",
+                         (!pe_free && pv->pe_count) ? "(but full)" : "");
+       else
+               log_print("Allocatable           NO");
+
+       /* LV count is no longer available when displaying PV
+          log_print("Cur LV                %u", vg->lv_count);
+        */
+
+       if (cmd->si_unit_consistency)
+               log_print("PE Size               %s", display_size(cmd, (uint64_t) pv->pe_size));
+       else
+               log_print("PE Size (KByte)       %" PRIu32, pv->pe_size / 2);
+
+       log_print("Total PE              %u", pv->pe_count);
+       log_print("Free PE               %" PRIu32, pe_free);
+       log_print("Allocated PE          %u", pv->pe_alloc_count);
+       log_print("PV UUID               %s", *uuid ? uuid : "none");
+       log_print(" ");
+}
+
+int pvdisplay_short(const struct cmd_context *cmd __attribute__((unused)),
+                   const struct volume_group *vg __attribute__((unused)),
+                   const struct physical_volume *pv,
+                   void *handle __attribute__((unused)))
+{
+       char uuid[64] __attribute__((aligned(8)));
+
+       if (!pv)
+               return 0;
+
+       if (!id_write_format(&pv->id, uuid, sizeof(uuid)))
+               return_0;
+
+       log_print("PV Name               %s     ", pv_dev_name(pv));
+       /* FIXME  pv->pv_number); */
+       log_print("PV UUID               %s", *uuid ? uuid : "none");
+       log_print("PV Status             %sallocatable",
+                 (pv->status & ALLOCATABLE_PV) ? "" : "NOT ");
+       log_print("Total PE / Free PE    %u / %u",
+                 pv->pe_count, pv->pe_count - pv->pe_alloc_count);
+
+       log_print(" ");
+       return 0;
+}
+
+void lvdisplay_colons(const struct logical_volume *lv)
+{
+       int inkernel;
+       struct lvinfo info;
+       inkernel = lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) && info.exists;
+
+       log_print("%s%s/%s:%s:%" PRIu64 ":%d:-1:%d:%" PRIu64 ":%d:-1:%d:%d:%d:%d",
+                 lv->vg->cmd->dev_dir,
+                 lv->vg->name,
+                 lv->name,
+                 lv->vg->name,
+                 (lv->status & (LVM_READ | LVM_WRITE)) >> 8, inkernel ? 1 : 0,
+                 /* FIXME lv->lv_number,  */
+                 inkernel ? info.open_count : 0, lv->size, lv->le_count,
+                 /* FIXME Add num allocated to struct! lv->lv_allocated_le, */
+                 (lv->alloc == ALLOC_CONTIGUOUS ? 2 : 0), lv->read_ahead,
+                 inkernel ? info.major : -1, inkernel ? info.minor : -1);
+}
+
+int lvdisplay_full(struct cmd_context *cmd,
+                  const struct logical_volume *lv,
+                  void *handle __attribute__((unused)))
+{
+       struct lvinfo info;
+       int inkernel, snap_active = 0;
+       char uuid[64] __attribute__((aligned(8)));
+       struct lv_segment *snap_seg = NULL, *mirror_seg = NULL;
+       percent_t snap_percent;
+
+       if (!id_write_format(&lv->lvid.id[1], uuid, sizeof(uuid)))
+               return_0;
+
+       inkernel = lv_info(cmd, lv, 0, &info, 1, 1) && info.exists;
+
+       log_print("--- Logical volume ---");
+
+       log_print("LV Name                %s%s/%s", lv->vg->cmd->dev_dir,
+                 lv->vg->name, lv->name);
+       log_print("VG Name                %s", lv->vg->name);
+
+       log_print("LV UUID                %s", uuid);
+
+       log_print("LV Write Access        %s",
+                 (lv->status & LVM_WRITE) ? "read/write" : "read only");
+
+       if (lv_is_origin(lv)) {
+               log_print("LV snapshot status     source of");
+
+               dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs,
+                                      origin_list) {
+                       if (inkernel &&
+                           (snap_active = lv_snapshot_percent(snap_seg->cow,
+                                                              &snap_percent)))
+                               if (snap_percent == PERCENT_INVALID)
+                                       snap_active = 0;
+                       log_print("                       %s%s/%s [%s]",
+                                 lv->vg->cmd->dev_dir, lv->vg->name,
+                                 snap_seg->cow->name,
+                                 snap_active ? "active" : "INACTIVE");
+               }
+               snap_seg = NULL;
+       } else if ((snap_seg = find_cow(lv))) {
+               if (inkernel &&
+                   (snap_active = lv_snapshot_percent(snap_seg->cow,
+                                                      &snap_percent)))
+                       if (snap_percent == PERCENT_INVALID)
+                               snap_active = 0;
+
+               log_print("LV snapshot status     %s destination for %s%s/%s",
+                         snap_active ? "active" : "INACTIVE",
+                         lv->vg->cmd->dev_dir, lv->vg->name,
+                         snap_seg->origin->name);
+       }
+
+       if (inkernel && info.suspended)
+               log_print("LV Status              suspended");
+       else
+               log_print("LV Status              %savailable",
+                         inkernel ? "" : "NOT ");
+
+/********* FIXME lv_number
+    log_print("LV #                   %u", lv->lv_number + 1);
+************/
+
+       if (inkernel)
+               log_print("# open                 %u", info.open_count);
+
+       log_print("LV Size                %s",
+                 display_size(cmd,
+                              snap_seg ? snap_seg->origin->size : lv->size));
+
+       log_print("Current LE             %u",
+                 snap_seg ? snap_seg->origin->le_count : lv->le_count);
+
+       if (snap_seg) {
+               log_print("COW-table size         %s",
+                         display_size(cmd, (uint64_t) lv->size));
+               log_print("COW-table LE           %u", lv->le_count);
+
+               if (snap_active)
+                       log_print("Allocated to snapshot  %.2f%% ",
+                                 percent_to_float(snap_percent));
+
+               log_print("Snapshot chunk size    %s",
+                         display_size(cmd, (uint64_t) snap_seg->chunk_size));
+       }
+
+       if (lv->status & MIRRORED) {
+               mirror_seg = first_seg(lv);
+               log_print("Mirrored volumes       %" PRIu32, mirror_seg->area_count);
+               if (lv->status & CONVERTING)
+                       log_print("LV type        Mirror undergoing conversion");
+       }
+
+       log_print("Segments               %u", dm_list_size(&lv->segments));
+
+/********* FIXME Stripes & stripesize for each segment
+       log_print("Stripe size            %s", display_size(cmd, (uint64_t) lv->stripesize));
+***********/
+
+       log_print("Allocation             %s", get_alloc_string(lv->alloc));
+       if (lv->read_ahead == DM_READ_AHEAD_AUTO)
+               log_print("Read ahead sectors     auto");
+       else if (lv->read_ahead == DM_READ_AHEAD_NONE)
+               log_print("Read ahead sectors     0");
+       else
+               log_print("Read ahead sectors     %u", lv->read_ahead);
+
+       if (inkernel && lv->read_ahead != info.read_ahead)
+               log_print("- currently set to     %u", info.read_ahead);
+
+       if (lv->status & FIXED_MINOR) {
+               if (lv->major >= 0)
+                       log_print("Persistent major       %d", lv->major);
+               log_print("Persistent minor       %d", lv->minor);
+       }
+
+       if (inkernel)
+               log_print("Block device           %d:%d", info.major,
+                         info.minor);
+
+       log_print(" ");
+
+       return 0;
+}
+
+void display_stripe(const struct lv_segment *seg, uint32_t s, const char *pre)
+{
+       switch (seg_type(seg, s)) {
+       case AREA_PV:
+               /* FIXME Re-check the conditions for 'Missing' */
+               log_print("%sPhysical volume\t%s", pre,
+                         seg_pv(seg, s) ?
+                         pv_dev_name(seg_pv(seg, s)) :
+                           "Missing");
+
+               if (seg_pv(seg, s))
+                       log_print("%sPhysical extents\t%d to %d", pre,
+                                 seg_pe(seg, s),
+                                 seg_pe(seg, s) + seg->area_len - 1);
+               break;
+       case AREA_LV:
+               log_print("%sLogical volume\t%s", pre,
+                         seg_lv(seg, s) ?
+                         seg_lv(seg, s)->name : "Missing");
+
+               if (seg_lv(seg, s))
+                       log_print("%sLogical extents\t%d to %d", pre,
+                                 seg_le(seg, s),
+                                 seg_le(seg, s) + seg->area_len - 1);
+               break;
+       case AREA_UNASSIGNED:
+               log_print("%sUnassigned area", pre);
+       }
+}
+
+int lvdisplay_segments(const struct logical_volume *lv)
+{
+       const struct lv_segment *seg;
+
+       log_print("--- Segments ---");
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               log_print("Logical extent %u to %u:",
+                         seg->le, seg->le + seg->len - 1);
+
+               log_print("  Type\t\t%s", seg->segtype->ops->name(seg));
+
+               if (seg->segtype->ops->display)
+                       seg->segtype->ops->display(seg);
+       }
+
+       log_print(" ");
+       return 1;
+}
+
+void vgdisplay_extents(const struct volume_group *vg __attribute__((unused)))
+{
+}
+
+void vgdisplay_full(const struct volume_group *vg)
+{
+       uint32_t access_str;
+       uint32_t active_pvs;
+       char uuid[64] __attribute__((aligned(8)));
+
+       active_pvs = vg->pv_count - vg_missing_pv_count(vg);
+
+       log_print("--- Volume group ---");
+       log_print("VG Name               %s", vg->name);
+       log_print("System ID             %s", vg->system_id);
+       log_print("Format                %s", vg->fid->fmt->name);
+       if (vg->fid->fmt->features & FMT_MDAS) {
+               log_print("Metadata Areas        %d",
+                         vg_mda_count(vg));
+               log_print("Metadata Sequence No  %d", vg->seqno);
+       }
+       access_str = vg->status & (LVM_READ | LVM_WRITE);
+       log_print("VG Access             %s%s%s%s",
+                 access_str == (LVM_READ | LVM_WRITE) ? "read/write" : "",
+                 access_str == LVM_READ ? "read" : "",
+                 access_str == LVM_WRITE ? "write" : "",
+                 access_str == 0 ? "error" : "");
+       log_print("VG Status             %s%sresizable",
+                 vg_is_exported(vg) ? "exported/" : "",
+                 vg_is_resizeable(vg) ? "" : "NOT ");
+       /* vg number not part of LVM2 design
+          log_print ("VG #                  %u\n", vg->vg_number);
+        */
+       if (vg_is_clustered(vg)) {
+               log_print("Clustered             yes");
+               log_print("Shared                %s",
+                         vg->status & SHARED ? "yes" : "no");
+       }
+
+       log_print("MAX LV                %u", vg->max_lv);
+       log_print("Cur LV                %u", vg_visible_lvs(vg));
+       log_print("Open LV               %u", lvs_in_vg_opened(vg));
+/****** FIXME Max LV Size
+      log_print ( "MAX LV Size           %s",
+               ( s1 = display_size ( LVM_LV_SIZE_MAX(vg))));
+      free ( s1);
+*********/
+       log_print("Max PV                %u", vg->max_pv);
+       log_print("Cur PV                %u", vg->pv_count);
+       log_print("Act PV                %u", active_pvs);
+
+       log_print("VG Size               %s",
+                 display_size(vg->cmd,
+                              (uint64_t) vg->extent_count * vg->extent_size));
+
+       log_print("PE Size               %s",
+                 display_size(vg->cmd, (uint64_t) vg->extent_size));
+
+       log_print("Total PE              %u", vg->extent_count);
+
+       log_print("Alloc PE / Size       %u / %s",
+                 vg->extent_count - vg->free_count,
+                 display_size(vg->cmd,
+                              ((uint64_t) vg->extent_count - vg->free_count) *
+                              vg->extent_size));
+
+       log_print("Free  PE / Size       %u / %s", vg->free_count,
+                 display_size(vg->cmd, vg_free(vg)));
+
+       if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
+               stack;
+               return;
+       }
+
+       log_print("VG UUID               %s", uuid);
+       log_print(" ");
+}
+
+void vgdisplay_colons(const struct volume_group *vg)
+{
+       uint32_t active_pvs;
+       const char *access_str;
+       char uuid[64] __attribute__((aligned(8)));
+
+       active_pvs = vg->pv_count - vg_missing_pv_count(vg);
+
+       switch (vg->status & (LVM_READ | LVM_WRITE)) {
+               case LVM_READ | LVM_WRITE:
+                       access_str = "r/w";
+                       break;
+               case LVM_READ:
+                       access_str = "r";
+                       break;
+               case LVM_WRITE:
+                       access_str = "w";
+                       break;
+               default:
+                       access_str = "";
+       }
+
+       if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
+               stack;
+               return;
+       }
+
+       log_print("%s:%s:%" PRIu64 ":-1:%u:%u:%u:-1:%u:%u:%u:%" PRIu64 ":%" PRIu32
+                 ":%u:%u:%u:%s",
+               vg->name,
+               access_str,
+               vg->status,
+               /* internal volume group number; obsolete */
+               vg->max_lv,
+               vg_visible_lvs(vg),
+               lvs_in_vg_opened(vg),
+               /* FIXME: maximum logical volume size */
+               vg->max_pv,
+               vg->pv_count,
+               active_pvs,
+               (uint64_t) vg->extent_count * (vg->extent_size / 2),
+               vg->extent_size / 2,
+               vg->extent_count,
+               vg->extent_count - vg->free_count,
+               vg->free_count,
+               uuid[0] ? uuid : "none");
+}
+
+void vgdisplay_short(const struct volume_group *vg)
+{
+       log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name,
+/********* FIXME if "open" print "/used" else print "/idle"???  ******/
+                 display_size(vg->cmd,
+                              (uint64_t) vg->extent_count * vg->extent_size),
+                 display_size(vg->cmd,
+                              ((uint64_t) vg->extent_count -
+                               vg->free_count) * vg->extent_size),
+                 display_size(vg->cmd, vg_free(vg)));
+}
+
+void display_formats(const struct cmd_context *cmd)
+{
+       const struct format_type *fmt;
+
+       dm_list_iterate_items(fmt, &cmd->formats) {
+               log_print("%s", fmt->name);
+       }
+}
+
+void display_segtypes(const struct cmd_context *cmd)
+{
+       const struct segment_type *segtype;
+
+       dm_list_iterate_items(segtype, &cmd->segtypes) {
+               log_print("%s", segtype->name);
+       }
+}
+
+char yes_no_prompt(const char *prompt, ...)
+{
+       int c = 0, ret = 0;
+       va_list ap;
+
+       sigint_allow();
+       do {
+               if (c == '\n' || !c) {
+                       va_start(ap, prompt);
+                       vprintf(prompt, ap);
+                       va_end(ap);
+                       fflush(stdout);
+               }
+
+               if ((c = getchar()) == EOF) {
+                       ret = 'n';
+                       break;
+               }
+
+               c = tolower(c);
+               if ((c == 'y') || (c == 'n'))
+                       ret = c;
+       } while (!ret || c != '\n');
+
+       sigint_restore();
+
+       if (c != '\n')
+               printf("\n");
+
+       return ret;
+}
+
diff --git a/lib/display/display.h b/lib/display/display.h
new file mode 100644 (file)
index 0000000..fb000a2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_DISPLAY_H
+#define _LVM_DISPLAY_H
+
+#include "metadata-exported.h"
+#include "locking.h"
+
+#include <stdint.h>
+
+uint64_t units_to_bytes(const char *units, char *unit_type);
+
+/* Specify size in KB */
+const char *display_size(const struct cmd_context *cmd, uint64_t size);
+const char *display_size_long(const struct cmd_context *cmd, uint64_t size);
+const char *display_size_units(const struct cmd_context *cmd, uint64_t size);
+
+char *display_uuid(char *uuidstr);
+void display_stripe(const struct lv_segment *seg, uint32_t s, const char *pre);
+
+void pvdisplay_colons(const struct physical_volume *pv);
+void pvdisplay_segments(const struct physical_volume *pv);
+void pvdisplay_full(const struct cmd_context *cmd,
+                   const struct physical_volume *pv,
+                   void *handle);
+int pvdisplay_short(const struct cmd_context *cmd,
+                   const struct volume_group *vg,
+                   const struct physical_volume *pv, void *handle);
+
+void lvdisplay_colons(const struct logical_volume *lv);
+int lvdisplay_segments(const struct logical_volume *lv);
+int lvdisplay_full(struct cmd_context *cmd, const struct logical_volume *lv,
+                  void *handle);
+
+void vgdisplay_extents(const struct volume_group *vg);
+void vgdisplay_full(const struct volume_group *vg);
+void vgdisplay_colons(const struct volume_group *vg);
+void vgdisplay_short(const struct volume_group *vg);
+
+void display_formats(const struct cmd_context *cmd);
+void display_segtypes(const struct cmd_context *cmd);
+
+/*
+ * Allocation policy display conversion routines.
+ */
+const char *get_alloc_string(alloc_policy_t alloc);
+const char alloc_policy_char(alloc_policy_t alloc);
+alloc_policy_t get_alloc_from_string(const char *str);
+
+char yes_no_prompt(const char *prompt, ...) __attribute__ ((format(printf, 1, 2)));
+
+#endif
diff --git a/lib/error/errseg.c b/lib/error/errseg.c
new file mode 100644 (file)
index 0000000..add1df8
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "str_list.h"
+#include "metadata.h"
+
+static const char *_errseg_name(const struct lv_segment *seg)
+{
+       return seg->segtype->name;
+}
+
+static int _errseg_merge_segments(struct lv_segment *seg1, struct lv_segment *seg2)
+{
+       seg1->len += seg2->len;
+       seg1->area_len += seg2->area_len;
+
+       return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _errseg_add_target_line(struct dev_manager *dm __attribute__((unused)),
+                               struct dm_pool *mem __attribute__((unused)),
+                               struct cmd_context *cmd __attribute__((unused)),
+                               void **target_state __attribute__((unused)),
+                               struct lv_segment *seg __attribute__((unused)),
+                               struct dm_tree_node *node, uint64_t len,
+                               uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+       return dm_tree_node_add_error_target(node, len);
+}
+
+static int _errseg_target_present(struct cmd_context *cmd,
+                                 const struct lv_segment *seg __attribute__((unused)),
+                                 unsigned *attributes __attribute__((unused)))
+{
+       static int _errseg_checked = 0;
+       static int _errseg_present = 0;
+
+       /* Reported truncated in older kernels */
+       if (!_errseg_checked &&
+           (target_present(cmd, "error", 0) ||
+            target_present(cmd, "erro", 0)))
+               _errseg_present = 1;
+
+       _errseg_checked = 1;
+       return _errseg_present;
+}
+#endif
+
+static int _errseg_modules_needed(struct dm_pool *mem,
+                                 const struct lv_segment *seg __attribute__((unused)),
+                                 struct dm_list *modules)
+{
+       if (!str_list_add(mem, modules, "error")) {
+               log_error("error module string list allocation failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _errseg_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _error_ops = {
+       .name = _errseg_name,
+       .merge_segments = _errseg_merge_segments,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _errseg_add_target_line,
+       .target_present = _errseg_target_present,
+#endif
+       .modules_needed = _errseg_modules_needed,
+       .destroy = _errseg_destroy,
+};
+
+struct segment_type *init_error_segtype(struct cmd_context *cmd)
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_error_ops;
+       segtype->name = "error";
+       segtype->private = NULL;
+       segtype->flags = SEG_CAN_SPLIT | SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/lib/filters/filter-composite.c b/lib/filters/filter-composite.c
new file mode 100644 (file)
index 0000000..130490a
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "filter-composite.h"
+
+#include <stdarg.h>
+
+static int _and_p(struct dev_filter *f, struct device *dev)
+{
+       struct dev_filter **filters = (struct dev_filter **) f->private;
+
+       while (*filters) {
+               if (!(*filters)->passes_filter(*filters, dev))
+                       return 0;
+               filters++;
+       }
+
+       log_debug("Using %s", dev_name(dev));
+
+       return 1;
+}
+
+static void _composite_destroy(struct dev_filter *f)
+{
+       struct dev_filter **filters = (struct dev_filter **) f->private;
+
+       if (f->use_count)
+               log_error(INTERNAL_ERROR "Destroying composite filter while in use %u times.", f->use_count);
+
+       while (*filters) {
+               (*filters)->destroy(*filters);
+               filters++;
+       }
+
+       dm_free(f->private);
+       dm_free(f);
+}
+
+struct dev_filter *composite_filter_create(int n, struct dev_filter **filters)
+{
+       struct dev_filter **filters_copy, *cft;
+
+       if (!filters)
+               return_NULL;
+
+       if (!(filters_copy = dm_malloc(sizeof(*filters) * (n + 1)))) {
+               log_error("composite filters allocation failed");
+               return NULL;
+       }
+
+       memcpy(filters_copy, filters, sizeof(*filters) * n);
+       filters_copy[n] = NULL;
+
+       if (!(cft = dm_malloc(sizeof(*cft)))) {
+               log_error("compsoite filters allocation failed");
+               dm_free(filters_copy);
+               return NULL;
+       }
+
+       cft->passes_filter = _and_p;
+       cft->destroy = _composite_destroy;
+       cft->use_count = 0;
+       cft->private = filters_copy;
+
+       return cft;
+}
diff --git a/lib/filters/filter-composite.h b/lib/filters/filter-composite.h
new file mode 100644 (file)
index 0000000..75c0b1f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILTER_COMPOSITE_H
+#define _LVM_FILTER_COMPOSITE_H
+
+#include "dev-cache.h"
+
+struct dev_filter *composite_filter_create(int n, struct dev_filter **filters);
+
+#endif
diff --git a/lib/filters/filter-md.c b/lib/filters/filter-md.c
new file mode 100644 (file)
index 0000000..5cd8fb2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2004 Luca Berra
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "filter-md.h"
+#include "metadata.h"
+
+#ifdef linux
+
+static int _ignore_md(struct dev_filter *f __attribute__((unused)),
+                     struct device *dev)
+{
+       int ret;
+       
+       if (!md_filtering())
+               return 1;
+       
+       ret = dev_is_md(dev, NULL);
+
+       if (ret == 1) {
+               log_debug("%s: Skipping md component device", dev_name(dev));
+               return 0;
+       }
+
+       if (ret < 0) {
+               log_debug("%s: Skipping: error in md component detection",
+                         dev_name(dev));
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _destroy(struct dev_filter *f)
+{
+       if (f->use_count)
+               log_error(INTERNAL_ERROR "Destroying md filter while in use %u times.", f->use_count);
+
+       dm_free(f);
+}
+
+struct dev_filter *md_filter_create(void)
+{
+       struct dev_filter *f;
+
+       if (!(f = dm_malloc(sizeof(*f)))) {
+               log_error("md filter allocation failed");
+               return NULL;
+       }
+
+       f->passes_filter = _ignore_md;
+       f->destroy = _destroy;
+       f->use_count = 0;
+       f->private = NULL;
+
+       return f;
+}
+
+#else
+
+struct dev_filter *md_filter_create(void)
+{
+       return NULL;
+}
+
+#endif
diff --git a/lib/filters/filter-md.h b/lib/filters/filter-md.h
new file mode 100644 (file)
index 0000000..6a98f0b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2004 Luca Berra
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILTER_MD_H
+#define _LVM_FILTER_MD_H
+
+#include "dev-cache.h"
+
+struct dev_filter *md_filter_create(void);
+
+#endif
+
diff --git a/lib/filters/filter-persistent.c b/lib/filters/filter-persistent.c
new file mode 100644 (file)
index 0000000..f3b1e05
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "config.h"
+#include "dev-cache.h"
+#include "filter.h"
+#include "filter-persistent.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "activate.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+struct pfilter {
+       char *file;
+       struct dm_hash_table *devices;
+       struct dev_filter *real;
+       time_t ctime;
+};
+
+/*
+ * The hash table holds one of these two states
+ * against each entry.
+ */
+#define PF_BAD_DEVICE ((void *) 1)
+#define PF_GOOD_DEVICE ((void *) 2)
+
+static int _init_hash(struct pfilter *pf)
+{
+       if (pf->devices)
+               dm_hash_destroy(pf->devices);
+
+       if (!(pf->devices = dm_hash_create(128)))
+               return_0;
+
+       return 1;
+}
+
+int persistent_filter_wipe(struct dev_filter *f)
+{
+       struct pfilter *pf = (struct pfilter *) f->private;
+
+       log_verbose("Wiping cache of LVM-capable devices");
+       dm_hash_wipe(pf->devices);
+
+       /* Trigger complete device scan */
+       dev_cache_scan(1);
+
+       return 1;
+}
+
+static int _read_array(struct pfilter *pf, struct config_tree *cft,
+                      const char *path, void *data)
+{
+       const struct config_node *cn;
+       const struct config_value *cv;
+
+       if (!(cn = find_config_node(cft->root, path))) {
+               log_very_verbose("Couldn't find %s array in '%s'",
+                                path, pf->file);
+               return 0;
+       }
+
+       /*
+        * iterate through the array, adding
+        * devices as we go.
+        */
+       for (cv = cn->v; cv; cv = cv->next) {
+               if (cv->type != CFG_STRING) {
+                       log_verbose("Devices array contains a value "
+                                   "which is not a string ... ignoring");
+                       continue;
+               }
+
+               if (!dm_hash_insert(pf->devices, cv->v.str, data))
+                       log_verbose("Couldn't add '%s' to filter ... ignoring",
+                                   cv->v.str);
+               /* Populate dev_cache ourselves */
+               dev_cache_get(cv->v.str, NULL);
+       }
+       return 1;
+}
+
+int persistent_filter_load(struct dev_filter *f, struct config_tree **cft_out)
+{
+       struct pfilter *pf = (struct pfilter *) f->private;
+       struct config_tree *cft;
+       struct stat info;
+       int r = 0;
+
+       if (!stat(pf->file, &info))
+               pf->ctime = info.st_ctime;
+       else {
+               log_very_verbose("%s: stat failed: %s", pf->file,
+                                strerror(errno));
+               return_0;
+       }
+
+       if (!(cft = create_config_tree(pf->file, 1)))
+               return_0;
+
+       if (!read_config_file(cft))
+               goto_out;
+
+       _read_array(pf, cft, "persistent_filter_cache/valid_devices",
+                   PF_GOOD_DEVICE);
+       /* We don't gain anything by holding invalid devices */
+       /* _read_array(pf, cft, "persistent_filter_cache/invalid_devices",
+          PF_BAD_DEVICE); */
+
+       /* Did we find anything? */
+       if (dm_hash_get_num_entries(pf->devices)) {
+               /* We populated dev_cache ourselves */
+               dev_cache_scan(0);
+               r = 1;
+       }
+
+       log_very_verbose("Loaded persistent filter cache from %s", pf->file);
+
+      out:
+       if (r && cft_out)
+               *cft_out = cft;
+       else
+               destroy_config_tree(cft);
+       return r;
+}
+
+static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
+                        void *data)
+{
+       void *d;
+       int first = 1;
+       char buf[2 * PATH_MAX];
+       struct dm_hash_node *n;
+
+       for (n = dm_hash_get_first(pf->devices); n;
+            n = dm_hash_get_next(pf->devices, n)) {
+               d = dm_hash_get_data(pf->devices, n);
+
+               if (d != data)
+                       continue;
+
+               if (!first)
+                       fprintf(fp, ",\n");
+               else {
+                       fprintf(fp, "\t%s=[\n", path);
+                       first = 0;
+               }
+
+               escape_double_quotes(buf, dm_hash_get_key(pf->devices, n));
+               fprintf(fp, "\t\t\"%s\"", buf);
+       }
+
+       if (!first)
+               fprintf(fp, "\n\t]\n");
+}
+
+int persistent_filter_dump(struct dev_filter *f, int merge_existing)
+{
+       struct pfilter *pf;
+       char *tmp_file;
+       struct stat info, info2;
+       struct config_tree *cft = NULL;
+       FILE *fp;
+       int lockfd;
+       int r = 0;
+
+       if (!f)
+               return_0;
+       pf = (struct pfilter *) f->private;
+
+       if (!dm_hash_get_num_entries(pf->devices)) {
+               log_very_verbose("Internal persistent device cache empty "
+                                "- not writing to %s", pf->file);
+               return 0;
+       }
+       if (!dev_cache_has_scanned()) {
+               log_very_verbose("Device cache incomplete - not writing "
+                                "to %s", pf->file);
+               return 0;
+       }
+
+       log_very_verbose("Dumping persistent device cache to %s", pf->file);
+
+       while (1) {
+               if ((lockfd = fcntl_lock_file(pf->file, F_WRLCK, 0)) < 0)
+                       return_0;
+
+               /*
+                * Ensure we locked the file we expected
+                */
+               if (fstat(lockfd, &info)) {
+                       log_sys_error("fstat", pf->file);
+                       goto out;
+               }
+               if (stat(pf->file, &info2)) {
+                       log_sys_error("stat", pf->file);
+                       goto out;
+               }
+
+               if (is_same_inode(info, info2))
+                       break;
+       
+               fcntl_unlock_file(lockfd);
+       }
+
+       /*
+        * If file contents changed since we loaded it, merge new contents
+        */
+       if (merge_existing && info.st_ctime != pf->ctime)
+               /* Keep cft open to avoid losing lock */
+               persistent_filter_load(f, &cft);
+
+       tmp_file = alloca(strlen(pf->file) + 5);
+       sprintf(tmp_file, "%s.tmp", pf->file);
+
+       if (!(fp = fopen(tmp_file, "w"))) {
+               /* EACCES has been reported over NFS */
+               if (errno != EROFS && errno != EACCES)
+                       log_sys_error("fopen", tmp_file);
+               goto out;
+       }
+
+       fprintf(fp, "# This file is automatically maintained by lvm.\n\n");
+       fprintf(fp, "persistent_filter_cache {\n");
+
+       _write_array(pf, fp, "valid_devices", PF_GOOD_DEVICE);
+       /* We don't gain anything by remembering invalid devices */
+       /* _write_array(pf, fp, "invalid_devices", PF_BAD_DEVICE); */
+
+       fprintf(fp, "}\n");
+       if (lvm_fclose(fp, tmp_file))
+               goto_out;
+
+       if (rename(tmp_file, pf->file))
+               log_error("%s: rename to %s failed: %s", tmp_file, pf->file,
+                         strerror(errno));
+
+       r = 1;
+
+out:
+       fcntl_unlock_file(lockfd);
+
+       if (cft)
+               destroy_config_tree(cft);
+
+       return r;
+}
+
+static int _lookup_p(struct dev_filter *f, struct device *dev)
+{
+       struct pfilter *pf = (struct pfilter *) f->private;
+       void *l = dm_hash_lookup(pf->devices, dev_name(dev));
+       struct str_list *sl;
+
+       /* Cached BAD? */
+       if (l == PF_BAD_DEVICE) {
+               log_debug("%s: Skipping (cached)", dev_name(dev));
+               return 0;
+       }
+
+       /* Test dm devices every time, so cache them as GOOD. */
+       if (MAJOR(dev->dev) == dm_major()) {
+               if (!l)
+                       dm_list_iterate_items(sl, &dev->aliases)
+                               dm_hash_insert(pf->devices, sl->str, PF_GOOD_DEVICE);
+               if (!device_is_usable(dev)) {
+                       log_debug("%s: Skipping unusable device", dev_name(dev));
+                       return 0;
+               }
+               return pf->real->passes_filter(pf->real, dev);
+       }
+
+       /* Uncached */
+       if (!l) {
+               l = pf->real->passes_filter(pf->real, dev) ?  PF_GOOD_DEVICE : PF_BAD_DEVICE;
+
+               dm_list_iterate_items(sl, &dev->aliases)
+                       dm_hash_insert(pf->devices, sl->str, l);
+       }
+
+       return (l == PF_BAD_DEVICE) ? 0 : 1;
+}
+
+static void _persistent_destroy(struct dev_filter *f)
+{
+       struct pfilter *pf = (struct pfilter *) f->private;
+
+       if (f->use_count)
+               log_error(INTERNAL_ERROR "Destroying persistent filter while in use %u times.", f->use_count);
+
+       dm_hash_destroy(pf->devices);
+       dm_free(pf->file);
+       pf->real->destroy(pf->real);
+       dm_free(pf);
+       dm_free(f);
+}
+
+struct dev_filter *persistent_filter_create(struct dev_filter *real,
+                                           const char *file)
+{
+       struct pfilter *pf;
+       struct dev_filter *f = NULL;
+       struct stat info;
+
+       if (!(pf = dm_zalloc(sizeof(*pf))))
+               return_NULL;
+
+       if (!(pf->file = dm_malloc(strlen(file) + 1)))
+               goto_bad;
+
+       strcpy(pf->file, file);
+       pf->real = real;
+
+       if (!(_init_hash(pf))) {
+               log_error("Couldn't create hash table for persistent filter.");
+               goto bad;
+       }
+
+       if (!(f = dm_malloc(sizeof(*f))))
+               goto_bad;
+
+       /* Only merge cache file before dumping it if it changed externally. */
+       if (!stat(pf->file, &info))
+               pf->ctime = info.st_ctime;
+
+       f->passes_filter = _lookup_p;
+       f->destroy = _persistent_destroy;
+       f->use_count = 0;
+       f->private = pf;
+
+       return f;
+
+      bad:
+       dm_free(pf->file);
+       if (pf->devices)
+               dm_hash_destroy(pf->devices);
+       dm_free(pf);
+       dm_free(f);
+       return NULL;
+}
diff --git a/lib/filters/filter-persistent.h b/lib/filters/filter-persistent.h
new file mode 100644 (file)
index 0000000..a7f1245
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILTER_PERSISTENT_H
+#define _LVM_FILTER_PERSISTENT_H
+
+#include "dev-cache.h"
+
+struct dev_filter *persistent_filter_create(struct dev_filter *f,
+                                           const char *file);
+
+int persistent_filter_wipe(struct dev_filter *f);
+int persistent_filter_load(struct dev_filter *f, struct config_tree **cft_out);
+int persistent_filter_dump(struct dev_filter *f, int merge_existing);
+
+#endif
diff --git a/lib/filters/filter-regex.c b/lib/filters/filter-regex.c
new file mode 100644 (file)
index 0000000..e046c71
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "filter-regex.h"
+#include "device.h"
+
+struct rfilter {
+       struct dm_pool *mem;
+       dm_bitset_t accept;
+       struct dm_regex *engine;
+};
+
+static int _extract_pattern(struct dm_pool *mem, const char *pat,
+                           char **regex, dm_bitset_t accept, int ix)
+{
+       char sep, *r, *ptr;
+
+       /*
+        * is this an accept or reject pattern
+        */
+       switch (*pat) {
+       case 'a':
+               dm_bit_set(accept, ix);
+               break;
+
+       case 'r':
+               dm_bit_clear(accept, ix);
+               break;
+
+       default:
+               log_info("pattern must begin with 'a' or 'r'");
+               return 0;
+       }
+       pat++;
+
+       /*
+        * get the separator
+        */
+       switch (*pat) {
+       case '(':
+               sep = ')';
+               break;
+
+       case '[':
+               sep = ']';
+               break;
+
+       case '{':
+               sep = '}';
+               break;
+
+       default:
+               sep = *pat;
+       }
+       pat++;
+
+       /*
+        * copy the regex
+        */
+       if (!(r = dm_pool_strdup(mem, pat)))
+               return_0;
+
+       /*
+        * trim the trailing character, having checked it's sep.
+        */
+       ptr = r + strlen(r) - 1;
+       if (*ptr != sep) {
+               log_info("invalid separator at end of regex");
+               return 0;
+       }
+       *ptr = '\0';
+
+       regex[ix] = r;
+       return 1;
+}
+
+static int _build_matcher(struct rfilter *rf, const struct config_value *val)
+{
+       struct dm_pool *scratch;
+       const struct config_value *v;
+       char **regex;
+       unsigned count = 0;
+       int i, r = 0;
+
+       if (!(scratch = dm_pool_create("filter dm_regex", 1024)))
+               return_0;
+
+       /*
+        * count how many patterns we have.
+        */
+       for (v = val; v; v = v->next) {
+               if (v->type != CFG_STRING) {
+                       log_error("Filter patterns must be enclosed in quotes.");
+                       goto out;
+               }
+
+               count++;
+       }
+
+       /*
+        * allocate space for them
+        */
+       if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count)))
+               goto_out;
+
+       /*
+        * create the accept/reject bitset
+        */
+       rf->accept = dm_bitset_create(rf->mem, count);
+
+       /*
+        * fill the array back to front because we
+        * want the opposite precedence to what
+        * the matcher gives.
+        */
+       for (v = val, i = count - 1; v; v = v->next, i--)
+               if (!_extract_pattern(scratch, v->v.str, regex, rf->accept, i)) {
+                       log_error("Invalid filter pattern \"%s\".", v->v.str);
+                       goto out;
+               }
+
+       /*
+        * build the matcher.
+        */
+       if (!(rf->engine = dm_regex_create(rf->mem, (const char **) regex,
+                                          count)))
+               goto_out;
+       r = 1;
+
+      out:
+       dm_pool_destroy(scratch);
+       return r;
+}
+
+static int _accept_p(struct dev_filter *f, struct device *dev)
+{
+       int m, first = 1, rejected = 0;
+       struct rfilter *rf = (struct rfilter *) f->private;
+       struct str_list *sl;
+
+       dm_list_iterate_items(sl, &dev->aliases) {
+               m = dm_regex_match(rf->engine, sl->str);
+
+               if (m >= 0) {
+                       if (dm_bit(rf->accept, m)) {
+                               if (!first)
+                                       dev_set_preferred_name(sl, dev);
+
+                               return 1;
+                       }
+
+                       rejected = 1;
+               }
+
+               first = 0;
+       }
+
+       if (rejected)
+               log_debug("%s: Skipping (regex)", dev_name(dev));
+
+       /*
+        * pass everything that doesn't match
+        * anything.
+        */
+       return !rejected;
+}
+
+static void _regex_destroy(struct dev_filter *f)
+{
+       struct rfilter *rf = (struct rfilter *) f->private;
+
+       if (f->use_count)
+               log_error(INTERNAL_ERROR "Destroying regex filter while in use %u times.", f->use_count);
+
+       dm_pool_destroy(rf->mem);
+}
+
+struct dev_filter *regex_filter_create(const struct config_value *patterns)
+{
+       struct dm_pool *mem = dm_pool_create("filter regex", 10 * 1024);
+       struct rfilter *rf;
+       struct dev_filter *f;
+
+       if (!mem)
+               return_NULL;
+
+       if (!(rf = dm_pool_alloc(mem, sizeof(*rf))))
+               goto_bad;
+
+       rf->mem = mem;
+
+       if (!_build_matcher(rf, patterns))
+               goto_bad;
+
+       if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
+               goto_bad;
+
+       f->passes_filter = _accept_p;
+       f->destroy = _regex_destroy;
+       f->use_count = 0;
+       f->private = rf;
+       return f;
+
+      bad:
+       dm_pool_destroy(mem);
+       return NULL;
+}
diff --git a/lib/filters/filter-regex.h b/lib/filters/filter-regex.h
new file mode 100644 (file)
index 0000000..a009c91
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILTER_REGEX_H
+#define _LVM_FILTER_REGEX_H
+
+#include "config.h"
+#include "dev-cache.h"
+
+/*
+ * patterns must be an array of strings of the form:
+ * [ra]<sep><regex><sep>, eg,
+ * r/cdrom/          - reject cdroms
+ * a|loop/[0-4]|     - accept loops 0 to 4
+ * r|.*|             - reject everything else
+ */
+
+struct dev_filter *regex_filter_create(const struct config_value *patterns);
+
+#endif
diff --git a/lib/filters/filter-sysfs.c b/lib/filters/filter-sysfs.c
new file mode 100644 (file)
index 0000000..be8fbde
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "filter-sysfs.h"
+#include "lvm-string.h"
+
+#ifdef linux
+
+#include <dirent.h>
+
+static int _locate_sysfs_blocks(const char *sysfs_dir, char *path, size_t len,
+                               unsigned *sysfs_depth)
+{
+       struct stat info;
+
+       /*
+        * unified classification directory for all kernel subsystems
+        *
+        * /sys/subsystem/block/devices
+        * |-- sda -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
+        * |-- sda1 -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
+        *  `-- sr0 -> ../../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
+        *
+        */
+       if (dm_snprintf(path, len, "%s/%s", sysfs_dir,
+                       "subsystem/block/devices") >= 0) {
+               if (!stat(path, &info)) {
+                       *sysfs_depth = 0;
+                       return 1;
+               }
+       }
+
+       /*
+        * block subsystem as a class
+        *
+        * /sys/class/block
+        * |-- sda -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
+        * |-- sda1 -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
+        *  `-- sr0 -> ../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
+        *
+        */
+       if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "class/block") >= 0) {
+               if (!stat(path, &info)) {
+                       *sysfs_depth = 0;
+                       return 1;
+               }
+       }
+
+       /*
+        * old block subsystem layout with nested directories
+        *
+        * /sys/block/
+        * |-- sda
+        * |   |-- capability
+        * |   |-- dev
+        * ...
+        * |   |-- sda1
+        * |   |   |-- dev
+        * ...
+        * |
+        * `-- sr0
+        *     |-- capability
+        *     |-- dev
+        * ...
+        *
+        */
+       if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "block") >= 0) {
+               if (!stat(path, &info)) {
+                       *sysfs_depth = 1;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*----------------------------------------------------------------
+ * We need to store a set of dev_t.
+ *--------------------------------------------------------------*/
+struct entry {
+       struct entry *next;
+       dev_t dev;
+};
+
+#define SET_BUCKETS 64
+struct dev_set {
+       struct dm_pool *mem;
+       const char *sys_block;
+       unsigned sysfs_depth;
+       int initialised;
+       struct entry *slots[SET_BUCKETS];
+};
+
+static struct dev_set *_dev_set_create(struct dm_pool *mem,
+                                      const char *sys_block,
+                                      unsigned sysfs_depth)
+{
+       struct dev_set *ds;
+
+       if (!(ds = dm_pool_zalloc(mem, sizeof(*ds))))
+               return NULL;
+
+       ds->mem = mem;
+       ds->sys_block = dm_pool_strdup(mem, sys_block);
+       ds->sysfs_depth = sysfs_depth;
+       ds->initialised = 0;
+
+       return ds;
+}
+
+static unsigned _hash_dev(dev_t dev)
+{
+       return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1);
+}
+
+/*
+ * Doesn't check that the set already contains dev.
+ */
+static int _set_insert(struct dev_set *ds, dev_t dev)
+{
+       struct entry *e;
+       unsigned h = _hash_dev(dev);
+
+       if (!(e = dm_pool_alloc(ds->mem, sizeof(*e))))
+               return 0;
+
+       e->next = ds->slots[h];
+       e->dev = dev;
+       ds->slots[h] = e;
+
+       return 1;
+}
+
+static int _set_lookup(struct dev_set *ds, dev_t dev)
+{
+       unsigned h = _hash_dev(dev);
+       struct entry *e;
+
+       for (e = ds->slots[h]; e; e = e->next)
+               if (e->dev == dev)
+                       return 1;
+
+       return 0;
+}
+
+/*----------------------------------------------------------------
+ * filter methods
+ *--------------------------------------------------------------*/
+static int _parse_dev(const char *file, FILE *fp, dev_t *result)
+{
+       unsigned major, minor;
+       char buffer[64];
+
+       if (!fgets(buffer, sizeof(buffer), fp)) {
+               log_error("Empty sysfs device file: %s", file);
+               return 0;
+       }
+
+       if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
+               log_info("sysfs device file not correct format");
+               return 0;
+       }
+
+       *result = makedev(major, minor);
+       return 1;
+}
+
+static int _read_dev(const char *file, dev_t *result)
+{
+       int r;
+       FILE *fp;
+
+       if (!(fp = fopen(file, "r"))) {
+               log_sys_error("fopen", file);
+               return 0;
+       }
+
+       r = _parse_dev(file, fp, result);
+
+       if (fclose(fp))
+               log_sys_error("fclose", file);
+
+       return r;
+}
+
+/*
+ * Recurse through sysfs directories, inserting any devs found.
+ */
+static int _read_devs(struct dev_set *ds, const char *dir, unsigned sysfs_depth)
+{
+       struct dirent *d;
+       DIR *dr;
+       struct stat info;
+       char path[PATH_MAX];
+       char file[PATH_MAX];
+       dev_t dev = { 0 };
+       int r = 1;
+
+       if (!(dr = opendir(dir))) {
+               log_sys_error("opendir", dir);
+               return 0;
+       }
+
+       while ((d = readdir(dr))) {
+               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       continue;
+
+               if (dm_snprintf(path, sizeof(path), "%s/%s", dir,
+                                d->d_name) < 0) {
+                       log_error("sysfs path name too long: %s in %s",
+                                 d->d_name, dir);
+                       continue;
+               }
+
+               /* devices have a "dev" file */
+               if (dm_snprintf(file, sizeof(file), "%s/dev", path) < 0) {
+                       log_error("sysfs path name too long: %s in %s",
+                                 d->d_name, dir);
+                       continue;
+               }
+
+               if (!stat(file, &info)) {
+                       /* recurse if we found a device and expect subdirs */
+                       if (sysfs_depth)
+                               _read_devs(ds, path, sysfs_depth - 1);
+
+                       /* add the device we have found */
+                       if (_read_dev(file, &dev))
+                               _set_insert(ds, dev);
+               }
+       }
+
+       if (closedir(dr))
+               log_sys_error("closedir", dir);
+
+       return r;
+}
+
+static int _init_devs(struct dev_set *ds)
+{
+       if (!_read_devs(ds, ds->sys_block, ds->sysfs_depth)) {
+               ds->initialised = -1;
+               return 0;
+       }
+
+       ds->initialised = 1;
+
+       return 1;
+}
+
+
+static int _accept_p(struct dev_filter *f, struct device *dev)
+{
+       struct dev_set *ds = (struct dev_set *) f->private;
+
+       if (!ds->initialised)
+               _init_devs(ds);
+
+       /* Pass through if initialisation failed */
+       if (ds->initialised != 1)
+               return 1;
+
+       if (!_set_lookup(ds, dev->dev)) {
+               log_debug("%s: Skipping (sysfs)", dev_name(dev));
+               return 0;
+       } else
+               return 1;
+}
+
+static void _destroy(struct dev_filter *f)
+{
+       struct dev_set *ds = (struct dev_set *) f->private;
+
+       if (f->use_count)
+               log_error(INTERNAL_ERROR "Destroying sysfs filter while in use %u times.", f->use_count);
+
+       dm_pool_destroy(ds->mem);
+}
+
+struct dev_filter *sysfs_filter_create(const char *sysfs_dir)
+{
+       char sys_block[PATH_MAX];
+       unsigned sysfs_depth;
+       struct dm_pool *mem;
+       struct dev_set *ds;
+       struct dev_filter *f;
+
+       if (!*sysfs_dir) {
+               log_verbose("No proc filesystem found: skipping sysfs filter");
+               return NULL;
+       }
+
+       if (!_locate_sysfs_blocks(sysfs_dir, sys_block, sizeof(sys_block), &sysfs_depth))
+               return NULL;
+
+       if (!(mem = dm_pool_create("sysfs", 256))) {
+               log_error("sysfs pool creation failed");
+               return NULL;
+       }
+
+       if (!(ds = _dev_set_create(mem, sys_block, sysfs_depth))) {
+               log_error("sysfs dev_set creation failed");
+               goto bad;
+       }
+
+       if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
+               goto_bad;
+
+       f->passes_filter = _accept_p;
+       f->destroy = _destroy;
+       f->use_count = 0;
+       f->private = ds;
+       return f;
+
+ bad:
+       dm_pool_destroy(mem);
+       return NULL;
+}
+
+#else
+
+struct dev_filter *sysfs_filter_create(const char *sysfs_dir __attribute__((unused)))
+{
+       return NULL;
+}
+
+#endif
diff --git a/lib/filters/filter-sysfs.h b/lib/filters/filter-sysfs.h
new file mode 100644 (file)
index 0000000..9e4f503
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILTER_SYSFS_H
+#define _LVM_FILTER_SYSFS_H
+
+#include "config.h"
+#include "dev-cache.h"
+
+struct dev_filter *sysfs_filter_create(const char *sysfs_dir);
+
+#endif
diff --git a/lib/filters/filter.c b/lib/filters/filter.c
new file mode 100644 (file)
index 0000000..c623d2a
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "dev-cache.h"
+#include "filter.h"
+#include "lvm-string.h"
+#include "config.h"
+#include "metadata.h"
+#include "activate.h"
+
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#define NUMBER_OF_MAJORS 4096
+
+/* 0 means LVM won't use this major number. */
+static int _max_partitions_by_major[NUMBER_OF_MAJORS];
+
+typedef struct {
+       const char *name;
+       const int max_partitions;
+} device_info_t;
+
+static int _md_major = -1;
+static int _blkext_major = -1;
+static int _drbd_major = -1;
+static int _device_mapper_major = -1;
+
+int dm_major(void)
+{
+       return _device_mapper_major;
+}
+
+int md_major(void)
+{
+       return _md_major;
+}
+
+int blkext_major(void)
+{
+       return _blkext_major;
+}
+
+int dev_subsystem_part_major(const struct device *dev)
+{
+       dev_t primary_dev;
+
+       if (MAJOR(dev->dev) == -1)
+               return 0;
+
+       if (MAJOR(dev->dev) == _md_major)
+               return 1;
+
+       if (MAJOR(dev->dev) == _drbd_major)
+               return 1;
+
+       if ((MAJOR(dev->dev) == _blkext_major) &&
+           (get_primary_dev(sysfs_dir_path(), dev, &primary_dev)) &&
+           (MAJOR(primary_dev) == _md_major))
+               return 1;
+
+       return 0;
+}
+
+const char *dev_subsystem_name(const struct device *dev)
+{
+       if (MAJOR(dev->dev) == _md_major)
+               return "MD";
+
+       if (MAJOR(dev->dev) == _drbd_major)
+               return "DRBD";
+
+       if (MAJOR(dev->dev) == _blkext_major)
+               return "BLKEXT";
+
+       return "";
+}
+
+/*
+ * Devices are only checked for partition tables if their minor number
+ * is a multiple of the number corresponding to their type below
+ * i.e. this gives the granularity of whole-device minor numbers.
+ * Use 1 if the device is not partitionable.
+ *
+ * The list can be supplemented with devices/types in the config file.
+ */
+static const device_info_t device_info[] = {
+       {"ide", 64},            /* IDE disk */
+       {"sd", 16},             /* SCSI disk */
+       {"md", 1},              /* Multiple Disk driver (SoftRAID) */
+       {"mdp", 1},             /* Partitionable MD */
+       {"loop", 1},            /* Loop device */
+       {"dasd", 4},            /* DASD disk (IBM S/390, zSeries) */
+       {"dac960", 8},          /* DAC960 */
+       {"nbd", 16},            /* Network Block Device */
+       {"ida", 16},            /* Compaq SMART2 */
+       {"cciss", 16},          /* Compaq CCISS array */
+       {"ubd", 16},            /* User-mode virtual block device */
+       {"ataraid", 16},        /* ATA Raid */
+       {"drbd", 16},           /* Distributed Replicated Block Device */
+       {"emcpower", 16},       /* EMC Powerpath */
+       {"power2", 16},         /* EMC Powerpath */
+       {"i2o_block", 16},      /* i2o Block Disk */
+       {"iseries/vd", 8},      /* iSeries disks */
+       {"gnbd", 1},            /* Network block device */
+       {"ramdisk", 1},         /* RAM disk */
+       {"aoe", 16},            /* ATA over Ethernet */
+       {"device-mapper", 1},   /* Other mapped devices */
+       {"xvd", 16},            /* Xen virtual block device */
+       {"vdisk", 8},           /* SUN's LDOM virtual block device */
+       {"ps3disk", 16},        /* PlayStation 3 internal disk */
+       {"virtblk", 8},         /* VirtIO disk */
+       {"mmc", 16},            /* MMC block device */
+       {"blkext", 1},          /* Extended device partitions */
+       {NULL, 0}
+};
+
+static int _passes_lvm_type_device_filter(struct dev_filter *f __attribute__((unused)),
+                                         struct device *dev)
+{
+       const char *name = dev_name(dev);
+       int ret = 0;
+       uint64_t size;
+
+       /* Is this a recognised device type? */
+       if (!_max_partitions_by_major[MAJOR(dev->dev)]) {
+               log_debug("%s: Skipping: Unrecognised LVM device type %"
+                         PRIu64, name, (uint64_t) MAJOR(dev->dev));
+               return 0;
+       }
+
+       /* Check it's accessible */
+       if (!dev_open_flags(dev, O_RDONLY, 0, 1)) {
+               log_debug("%s: Skipping: open failed", name);
+               return 0;
+       }
+       
+       /* Check it's not too small */
+       if (!dev_get_size(dev, &size)) {
+               log_debug("%s: Skipping: dev_get_size failed", name);
+               goto out;
+       }
+
+       if (size < PV_MIN_SIZE) {
+               log_debug("%s: Skipping: Too small to hold a PV", name);
+               goto out;
+       }
+
+       if (is_partitioned_dev(dev)) {
+               log_debug("%s: Skipping: Partition table signature found",
+                         name);
+               goto out;
+       }
+
+       ret = 1;
+
+      out:
+       dev_close(dev);
+
+       return ret;
+}
+
+static int _scan_proc_dev(const char *proc, const struct config_node *cn)
+{
+       char line[80];
+       char proc_devices[PATH_MAX];
+       FILE *pd = NULL;
+       int i, j = 0;
+       int line_maj = 0;
+       int blocksection = 0;
+       size_t dev_len = 0;
+       const struct config_value *cv;
+       const char *name;
+
+
+       if (!*proc) {
+               log_verbose("No proc filesystem found: using all block device "
+                           "types");
+               for (i = 0; i < NUMBER_OF_MAJORS; i++)
+                       _max_partitions_by_major[i] = 1;
+               return 1;
+       }
+
+       /* All types unrecognised initially */
+       memset(_max_partitions_by_major, 0, sizeof(int) * NUMBER_OF_MAJORS);
+
+       if (dm_snprintf(proc_devices, sizeof(proc_devices),
+                        "%s/devices", proc) < 0) {
+               log_error("Failed to create /proc/devices string");
+               return 0;
+       }
+
+       if (!(pd = fopen(proc_devices, "r"))) {
+               log_sys_error("fopen", proc_devices);
+               return 0;
+       }
+
+       while (fgets(line, 80, pd) != NULL) {
+               i = 0;
+               while (line[i] == ' ' && line[i] != '\0')
+                       i++;
+
+               /* If it's not a number it may be name of section */
+               line_maj = atoi(((char *) (line + i)));
+               if (!line_maj) {
+                       blocksection = (line[i] == 'B') ? 1 : 0;
+                       continue;
+               }
+
+               /* We only want block devices ... */
+               if (!blocksection)
+                       continue;
+
+               /* Find the start of the device major name */
+               while (line[i] != ' ' && line[i] != '\0')
+                       i++;
+               while (line[i] == ' ' && line[i] != '\0')
+                       i++;
+
+               /* Look for md device */
+               if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
+                       _md_major = line_maj;
+
+               /* Look for blkext device */
+               if (!strncmp("blkext", line + i, 6) && isspace(*(line + i + 6)))
+                       _blkext_major = line_maj;
+
+               /* Look for drbd device */
+               if (!strncmp("drbd", line + i, 4) && isspace(*(line + i + 4)))
+                       _drbd_major = line_maj;
+
+               /* Look for device-mapper device */
+               /* FIXME Cope with multiple majors */
+               if (!strncmp("device-mapper", line + i, 13) && isspace(*(line + i + 13)))
+                       _device_mapper_major = line_maj;
+
+               /* Go through the valid device names and if there is a
+                  match store max number of partitions */
+               for (j = 0; device_info[j].name != NULL; j++) {
+                       dev_len = strlen(device_info[j].name);
+                       if (dev_len <= strlen(line + i) &&
+                           !strncmp(device_info[j].name, line + i, dev_len) &&
+                           (line_maj < NUMBER_OF_MAJORS)) {
+                               _max_partitions_by_major[line_maj] =
+                                   device_info[j].max_partitions;
+                               break;
+                       }
+               }
+
+               if (!cn)
+                       continue;
+
+               /* Check devices/types for local variations */
+               for (cv = cn->v; cv; cv = cv->next) {
+                       if (cv->type != CFG_STRING) {
+                               log_error("Expecting string in devices/types "
+                                         "in config file");
+                               if (fclose(pd))
+                                       log_sys_error("fclose", proc_devices);
+                               return 0;
+                       }
+                       dev_len = strlen(cv->v.str);
+                       name = cv->v.str;
+                       cv = cv->next;
+                       if (!cv || cv->type != CFG_INT) {
+                               log_error("Max partition count missing for %s "
+                                         "in devices/types in config file",
+                                         name);
+                               if (fclose(pd))
+                                       log_sys_error("fclose", proc_devices);
+                               return 0;
+                       }
+                       if (!cv->v.i) {
+                               log_error("Zero partition count invalid for "
+                                         "%s in devices/types in config file",
+                                         name);
+                               if (fclose(pd))
+                                       log_sys_error("fclose", proc_devices);
+                               return 0;
+                       }
+                       if (dev_len <= strlen(line + i) &&
+                           !strncmp(name, line + i, dev_len) &&
+                           (line_maj < NUMBER_OF_MAJORS)) {
+                               _max_partitions_by_major[line_maj] = cv->v.i;
+                               break;
+                       }
+               }
+       }
+
+       if (fclose(pd))
+               log_sys_error("fclose", proc_devices);
+
+       return 1;
+}
+
+int max_partitions(int major)
+{
+       return _max_partitions_by_major[major];
+}
+
+struct dev_filter *lvm_type_filter_create(const char *proc,
+                                         const struct config_node *cn)
+{
+       struct dev_filter *f;
+
+       if (!(f = dm_malloc(sizeof(struct dev_filter)))) {
+               log_error("LVM type filter allocation failed");
+               return NULL;
+       }
+
+       f->passes_filter = _passes_lvm_type_device_filter;
+       f->destroy = lvm_type_filter_destroy;
+       f->use_count = 0;
+       f->private = NULL;
+
+       if (!_scan_proc_dev(proc, cn)) {
+               dm_free(f);
+               return_NULL;
+       }
+
+       return f;
+}
+
+void lvm_type_filter_destroy(struct dev_filter *f)
+{
+       if (f->use_count)
+               log_error(INTERNAL_ERROR "Destroying lvm_type filter while in use %u times.", f->use_count);
+
+       dm_free(f);
+}
diff --git a/lib/filters/filter.h b/lib/filters/filter.h
new file mode 100644 (file)
index 0000000..07611f9
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILTER_H
+#define _LVM_FILTER_H
+
+#include "config.h"
+
+#include <sys/stat.h>
+
+#ifdef linux
+#  define MAJOR(dev)   ((dev & 0xfff00) >> 8)
+#  define MINOR(dev)   ((dev & 0xff) | ((dev >> 12) & 0xfff00))
+#  define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
+#else
+#  define MAJOR(x) major((x))
+#  define MINOR(x) minor((x))
+#  define MKDEV(x,y) makedev((x),(y))
+#endif
+
+struct dev_filter *lvm_type_filter_create(const char *proc,
+                                         const struct config_node *cn);
+
+void lvm_type_filter_destroy(struct dev_filter *f);
+
+int dm_major(void);
+int md_major(void);
+int blkext_major(void);
+int max_partitions(int major);
+
+int dev_subsystem_part_major(const struct device *dev);
+const char *dev_subsystem_name(const struct device *dev);
+
+#endif
diff --git a/lib/format1/.exported_symbols b/lib/format1/.exported_symbols
new file mode 100644 (file)
index 0000000..e9fac2e
--- /dev/null
@@ -0,0 +1 @@
+init_format
diff --git a/lib/format1/Makefile.in b/lib/format1/Makefile.in
new file mode 100644 (file)
index 0000000..e102fe8
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+       disk-rep.c \
+       format1.c \
+       import-export.c \
+       import-extents.c \
+       layout.c \
+       lvm1-label.c \
+       vg_number.c
+
+LIB_SHARED = liblvm2format1.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
diff --git a/lib/format1/disk-rep.c b/lib/format1/disk-rep.c
new file mode 100644 (file)
index 0000000..071b39d
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+#include "xlate.h"
+#include "filter.h"
+#include "lvmcache.h"
+
+#include <fcntl.h>
+
+#define xx16(v) disk->v = xlate16(disk->v)
+#define xx32(v) disk->v = xlate32(disk->v)
+#define xx64(v) disk->v = xlate64(disk->v)
+
+/*
+ * Functions to perform the endian conversion
+ * between disk and core.  The same code works
+ * both ways of course.
+ */
+static void _xlate_pvd(struct pv_disk *disk)
+{
+       xx16(version);
+
+       xx32(pv_on_disk.base);
+       xx32(pv_on_disk.size);
+       xx32(vg_on_disk.base);
+       xx32(vg_on_disk.size);
+       xx32(pv_uuidlist_on_disk.base);
+       xx32(pv_uuidlist_on_disk.size);
+       xx32(lv_on_disk.base);
+       xx32(lv_on_disk.size);
+       xx32(pe_on_disk.base);
+       xx32(pe_on_disk.size);
+
+       xx32(pv_major);
+       xx32(pv_number);
+       xx32(pv_status);
+       xx32(pv_allocatable);
+       xx32(pv_size);
+       xx32(lv_cur);
+       xx32(pe_size);
+       xx32(pe_total);
+       xx32(pe_allocated);
+       xx32(pe_start);
+}
+
+static void _xlate_lvd(struct lv_disk *disk)
+{
+       xx32(lv_access);
+       xx32(lv_status);
+       xx32(lv_open);
+       xx32(lv_dev);
+       xx32(lv_number);
+       xx32(lv_mirror_copies);
+       xx32(lv_recovery);
+       xx32(lv_schedule);
+       xx32(lv_size);
+       xx32(lv_snapshot_minor);
+       xx16(lv_chunk_size);
+       xx16(dummy);
+       xx32(lv_allocated_le);
+       xx32(lv_stripes);
+       xx32(lv_stripesize);
+       xx32(lv_badblock);
+       xx32(lv_allocation);
+       xx32(lv_io_timeout);
+       xx32(lv_read_ahead);
+}
+
+static void _xlate_vgd(struct vg_disk *disk)
+{
+       xx32(vg_number);
+       xx32(vg_access);
+       xx32(vg_status);
+       xx32(lv_max);
+       xx32(lv_cur);
+       xx32(lv_open);
+       xx32(pv_max);
+       xx32(pv_cur);
+       xx32(pv_act);
+       xx32(dummy);
+       xx32(vgda);
+       xx32(pe_size);
+       xx32(pe_total);
+       xx32(pe_allocated);
+       xx32(pvg_total);
+}
+
+static void _xlate_extents(struct pe_disk *extents, uint32_t count)
+{
+       unsigned i;
+
+       for (i = 0; i < count; i++) {
+               extents[i].lv_num = xlate16(extents[i].lv_num);
+               extents[i].le_num = xlate16(extents[i].le_num);
+       }
+}
+
+/*
+ * Handle both minor metadata formats.
+ */
+static int _munge_formats(struct pv_disk *pvd)
+{
+       uint32_t pe_start;
+       unsigned b, e;
+
+       switch (pvd->version) {
+       case 1:
+               pvd->pe_start = ((pvd->pe_on_disk.base +
+                                 pvd->pe_on_disk.size) >> SECTOR_SHIFT);
+               break;
+
+       case 2:
+               pvd->version = 1;
+               pe_start = pvd->pe_start << SECTOR_SHIFT;
+               pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
+               break;
+
+       default:
+               return 0;
+       }
+
+       /* UUID too long? */
+       if (pvd->pv_uuid[ID_LEN]) {
+               /* Retain ID_LEN chars from end */
+               for (e = ID_LEN; e < sizeof(pvd->pv_uuid); e++) {
+                       if (!pvd->pv_uuid[e]) {
+                               e--;
+                               break;
+                       }
+               }
+               for (b = 0; b < ID_LEN; b++) {
+                       pvd->pv_uuid[b] = pvd->pv_uuid[++e - ID_LEN];
+                       /* FIXME Remove all invalid chars */
+                       if (pvd->pv_uuid[b] == '/')
+                               pvd->pv_uuid[b] = '#';
+               }
+               memset(&pvd->pv_uuid[ID_LEN], 0, sizeof(pvd->pv_uuid) - ID_LEN);
+       }
+
+       /* If UUID is missing, create one */
+       if (pvd->pv_uuid[0] == '\0') {
+               uuid_from_num((char *)pvd->pv_uuid, pvd->pv_number);
+               pvd->pv_uuid[ID_LEN] = '\0';
+       }
+
+       return 1;
+}
+
+/*
+ * If exported, remove "PV_EXP" from end of VG name
+ */
+static void _munge_exported_vg(struct pv_disk *pvd)
+{
+       int l;
+       size_t s;
+
+       /* Return if PV not in a VG */
+       if ((!*pvd->vg_name))
+               return;
+       /* FIXME also check vgd->status & VG_EXPORTED? */
+
+       l = strlen((char *)pvd->vg_name);
+       s = sizeof(EXPORTED_TAG);
+       if (!strncmp((char *)pvd->vg_name + l - s + 1, EXPORTED_TAG, s)) {
+               pvd->vg_name[l - s + 1] = '\0';
+               pvd->pv_status |= VG_EXPORTED;
+       }
+}
+
+int munge_pvd(struct device *dev, struct pv_disk *pvd)
+{
+       _xlate_pvd(pvd);
+
+       if (pvd->id[0] != 'H' || pvd->id[1] != 'M') {
+               log_very_verbose("%s does not have a valid LVM1 PV identifier",
+                                dev_name(dev));
+               return 0;
+       }
+
+       if (!_munge_formats(pvd)) {
+               log_very_verbose("format1: Unknown metadata version %d "
+                                "found on %s", pvd->version, dev_name(dev));
+               return 0;
+       }
+
+       /* If VG is exported, set VG name back to the real name */
+       _munge_exported_vg(pvd);
+
+       return 1;
+}
+
+static int _read_pvd(struct device *dev, struct pv_disk *pvd)
+{
+       if (!dev_read(dev, UINT64_C(0), sizeof(*pvd), pvd)) {
+               log_very_verbose("Failed to read PV data from %s",
+                                dev_name(dev));
+               return 0;
+       }
+
+       return munge_pvd(dev, pvd);
+}
+
+static int _read_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
+{
+       if (!dev_read(dev, pos, sizeof(*disk), disk))
+               return_0;
+
+       _xlate_lvd(disk);
+
+       return 1;
+}
+
+int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd)
+{
+       uint64_t pos = pvd->vg_on_disk.base;
+
+       if (!dev_read(dev, pos, sizeof(*vgd), vgd))
+               return_0;
+
+       _xlate_vgd(vgd);
+
+       if ((vgd->lv_max > MAX_LV) || (vgd->pv_max > MAX_PV))
+               return_0;
+               
+       /* If UUID is missing, create one */
+       if (vgd->vg_uuid[0] == '\0')
+               uuid_from_num((char *)vgd->vg_uuid, vgd->vg_number);
+
+       return 1;
+}
+
+static int _read_uuids(struct disk_list *data)
+{
+       unsigned num_read = 0;
+       struct uuid_list *ul;
+       char buffer[NAME_LEN] __attribute__((aligned(8)));
+       uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
+       uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
+
+       while (pos < end && num_read < data->vgd.pv_cur) {
+               if (!dev_read(data->dev, pos, sizeof(buffer), buffer))
+                       return_0;
+
+               if (!(ul = dm_pool_alloc(data->mem, sizeof(*ul))))
+                       return_0;
+
+               memcpy(ul->uuid, buffer, NAME_LEN);
+               ul->uuid[NAME_LEN - 1] = '\0';
+
+               dm_list_add(&data->uuids, &ul->list);
+
+               pos += NAME_LEN;
+               num_read++;
+       }
+
+       return 1;
+}
+
+static int _check_lvd(struct lv_disk *lvd)
+{
+       return !(lvd->lv_name[0] == '\0');
+}
+
+static int _read_lvs(struct disk_list *data)
+{
+       unsigned int i, lvs_read = 0;
+       uint64_t pos;
+       struct lvd_list *ll;
+       struct vg_disk *vgd = &data->vgd;
+
+       for (i = 0; (i < vgd->lv_max) && (lvs_read < vgd->lv_cur); i++) {
+               pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
+               ll = dm_pool_alloc(data->mem, sizeof(*ll));
+
+               if (!ll)
+                       return_0;
+
+               if (!_read_lvd(data->dev, pos, &ll->lvd))
+                       return_0;
+
+               if (!_check_lvd(&ll->lvd))
+                       continue;
+
+               lvs_read++;
+               dm_list_add(&data->lvds, &ll->list);
+       }
+
+       return 1;
+}
+
+static int _read_extents(struct disk_list *data)
+{
+       size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
+       struct pe_disk *extents = dm_pool_alloc(data->mem, len);
+       uint64_t pos = data->pvd.pe_on_disk.base;
+
+       if (!extents)
+               return_0;
+
+       if (!dev_read(data->dev, pos, len, extents))
+               return_0;
+
+       _xlate_extents(extents, data->pvd.pe_total);
+       data->extents = extents;
+
+       return 1;
+}
+
+static void __update_lvmcache(const struct format_type *fmt,
+                             struct disk_list *dl,
+                             struct device *dev, const char *vgid,
+                             unsigned exported)
+{
+       struct lvmcache_info *info;
+       const char *vgname = *((char *)dl->pvd.vg_name) ?
+                            (char *)dl->pvd.vg_name : fmt->orphan_vg_name;
+
+       if (!(info = lvmcache_add(fmt->labeller, (char *)dl->pvd.pv_uuid, dev,
+                                 vgname, vgid, exported ? EXPORTED_VG : 0))) {
+               stack;
+               return;
+       }
+
+       info->device_size = xlate32(dl->pvd.pv_size) << SECTOR_SHIFT;
+       dm_list_init(&info->mdas);
+       info->status &= ~CACHE_INVALID;
+}
+
+static struct disk_list *__read_disk(const struct format_type *fmt,
+                                    struct device *dev, struct dm_pool *mem,
+                                    const char *vg_name)
+{
+       struct disk_list *dl = dm_pool_zalloc(mem, sizeof(*dl));
+       const char *name = dev_name(dev);
+
+       if (!dl)
+               return_NULL;
+
+       dl->dev = dev;
+       dl->mem = mem;
+       dm_list_init(&dl->uuids);
+       dm_list_init(&dl->lvds);
+
+       if (!_read_pvd(dev, &dl->pvd))
+               goto_bad;
+
+       /*
+        * is it an orphan ?
+        */
+       if (!*dl->pvd.vg_name) {
+               log_very_verbose("%s is not a member of any format1 VG", name);
+
+               __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
+               return (vg_name) ? NULL : dl;
+       }
+
+       if (!read_vgd(dl->dev, &dl->vgd, &dl->pvd)) {
+               log_error("Failed to read VG data from PV (%s)", name);
+               __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
+               goto bad;
+       }
+
+       if (vg_name && strcmp(vg_name, (char *)dl->pvd.vg_name)) {
+               log_very_verbose("%s is not a member of the VG %s",
+                                name, vg_name);
+               __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
+               goto bad;
+       }
+
+       __update_lvmcache(fmt, dl, dev, (char *)dl->vgd.vg_uuid,
+                         dl->vgd.vg_status & VG_EXPORTED);
+
+       if (!_read_uuids(dl)) {
+               log_error("Failed to read PV uuid list from %s", name);
+               goto bad;
+       }
+
+       if (!_read_lvs(dl)) {
+               log_error("Failed to read LV's from %s", name);
+               goto bad;
+       }
+
+       if (!_read_extents(dl)) {
+               log_error("Failed to read extents from %s", name);
+               goto bad;
+       }
+
+       log_very_verbose("Found %s in %sVG %s", name,
+                        (dl->vgd.vg_status & VG_EXPORTED) ? "exported " : "",
+                        dl->pvd.vg_name);
+
+       return dl;
+
+      bad:
+       dm_pool_free(dl->mem, dl);
+       return NULL;
+}
+
+struct disk_list *read_disk(const struct format_type *fmt, struct device *dev,
+                           struct dm_pool *mem, const char *vg_name)
+{
+       struct disk_list *dl;
+
+       if (!dev_open(dev))
+               return_NULL;
+
+       dl = __read_disk(fmt, dev, mem, vg_name);
+
+       if (!dev_close(dev))
+               stack;
+
+       return dl;
+}
+
+static void _add_pv_to_list(struct dm_list *head, struct disk_list *data)
+{
+       struct pv_disk *pvd;
+       struct disk_list *diskl;
+
+       dm_list_iterate_items(diskl, head) {
+               pvd = &diskl->pvd;
+               if (!strncmp((char *)data->pvd.pv_uuid, (char *)pvd->pv_uuid,
+                            sizeof(pvd->pv_uuid))) {
+                       if (!dev_subsystem_part_major(data->dev)) {
+                               log_very_verbose("Ignoring duplicate PV %s on "
+                                                "%s", pvd->pv_uuid,
+                                                dev_name(data->dev));
+                               return;
+                       }
+                       log_very_verbose("Duplicate PV %s - using %s %s",
+                                        pvd->pv_uuid, dev_subsystem_name(data->dev),
+                                        dev_name(data->dev));
+                       dm_list_del(&diskl->list);
+                       break;
+               }
+       }
+       dm_list_add(head, &data->list);
+}
+
+/*
+ * Build a list of pv_d's structures, allocated from mem.
+ * We keep track of the first object allocated from the pool
+ * so we can free off all the memory if something goes wrong.
+ */
+int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name,
+                  struct dev_filter *filter, struct dm_pool *mem,
+                  struct dm_list *head)
+{
+       struct dev_iter *iter;
+       struct device *dev;
+       struct disk_list *data = NULL;
+       struct lvmcache_vginfo *vginfo;
+       struct lvmcache_info *info;
+
+       /* Fast path if we already saw this VG and cached the list of PVs */
+       if (vg_name && (vginfo = vginfo_from_vgname(vg_name, NULL)) &&
+           vginfo->infos.n) {
+               dm_list_iterate_items(info, &vginfo->infos) {
+                       dev = info->dev;
+                       if (!dev || !(data = read_disk(fmt, dev, mem, vg_name)))
+                               break;
+                       _add_pv_to_list(head, data);
+               }
+
+               /* Did we find the whole VG? */
+               if (!vg_name || is_orphan_vg(vg_name) ||
+                   (data && *data->pvd.vg_name &&
+                    dm_list_size(head) == data->vgd.pv_cur))
+                       return 1;
+
+               /* Failed */
+               dm_list_init(head);
+               /* vgcache_del(vg_name); */
+       }
+
+       if (!(iter = dev_iter_create(filter, 1))) {
+               log_error("read_pvs_in_vg: dev_iter_create failed");
+               return 0;
+       }
+
+       /* Otherwise do a complete scan */
+       for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
+               if ((data = read_disk(fmt, dev, mem, vg_name))) {
+                       _add_pv_to_list(head, data);
+               }
+       }
+       dev_iter_destroy(iter);
+
+       if (dm_list_empty(head))
+               return 0;
+
+       return 1;
+}
+
+static int _write_vgd(struct disk_list *data)
+{
+       struct vg_disk *vgd = &data->vgd;
+       uint64_t pos = data->pvd.vg_on_disk.base;
+
+       log_debug("Writing %s VG metadata to %s at %" PRIu64 " len %" PRIsize_t,
+                 data->pvd.vg_name, dev_name(data->dev), pos, sizeof(*vgd));
+
+       _xlate_vgd(vgd);
+       if (!dev_write(data->dev, pos, sizeof(*vgd), vgd))
+               return_0;
+
+       _xlate_vgd(vgd);
+
+       return 1;
+}
+
+static int _write_uuids(struct disk_list *data)
+{
+       struct uuid_list *ul;
+       uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
+       uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
+
+       dm_list_iterate_items(ul, &data->uuids) {
+               if (pos >= end) {
+                       log_error("Too many uuids to fit on %s",
+                                 dev_name(data->dev));
+                       return 0;
+               }
+
+               log_debug("Writing %s uuidlist to %s at %" PRIu64 " len %d",
+                         data->pvd.vg_name, dev_name(data->dev),
+                         pos, NAME_LEN);
+
+               if (!dev_write(data->dev, pos, NAME_LEN, ul->uuid))
+                       return_0;
+
+               pos += NAME_LEN;
+       }
+
+       return 1;
+}
+
+static int _write_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
+{
+       log_debug("Writing %s LV %s metadata to %s at %" PRIu64 " len %"
+                 PRIsize_t, disk->vg_name, disk->lv_name, dev_name(dev),
+                 pos, sizeof(*disk));
+
+       _xlate_lvd(disk);
+       if (!dev_write(dev, pos, sizeof(*disk), disk))
+               return_0;
+
+       _xlate_lvd(disk);
+
+       return 1;
+}
+
+static int _write_lvs(struct disk_list *data)
+{
+       struct lvd_list *ll;
+       uint64_t pos, offset;
+
+       pos = data->pvd.lv_on_disk.base;
+
+       if (!dev_set(data->dev, pos, data->pvd.lv_on_disk.size, 0)) {
+               log_error("Couldn't zero lv area on device '%s'",
+                         dev_name(data->dev));
+               return 0;
+       }
+
+       dm_list_iterate_items(ll, &data->lvds) {
+               offset = sizeof(struct lv_disk) * ll->lvd.lv_number;
+               if (offset + sizeof(struct lv_disk) > data->pvd.lv_on_disk.size) {
+                       log_error("lv_number %d too large", ll->lvd.lv_number);
+                       return 0;
+               }
+
+               if (!_write_lvd(data->dev, pos + offset, &ll->lvd))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _write_extents(struct disk_list *data)
+{
+       size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
+       struct pe_disk *extents = data->extents;
+       uint64_t pos = data->pvd.pe_on_disk.base;
+
+       log_debug("Writing %s extents metadata to %s at %" PRIu64 " len %"
+                 PRIsize_t, data->pvd.vg_name, dev_name(data->dev),
+                 pos, len);
+
+       _xlate_extents(extents, data->pvd.pe_total);
+       if (!dev_write(data->dev, pos, len, extents))
+               return_0;
+
+       _xlate_extents(extents, data->pvd.pe_total);
+
+       return 1;
+}
+
+static int _write_pvd(struct disk_list *data)
+{
+       char *buf;
+       uint64_t pos = data->pvd.pv_on_disk.base;
+       size_t size = data->pvd.pv_on_disk.size;
+
+       if (size < sizeof(struct pv_disk)) {
+               log_error("Invalid PV structure size.");
+               return 0;
+       }
+
+       /* Make sure that the gap between the PV structure and
+          the next one is zeroed in order to make non LVM tools
+          happy (idea from AED) */
+       buf = dm_zalloc(size);
+       if (!buf) {
+               log_error("Couldn't allocate temporary PV buffer.");
+               return 0;
+       }
+
+       memcpy(buf, &data->pvd, sizeof(struct pv_disk));
+
+       log_debug("Writing %s PV metadata to %s at %" PRIu64 " len %"
+                 PRIsize_t, data->pvd.vg_name, dev_name(data->dev),
+                 pos, size);
+
+       _xlate_pvd((struct pv_disk *) buf);
+       if (!dev_write(data->dev, pos, size, buf)) {
+               dm_free(buf);
+               return_0;
+       }
+
+       dm_free(buf);
+       return 1;
+}
+
+/*
+ * assumes the device has been opened.
+ */
+static int __write_all_pvd(const struct format_type *fmt __attribute__((unused)),
+                          struct disk_list *data)
+{
+       const char *pv_name = dev_name(data->dev);
+
+       if (!_write_pvd(data)) {
+               log_error("Failed to write PV structure onto %s", pv_name);
+               return 0;
+       }
+
+       /* vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, fmt); */
+       /*
+        * Stop here for orphan pv's.
+        */
+       if (data->pvd.vg_name[0] == '\0') {
+               /* if (!test_mode())
+                  vgcache_add(data->pvd.vg_name, NULL, data->dev, fmt); */
+               return 1;
+       }
+
+       /* if (!test_mode())
+          vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev,
+          fmt); */
+
+       if (!_write_vgd(data)) {
+               log_error("Failed to write VG data to %s", pv_name);
+               return 0;
+       }
+
+       if (!_write_uuids(data)) {
+               log_error("Failed to write PV uuid list to %s", pv_name);
+               return 0;
+       }
+
+       if (!_write_lvs(data)) {
+               log_error("Failed to write LV's to %s", pv_name);
+               return 0;
+       }
+
+       if (!_write_extents(data)) {
+               log_error("Failed to write extents to %s", pv_name);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * opens the device and hands to the above fn.
+ */
+static int _write_all_pvd(const struct format_type *fmt, struct disk_list *data)
+{
+       int r;
+
+       if (!dev_open(data->dev))
+               return_0;
+
+       r = __write_all_pvd(fmt, data);
+
+       if (!dev_close(data->dev))
+               stack;
+
+       return r;
+}
+
+/*
+ * Writes all the given pv's to disk.  Does very
+ * little sanity checking, so make sure correct
+ * data is passed to here.
+ */
+int write_disks(const struct format_type *fmt, struct dm_list *pvs)
+{
+       struct disk_list *dl;
+
+       dm_list_iterate_items(dl, pvs) {
+               if (!(_write_all_pvd(fmt, dl)))
+                       return_0;
+
+               log_very_verbose("Successfully wrote data to %s",
+                                dev_name(dl->dev));
+       }
+
+       return 1;
+}
diff --git a/lib/format1/disk-rep.h b/lib/format1/disk-rep.h
new file mode 100644 (file)
index 0000000..0d54438
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef DISK_REP_FORMAT1_H
+#define DISK_REP_FORMAT1_H
+
+#include "lvm-types.h"
+#include "metadata.h"
+#include "toolcontext.h"
+
+#define MAX_PV 256
+#define MAX_LV 256
+#define MAX_VG 99
+
+#define LVM_BLK_MAJOR 58
+
+#define MAX_PV_SIZE    ((uint32_t) -1) /* 2TB in sectors - 1 */
+#define MIN_PE_SIZE    (8192L >> SECTOR_SHIFT) /* 8 KB in sectors */
+#define MAX_PE_SIZE    (16L * 1024L * (1024L >> SECTOR_SHIFT) * 1024L)
+#define PE_SIZE_PV_SIZE_REL 5  /* PV size must be at least 5 times PE size */
+#define        MAX_LE_TOTAL    65534   /* 2^16 - 2 */
+#define        MAX_PE_TOTAL    ((uint32_t) -2)
+
+#define UNMAPPED_EXTENT 0
+
+/* volume group */
+#define        VG_ACTIVE            0x01       /* vg_status */
+#define        VG_EXPORTED          0x02       /*     "     */
+#define        VG_EXTENDABLE        0x04       /*     "     */
+
+#define        VG_READ              0x01       /* vg_access */
+#define        VG_WRITE             0x02       /*     "     */
+#define        VG_CLUSTERED         0x04       /*     "     */
+#define        VG_SHARED            0x08       /*     "     */
+
+/* logical volume */
+#define        LV_ACTIVE            0x01       /* lv_status */
+#define        LV_SPINDOWN          0x02       /*     "     */
+#define LV_PERSISTENT_MINOR  0x04      /*     "     */
+
+#define        LV_READ              0x01       /* lv_access */
+#define        LV_WRITE             0x02       /*     "     */
+#define        LV_SNAPSHOT          0x04       /*     "     */
+#define        LV_SNAPSHOT_ORG      0x08       /*     "     */
+
+#define        LV_BADBLOCK_ON       0x01       /* lv_badblock */
+
+#define        LV_STRICT            0x01       /* lv_allocation */
+#define        LV_CONTIGUOUS        0x02       /*       "       */
+
+/* physical volume */
+#define        PV_ACTIVE            0x01       /* pv_status */
+#define        PV_ALLOCATABLE       0x02       /* pv_allocatable */
+
+#define EXPORTED_TAG "PV_EXP"  /* Identifier for exported PV */
+#define IMPORTED_TAG "PV_IMP"  /* Identifier for imported PV */
+
+struct data_area {
+       uint32_t base;
+       uint32_t size;
+} __attribute__ ((packed));
+
+struct pv_disk {
+       int8_t id[2];
+       uint16_t version;       /* lvm version */
+       struct data_area pv_on_disk;
+       struct data_area vg_on_disk;
+       struct data_area pv_uuidlist_on_disk;
+       struct data_area lv_on_disk;
+       struct data_area pe_on_disk;
+       int8_t pv_uuid[NAME_LEN];
+       int8_t vg_name[NAME_LEN];
+       int8_t system_id[NAME_LEN];     /* for vgexport/vgimport */
+       uint32_t pv_major;
+       uint32_t pv_number;
+       uint32_t pv_status;
+       uint32_t pv_allocatable;
+       uint32_t pv_size;
+       uint32_t lv_cur;
+       uint32_t pe_size;
+       uint32_t pe_total;
+       uint32_t pe_allocated;
+
+       /* only present on version == 2 pv's */
+       uint32_t pe_start;
+} __attribute__ ((packed));
+
+struct lv_disk {
+       int8_t lv_name[NAME_LEN];
+       int8_t vg_name[NAME_LEN];
+       uint32_t lv_access;
+       uint32_t lv_status;
+       uint32_t lv_open;
+       uint32_t lv_dev;
+       uint32_t lv_number;
+       uint32_t lv_mirror_copies;      /* for future use */
+       uint32_t lv_recovery;   /*       "        */
+       uint32_t lv_schedule;   /*       "        */
+       uint32_t lv_size;
+       uint32_t lv_snapshot_minor;     /* minor number of original */
+       uint16_t lv_chunk_size; /* chunk size of snapshot */
+       uint16_t dummy;
+       uint32_t lv_allocated_le;
+       uint32_t lv_stripes;
+       uint32_t lv_stripesize;
+       uint32_t lv_badblock;   /* for future use */
+       uint32_t lv_allocation;
+       uint32_t lv_io_timeout; /* for future use */
+       uint32_t lv_read_ahead;
+} __attribute__ ((packed));
+
+struct vg_disk {
+       int8_t vg_uuid[ID_LEN]; /* volume group UUID */
+       int8_t vg_name_dummy[NAME_LEN - ID_LEN];        /* rest of v1 VG name */
+       uint32_t vg_number;     /* volume group number */
+       uint32_t vg_access;     /* read/write */
+       uint32_t vg_status;     /* active or not */
+       uint32_t lv_max;        /* maximum logical volumes */
+       uint32_t lv_cur;        /* current logical volumes */
+       uint32_t lv_open;       /* open logical volumes */
+       uint32_t pv_max;        /* maximum physical volumes */
+       uint32_t pv_cur;        /* current physical volumes FU */
+       uint32_t pv_act;        /* active physical volumes */
+       uint32_t dummy;
+       uint32_t vgda;          /* volume group descriptor arrays FU */
+       uint32_t pe_size;       /* physical extent size in sectors */
+       uint32_t pe_total;      /* total of physical extents */
+       uint32_t pe_allocated;  /* allocated physical extents */
+       uint32_t pvg_total;     /* physical volume groups FU */
+} __attribute__ ((packed));
+
+struct pe_disk {
+       uint16_t lv_num;
+       uint16_t le_num;
+} __attribute__ ((packed));
+
+struct uuid_list {
+       struct dm_list list;
+       char uuid[NAME_LEN] __attribute__((aligned(8)));
+};
+
+struct lvd_list {
+       struct dm_list list;
+       struct lv_disk lvd;
+};
+
+struct disk_list {
+       struct dm_list list;
+       struct dm_pool *mem;
+       struct device *dev;
+
+       struct pv_disk pvd __attribute__((aligned(8)));
+       struct vg_disk vgd __attribute__((aligned(8)));
+       struct dm_list uuids __attribute__((aligned(8)));
+       struct dm_list lvds __attribute__((aligned(8)));
+       struct pe_disk *extents __attribute__((aligned(8)));
+};
+
+/*
+ * Layout constants.
+ */
+#define METADATA_ALIGN 4096UL
+#define LVM1_PE_ALIGN (65536UL >> SECTOR_SHIFT)      /* PE alignment */
+
+#define        METADATA_BASE 0UL
+#define        PV_SIZE 1024UL
+#define        VG_SIZE 4096UL
+
+/*
+ * Functions to calculate layout info.
+ */
+int calculate_layout(struct disk_list *dl);
+int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
+                          uint32_t max_extent_count, uint64_t pe_start);
+
+/*
+ * Low level io routines which read/write
+ * disk_lists.
+ */
+
+struct disk_list *read_disk(const struct format_type *fmt, struct device *dev,
+                           struct dm_pool *mem, const char *vg_name);
+
+int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name,
+                  struct dev_filter *filter,
+                  struct dm_pool *mem, struct dm_list *results);
+
+int write_disks(const struct format_type *fmt, struct dm_list *pvds);
+
+/*
+ * Functions to translate to between disk and in
+ * core structures.
+ */
+int import_pv(const struct format_type *fmt, struct dm_pool *mem,
+             struct device *dev, struct volume_group *vg,
+             struct physical_volume *pv, struct pv_disk *pvd,
+             struct vg_disk *vgd);
+int export_pv(struct cmd_context *cmd, struct dm_pool *mem,
+             struct volume_group *vg,
+             struct pv_disk *pvd, struct physical_volume *pv);
+
+int import_vg(struct dm_pool *mem,
+             struct volume_group *vg, struct disk_list *dl);
+int export_vg(struct vg_disk *vgd, struct volume_group *vg);
+
+int import_lv(struct cmd_context *cmd, struct dm_pool *mem,
+             struct logical_volume *lv, struct lv_disk *lvd);
+
+int import_extents(struct cmd_context *cmd, struct volume_group *vg,
+                  struct dm_list *pvds);
+int export_extents(struct disk_list *dl, uint32_t lv_num,
+                  struct logical_volume *lv, struct physical_volume *pv);
+
+int import_pvs(const struct format_type *fmt, struct dm_pool *mem,
+              struct volume_group *vg, struct dm_list *pvds);
+
+int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds);
+int export_lvs(struct disk_list *dl, struct volume_group *vg,
+              struct physical_volume *pv, const char *dev_dir);
+
+int import_snapshots(struct dm_pool *mem, struct volume_group *vg,
+                    struct dm_list *pvds);
+
+int export_uuids(struct disk_list *dl, struct volume_group *vg);
+
+void export_numbers(struct dm_list *pvds, struct volume_group *vg);
+
+void export_pv_act(struct dm_list *pvds);
+int munge_pvd(struct device *dev, struct pv_disk *pvd);
+int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd);
+
+/* blech */
+int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter,
+                      const char *candidate_vg, int *result);
+int export_vg_number(struct format_instance *fid, struct dm_list *pvds,
+                    const char *vg_name, struct dev_filter *filter);
+
+#endif
diff --git a/lib/format1/format1.c b/lib/format1/format1.c
new file mode 100644 (file)
index 0000000..fc14444
--- /dev/null
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+#include "limits.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvm1-label.h"
+#include "format1.h"
+#include "segtype.h"
+#include "pv_alloc.h"
+
+/* VG consistency checks */
+static int _check_vgs(struct dm_list *pvs, struct volume_group *vg)
+{
+       struct dm_list *pvh, *t;
+       struct disk_list *dl = NULL;
+       struct disk_list *first = NULL;
+
+       uint32_t pv_count = 0;
+       uint32_t exported = 0;
+       int first_time = 1;
+
+       /*
+        * If there are exported and unexported PVs, ignore exported ones.
+        * This means an active VG won't be affected if disks are inserted
+        * bearing an exported VG with the same name.
+        */
+       dm_list_iterate_items(dl, pvs) {
+               if (first_time) {
+                       exported = dl->pvd.pv_status & VG_EXPORTED;
+                       first_time = 0;
+                       continue;
+               }
+
+               if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
+                       /* Remove exported PVs */
+                       dm_list_iterate_safe(pvh, t, pvs) {
+                               dl = dm_list_item(pvh, struct disk_list);
+                               if (dl->pvd.pv_status & VG_EXPORTED)
+                                       dm_list_del(pvh);
+                       }
+                       break;
+               }
+       }
+
+       /* Remove any PVs with VG structs that differ from the first */
+       dm_list_iterate_safe(pvh, t, pvs) {
+               dl = dm_list_item(pvh, struct disk_list);
+
+               if (!first)
+                       first = dl;
+
+               else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
+                       log_error("VG data differs between PVs %s and %s",
+                                 dev_name(first->dev), dev_name(dl->dev));
+                       log_debug("VG data on %s: %s %s %" PRIu32 " %" PRIu32
+                                 "  %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+                                 PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
+                                 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+                                 PRIu32 " %" PRIu32 " %" PRIu32,
+                                 dev_name(first->dev), first->vgd.vg_uuid,
+                                 first->vgd.vg_name_dummy,
+                                 first->vgd.vg_number, first->vgd.vg_access,
+                                 first->vgd.vg_status, first->vgd.lv_max,
+                                 first->vgd.lv_cur, first->vgd.lv_open,
+                                 first->vgd.pv_max, first->vgd.pv_cur,
+                                 first->vgd.pv_act, first->vgd.dummy,
+                                 first->vgd.vgda, first->vgd.pe_size,
+                                 first->vgd.pe_total, first->vgd.pe_allocated,
+                                 first->vgd.pvg_total);
+                       log_debug("VG data on %s: %s %s %" PRIu32 " %" PRIu32
+                                 "  %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+                                 PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
+                                 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+                                 PRIu32 " %" PRIu32 " %" PRIu32,
+                                 dev_name(dl->dev), dl->vgd.vg_uuid,
+                                 dl->vgd.vg_name_dummy, dl->vgd.vg_number,
+                                 dl->vgd.vg_access, dl->vgd.vg_status,
+                                 dl->vgd.lv_max, dl->vgd.lv_cur,
+                                 dl->vgd.lv_open, dl->vgd.pv_max,
+                                 dl->vgd.pv_cur, dl->vgd.pv_act, dl->vgd.dummy,
+                                 dl->vgd.vgda, dl->vgd.pe_size,
+                                 dl->vgd.pe_total, dl->vgd.pe_allocated,
+                                 dl->vgd.pvg_total);
+                       dm_list_del(pvh);
+                       return 0;
+               }
+               pv_count++;
+       }
+
+       /* On entry to fn, list known to be non-empty */
+       if (pv_count != first->vgd.pv_cur) {
+               log_error("%d PV(s) found for VG %s: expected %d",
+                         pv_count, first->pvd.vg_name, first->vgd.pv_cur);
+               vg->status |= PARTIAL_VG;
+       }
+
+       return 1;
+}
+
+static int _fix_partial_vg(struct volume_group *vg, struct dm_list *pvs)
+{
+       uint32_t extent_count = 0;
+       struct disk_list *dl;
+       struct dm_list *pvh;
+       struct pv_list *pvl;
+       struct lv_list *ll;
+       struct lv_segment *seg;
+
+       /*
+        * FIXME: code should remap missing segments to error segment.
+        * Also current mapping code allocates 1 segment per missing extent.
+        * For now bail out completely - allocated structures are not complete
+        */
+       dm_list_iterate_items(ll, &vg->lvs)
+               dm_list_iterate_items(seg, &ll->lv->segments) {
+
+                       /* area_count is always 1 here, s == 0 */
+                       if (seg_type(seg, 0) != AREA_PV)
+                               continue;
+
+                       if (seg_pv(seg, 0))
+                               continue;
+
+                       log_error("Partial mode support for missing lvm1 PVs and "
+                                 "partially available LVs is currently not implemented.");
+                       return 0;
+       }
+
+       dm_list_iterate(pvh, pvs) {
+               dl = dm_list_item(pvh, struct disk_list);
+               extent_count += dl->pvd.pe_total;
+       }
+
+       /* FIXME: move this to one place to pv_manip */
+       if (!(pvl = dm_pool_zalloc(vg->vgmem, sizeof(*pvl))) ||
+           !(pvl->pv = dm_pool_zalloc(vg->vgmem, sizeof(*pvl->pv))))
+               return_0;
+
+       /* Use vg uuid with replaced first chars to "missing" as missing PV UUID */
+       memcpy(&pvl->pv->id.uuid, vg->id.uuid, sizeof(pvl->pv->id.uuid));
+       memcpy(&pvl->pv->id.uuid, "missing", 7);
+
+       if (!(pvl->pv->vg_name = dm_pool_strdup(vg->vgmem, vg->name)))
+               goto_out;
+       memcpy(&pvl->pv->vgid, &vg->id, sizeof(vg->id));
+       pvl->pv->status |= MISSING_PV;
+       dm_list_init(&pvl->pv->tags);
+       dm_list_init(&pvl->pv->segments);
+
+       pvl->pv->pe_size = vg->extent_size;
+       pvl->pv->pe_count = vg->extent_count - extent_count;
+       if (!alloc_pv_segment_whole_pv(vg->vgmem, pvl->pv))
+               goto_out;
+
+       add_pvl_to_vgs(vg, pvl);
+       log_debug("%s: partial VG, allocated missing PV using %d extents.",
+                 vg->name, pvl->pv->pe_count);
+
+       return 1;
+out:
+       dm_pool_free(vg->vgmem, pvl);
+       return 0;
+}
+
+static struct volume_group *_build_vg(struct format_instance *fid,
+                                     struct dm_list *pvs,
+                                     struct dm_pool *mem)
+{
+       struct volume_group *vg = dm_pool_zalloc(mem, sizeof(*vg));
+       struct disk_list *dl;
+
+       if (!vg)
+               goto_bad;
+
+       if (dm_list_empty(pvs))
+               goto_bad;
+
+       vg->cmd = fid->fmt->cmd;
+       vg->vgmem = mem;
+       vg->fid = fid;
+       vg->seqno = 0;
+       dm_list_init(&vg->pvs);
+       dm_list_init(&vg->lvs);
+       dm_list_init(&vg->tags);
+       dm_list_init(&vg->removed_pvs);
+
+       if (!_check_vgs(pvs, vg))
+               goto_bad;
+
+       dl = dm_list_item(pvs->n, struct disk_list);
+
+       if (!import_vg(mem, vg, dl))
+               goto_bad;
+
+       if (!import_pvs(fid->fmt, mem, vg, pvs))
+               goto_bad;
+
+       if (!import_lvs(mem, vg, pvs))
+               goto_bad;
+
+       if (!import_extents(fid->fmt->cmd, vg, pvs))
+               goto_bad;
+
+       if (!import_snapshots(mem, vg, pvs))
+               goto_bad;
+
+       /* Fix extents counts by adding missing PV if partial VG */
+       if ((vg->status & PARTIAL_VG) && !_fix_partial_vg(vg, pvs))
+               goto_bad;
+
+       return vg;
+
+      bad:
+       dm_pool_free(mem, vg);
+       return NULL;
+}
+
+static struct volume_group *_format1_vg_read(struct format_instance *fid,
+                                    const char *vg_name,
+                                    struct metadata_area *mda __attribute__((unused)))
+{
+       struct dm_pool *mem = dm_pool_create("lvm1 vg_read", VG_MEMPOOL_CHUNK);
+       struct dm_list pvs;
+       struct volume_group *vg = NULL;
+       dm_list_init(&pvs);
+
+       if (!mem)
+               return_NULL;
+
+       /* Strip dev_dir if present */
+       vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir);
+
+       if (!read_pvs_in_vg
+           (fid->fmt, vg_name, fid->fmt->cmd->filter, mem, &pvs))
+               goto_bad;
+
+       if (!(vg = _build_vg(fid, &pvs, mem)))
+               goto_bad;
+
+       return vg;
+bad:
+       dm_pool_destroy(mem);
+       return NULL;
+}
+
+static struct disk_list *_flatten_pv(struct format_instance *fid,
+                                    struct dm_pool *mem, struct volume_group *vg,
+                                    struct physical_volume *pv,
+                                    const char *dev_dir)
+{
+       struct disk_list *dl = dm_pool_alloc(mem, sizeof(*dl));
+
+       if (!dl)
+               return_NULL;
+
+       dl->mem = mem;
+       dl->dev = pv->dev;
+
+       dm_list_init(&dl->uuids);
+       dm_list_init(&dl->lvds);
+
+       if (!export_pv(fid->fmt->cmd, mem, vg, &dl->pvd, pv) ||
+           !export_vg(&dl->vgd, vg) ||
+           !export_uuids(dl, vg) ||
+           !export_lvs(dl, vg, pv, dev_dir) || !calculate_layout(dl)) {
+               dm_pool_free(mem, dl);
+               return_NULL;
+       }
+
+       return dl;
+}
+
+static int _flatten_vg(struct format_instance *fid, struct dm_pool *mem,
+                      struct volume_group *vg,
+                      struct dm_list *pvds, const char *dev_dir,
+                      struct dev_filter *filter)
+{
+       struct pv_list *pvl;
+       struct disk_list *data;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (!(data = _flatten_pv(fid, mem, vg, pvl->pv, dev_dir)))
+                       return_0;
+
+               dm_list_add(pvds, &data->list);
+       }
+
+       export_numbers(pvds, vg);
+       export_pv_act(pvds);
+
+       if (!export_vg_number(fid, pvds, vg->name, filter))
+               return_0;
+
+       return 1;
+}
+
+static int _format1_vg_write(struct format_instance *fid, struct volume_group *vg,
+                    struct metadata_area *mda __attribute__((unused)))
+{
+       struct dm_pool *mem = dm_pool_create("lvm1 vg_write", VG_MEMPOOL_CHUNK);
+       struct dm_list pvds;
+       int r = 0;
+
+       if (!mem)
+               return_0;
+
+       dm_list_init(&pvds);
+
+       r = (_flatten_vg(fid, mem, vg, &pvds, fid->fmt->cmd->dev_dir,
+                        fid->fmt->cmd->filter) &&
+            write_disks(fid->fmt, &pvds));
+
+       lvmcache_update_vg(vg, 0);
+       dm_pool_destroy(mem);
+       return r;
+}
+
+static int _format1_pv_read(const struct format_type *fmt, const char *pv_name,
+                   struct physical_volume *pv, struct dm_list *mdas __attribute__((unused)),
+                   int scan_label_only __attribute__((unused)))
+{
+       struct dm_pool *mem = dm_pool_create("lvm1 pv_read", 1024);
+       struct disk_list *dl;
+       struct device *dev;
+       int r = 0;
+
+       log_very_verbose("Reading physical volume data %s from disk", pv_name);
+
+       if (!mem)
+               return_0;
+
+       if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
+               goto_out;
+
+       if (!(dl = read_disk(fmt, dev, mem, NULL)))
+               goto_out;
+
+       if (!import_pv(fmt, fmt->cmd->mem, dl->dev, NULL, pv, &dl->pvd, &dl->vgd))
+               goto_out;
+
+       pv->fmt = fmt;
+
+       r = 1;
+
+      out:
+       dm_pool_destroy(mem);
+       return r;
+}
+
+static int _format1_pv_setup(const struct format_type *fmt,
+                            uint64_t pe_start, uint32_t extent_count,
+                            uint32_t extent_size,
+                            unsigned long data_alignment __attribute__((unused)),
+                            unsigned long data_alignment_offset __attribute__((unused)),
+                            int pvmetadatacopies __attribute__((unused)),
+                            uint64_t pvmetadatasize __attribute__((unused)),
+                            unsigned metadataignore __attribute__((unused)),
+                            struct dm_list *mdas __attribute__((unused)),
+                            struct physical_volume *pv, struct volume_group *vg __attribute__((unused)))
+{
+       if (pv->size > MAX_PV_SIZE)
+               pv->size--;
+       if (pv->size > MAX_PV_SIZE) {
+               log_error("Physical volumes cannot be bigger than %s",
+                         display_size(fmt->cmd, (uint64_t) MAX_PV_SIZE));
+               return 0;
+       }
+
+       /* Nothing more to do if extent size isn't provided */
+       if (!extent_size)
+               return 1;
+
+       /*
+        * This works out pe_start and pe_count.
+        */
+       if (!calculate_extent_count(pv, extent_size, extent_count, pe_start))
+               return_0;
+
+       /* Retain existing extent locations exactly */
+       if (((pe_start || extent_count) && (pe_start != pv->pe_start)) ||
+           (extent_count && (extent_count != pv->pe_count))) {
+               log_error("Metadata would overwrite physical extents");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _format1_lv_setup(struct format_instance *fid, struct logical_volume *lv)
+{
+       uint64_t max_size = UINT_MAX;
+
+       if (!*lv->lvid.s)
+               lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv));
+
+       if (lv->le_count > MAX_LE_TOTAL) {
+               log_error("logical volumes cannot contain more than "
+                         "%d extents.", MAX_LE_TOTAL);
+               return 0;
+       }
+       if (lv->size > max_size) {
+               log_error("logical volumes cannot be larger than %s",
+                         display_size(fid->fmt->cmd, max_size));
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _format1_pv_write(const struct format_type *fmt, struct physical_volume *pv,
+                    struct dm_list *mdas __attribute__((unused)), int64_t sector __attribute__((unused)))
+{
+       struct dm_pool *mem;
+       struct disk_list *dl;
+       struct dm_list pvs;
+       struct lvmcache_info *info;
+
+       if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
+                                 pv->vg_name, NULL, 0)))
+               return_0;
+
+       info->device_size = pv->size << SECTOR_SHIFT;
+       info->fmt = fmt;
+
+       dm_list_init(&info->mdas);
+
+       dm_list_init(&pvs);
+
+       /* Ensure any residual PE structure is gone */
+       pv->pe_size = pv->pe_count = 0;
+       pv->pe_start = LVM1_PE_ALIGN;
+
+       if (!(mem = dm_pool_create("lvm1 pv_write", 1024)))
+               return_0;
+
+       if (!(dl = dm_pool_alloc(mem, sizeof(*dl))))
+               goto_bad;
+
+       dl->mem = mem;
+       dl->dev = pv->dev;
+
+       if (!export_pv(fmt->cmd, mem, NULL, &dl->pvd, pv))
+               goto_bad;
+
+       /* must be set to be able to zero gap after PV structure in
+          dev_write in order to make other disk tools happy */
+       dl->pvd.pv_on_disk.base = METADATA_BASE;
+       dl->pvd.pv_on_disk.size = PV_SIZE;
+       dl->pvd.pe_on_disk.base = LVM1_PE_ALIGN << SECTOR_SHIFT;
+
+       dm_list_add(&pvs, &dl->list);
+       if (!write_disks(fmt, &pvs))
+               goto_bad;
+
+       dm_pool_destroy(mem);
+       return 1;
+
+      bad:
+       dm_pool_destroy(mem);
+       return 0;
+}
+
+static int _format1_vg_setup(struct format_instance *fid, struct volume_group *vg)
+{
+       /* just check max_pv and max_lv */
+       if (!vg->max_lv || vg->max_lv >= MAX_LV)
+               vg->max_lv = MAX_LV - 1;
+
+       if (!vg->max_pv || vg->max_pv >= MAX_PV)
+               vg->max_pv = MAX_PV - 1;
+
+       if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) {
+               log_error("Extent size must be between %s and %s",
+                         display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE),
+                         display_size(fid->fmt->cmd, (uint64_t) MAX_PE_SIZE));
+
+               return 0;
+       }
+
+       if (vg->extent_size % MIN_PE_SIZE) {
+               log_error("Extent size must be multiple of %s",
+                         display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE));
+               return 0;
+       }
+
+       /* Redundant? */
+       if (vg->extent_size & (vg->extent_size - 1)) {
+               log_error("Extent size must be power of 2");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _format1_segtype_supported(struct format_instance *fid __attribute__((unused)),
+                                     const struct segment_type *segtype)
+{
+       if (!(segtype->flags & SEG_FORMAT1_SUPPORT))
+               return_0;
+
+       return 1;
+}
+
+static struct metadata_area_ops _metadata_format1_ops = {
+       .vg_read = _format1_vg_read,
+       .vg_write = _format1_vg_write,
+};
+
+static struct format_instance *_format1_create_instance(const struct format_type *fmt,
+                                               const char *vgname __attribute__((unused)),
+                                               const char *vgid __attribute__((unused)),
+                                               void *private __attribute__((unused)))
+{
+       struct format_instance *fid;
+       struct metadata_area *mda;
+
+       if (!(fid = dm_pool_alloc(fmt->cmd->mem, sizeof(*fid))))
+               return_NULL;
+
+       fid->fmt = fmt;
+       dm_list_init(&fid->metadata_areas_in_use);
+       dm_list_init(&fid->metadata_areas_ignored);
+
+       /* Define a NULL metadata area */
+       if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda)))) {
+               dm_pool_free(fmt->cmd->mem, fid);
+               return_NULL;
+       }
+
+       mda->ops = &_metadata_format1_ops;
+       mda->metadata_locn = NULL;
+       mda->status = 0;
+       dm_list_add(&fid->metadata_areas_in_use, &mda->list);
+
+       return fid;
+}
+
+static void _format1_destroy_instance(struct format_instance *fid __attribute__((unused)))
+{
+}
+
+static void _format1_destroy(struct format_type *fmt)
+{
+       dm_free(fmt);
+}
+
+static struct format_handler _format1_ops = {
+       .pv_read = _format1_pv_read,
+       .pv_setup = _format1_pv_setup,
+       .pv_write = _format1_pv_write,
+       .lv_setup = _format1_lv_setup,
+       .vg_setup = _format1_vg_setup,
+       .segtype_supported = _format1_segtype_supported,
+       .create_instance = _format1_create_instance,
+       .destroy_instance = _format1_destroy_instance,
+       .destroy = _format1_destroy,
+};
+
+#ifdef LVM1_INTERNAL
+struct format_type *init_lvm1_format(struct cmd_context *cmd)
+#else                          /* Shared */
+struct format_type *init_format(struct cmd_context *cmd);
+struct format_type *init_format(struct cmd_context *cmd)
+#endif
+{
+       struct format_type *fmt = dm_malloc(sizeof(*fmt));
+
+       if (!fmt)
+               return_NULL;
+
+       fmt->cmd = cmd;
+       fmt->ops = &_format1_ops;
+       fmt->name = FMT_LVM1_NAME;
+       fmt->alias = NULL;
+       fmt->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME;
+       fmt->features = FMT_RESTRICTED_LVIDS | FMT_ORPHAN_ALLOCATABLE |
+                       FMT_RESTRICTED_READAHEAD;
+       fmt->private = NULL;
+
+       if (!(fmt->labeller = lvm1_labeller_create(fmt))) {
+               log_error("Couldn't create lvm1 label handler.");
+               return NULL;
+       }
+
+       if (!(label_register_handler(FMT_LVM1_NAME, fmt->labeller))) {
+               log_error("Couldn't register lvm1 label handler.");
+               return NULL;
+       }
+
+       log_very_verbose("Initialised format: %s", fmt->name);
+
+       return fmt;
+}
diff --git a/lib/format1/format1.h b/lib/format1/format1.h
new file mode 100644 (file)
index 0000000..c76ba62
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FORMAT1_H
+#define _LVM_FORMAT1_H
+
+#include "metadata.h"
+#include "lvmcache.h"
+
+#define FMT_LVM1_NAME "lvm1"
+#define FMT_LVM1_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_LVM1_NAME)
+
+#ifdef LVM1_INTERNAL
+struct format_type *init_lvm1_format(struct cmd_context *cmd);
+#endif
+
+#endif
diff --git a/lib/format1/import-export.c b/lib/format1/import-export.c
new file mode 100644 (file)
index 0000000..d0b1b31
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Translates between disk and in-core formats.
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+#include "lvm-string.h"
+#include "filter.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "pv_alloc.h"
+#include "display.h"
+#include "lvmcache.h"
+#include "metadata.h"
+
+#include <time.h>
+
+static int _check_vg_name(const char *name)
+{
+       return strlen(name) < NAME_LEN;
+}
+
+/*
+ * Extracts the last part of a path.
+ */
+static char *_create_lv_name(struct dm_pool *mem, const char *full_name)
+{
+       const char *ptr = strrchr(full_name, '/');
+
+       if (!ptr)
+               ptr = full_name;
+       else
+               ptr++;
+
+       return dm_pool_strdup(mem, ptr);
+}
+
+int import_pv(const struct format_type *fmt, struct dm_pool *mem,
+             struct device *dev, struct volume_group *vg,
+             struct physical_volume *pv, struct pv_disk *pvd,
+             struct vg_disk *vgd)
+{
+       uint64_t size;
+
+       memset(pv, 0, sizeof(*pv));
+       memcpy(&pv->id, pvd->pv_uuid, ID_LEN);
+
+       pv->dev = dev;
+       if (!*pvd->vg_name)
+               pv->vg_name = fmt->orphan_vg_name;
+       else if (!(pv->vg_name = dm_pool_strdup(mem, (char *)pvd->vg_name))) {
+               log_error("Volume Group name allocation failed.");
+               return 0;
+       }
+
+       memcpy(&pv->vgid, vgd->vg_uuid, sizeof(vg->id));
+
+       /* Store system_id from first PV if PV belongs to a VG */
+       if (vg && !*vg->system_id)
+               strncpy(vg->system_id, (char *)pvd->system_id, NAME_LEN);
+
+       if (vg &&
+           strncmp(vg->system_id, (char *)pvd->system_id, sizeof(pvd->system_id)))
+                   log_very_verbose("System ID %s on %s differs from %s for "
+                                    "volume group", pvd->system_id,
+                                    pv_dev_name(pv), vg->system_id);
+
+       /*
+        * If exported, we still need to flag in pv->status too because
+        * we don't always have a struct volume_group when we need this.
+        */
+       if (pvd->pv_status & VG_EXPORTED)
+               pv->status |= EXPORTED_VG;
+
+       if (pvd->pv_allocatable)
+               pv->status |= ALLOCATABLE_PV;
+
+       pv->size = pvd->pv_size;
+       pv->pe_size = pvd->pe_size;
+       pv->pe_start = pvd->pe_start;
+       pv->pe_count = pvd->pe_total;
+       pv->pe_alloc_count = 0;
+       pv->pe_align = 0;
+
+       /* Fix up pv size if missing or impossibly large */
+       if (!pv->size || pv->size > (1ULL << 62)) {
+               if (!dev_get_size(dev, &pv->size)) {
+                       log_error("%s: Couldn't get size.", pv_dev_name(pv));
+                       return 0;
+               }
+               log_verbose("Fixing up missing format1 size (%s) "
+                           "for PV %s", display_size(fmt->cmd, pv->size),
+                           pv_dev_name(pv));
+               if (vg) {
+                       size = pv->pe_count * (uint64_t) vg->extent_size +
+                              pv->pe_start;
+                       if (size > pv->size)
+                               log_warn("WARNING: Physical Volume %s is too "
+                                        "large for underlying device",
+                                        pv_dev_name(pv));
+               }
+       }
+
+       dm_list_init(&pv->tags);
+       dm_list_init(&pv->segments);
+
+       if (!alloc_pv_segment_whole_pv(mem, pv))
+               return_0;
+
+       return 1;
+}
+
+static int _system_id(struct cmd_context *cmd, char *s, const char *prefix)
+{
+
+       if (dm_snprintf(s, NAME_LEN, "%s%s%lu",
+                        prefix, cmd->hostname, time(NULL)) < 0) {
+               log_error("Generated system_id too long");
+               return 0;
+       }
+
+       return 1;
+}
+
+int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused)),
+             struct volume_group *vg,
+             struct pv_disk *pvd, struct physical_volume *pv)
+{
+       memset(pvd, 0, sizeof(*pvd));
+
+       pvd->id[0] = 'H';
+       pvd->id[1] = 'M';
+       pvd->version = 1;
+
+       memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN);
+
+       if (pv->vg_name && !is_orphan(pv)) {
+               if (!_check_vg_name(pv->vg_name))
+                       return_0;
+               strncpy((char *)pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
+       }
+
+       /* Preserve existing system_id if it exists */
+       if (vg && *vg->system_id)
+               strncpy((char *)pvd->system_id, vg->system_id, sizeof(pvd->system_id));
+
+       /* Is VG already exported or being exported? */
+       if (vg && vg_is_exported(vg)) {
+               /* Does system_id need setting? */
+               if (!*vg->system_id ||
+                   strncmp(vg->system_id, EXPORTED_TAG,
+                           sizeof(EXPORTED_TAG) - 1)) {
+                       if (!_system_id(cmd, (char *)pvd->system_id, EXPORTED_TAG))
+                               return_0;
+               }
+               if (strlen((char *)pvd->vg_name) + sizeof(EXPORTED_TAG) >
+                   sizeof(pvd->vg_name)) {
+                       log_error("Volume group name %s too long to export",
+                                 pvd->vg_name);
+                       return 0;
+               }
+               strcat((char *)pvd->vg_name, EXPORTED_TAG);
+       }
+
+       /* Is VG being imported? */
+       if (vg && !vg_is_exported(vg) && *vg->system_id &&
+           !strncmp(vg->system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
+               if (!_system_id(cmd, (char *)pvd->system_id, IMPORTED_TAG))
+                       return_0;
+       }
+
+       /* Generate system_id if PV is in VG */
+       if (!pvd->system_id[0])
+               if (!_system_id(cmd, (char *)pvd->system_id, ""))
+                       return_0;
+
+       /* Update internal system_id if we changed it */
+       if (vg &&
+           (!*vg->system_id ||
+            strncmp(vg->system_id, (char *)pvd->system_id, sizeof(pvd->system_id))))
+                   strncpy(vg->system_id, (char *)pvd->system_id, NAME_LEN);
+
+       //pvd->pv_major = MAJOR(pv->dev);
+
+       if (pv->status & ALLOCATABLE_PV)
+               pvd->pv_allocatable = PV_ALLOCATABLE;
+
+       pvd->pv_size = pv->size;
+       pvd->lv_cur = 0;        /* this is set when exporting the lv list */
+       if (vg)
+               pvd->pe_size = vg->extent_size;
+       else
+               pvd->pe_size = pv->pe_size;
+       pvd->pe_total = pv->pe_count;
+       pvd->pe_allocated = pv->pe_alloc_count;
+       pvd->pe_start = pv->pe_start;
+
+       return 1;
+}
+
+int import_vg(struct dm_pool *mem,
+             struct volume_group *vg, struct disk_list *dl)
+{
+       struct vg_disk *vgd = &dl->vgd;
+       memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
+
+       if (!_check_vg_name((char *)dl->pvd.vg_name))
+               return_0;
+
+       if (!(vg->name = dm_pool_strdup(mem, (char *)dl->pvd.vg_name)))
+               return_0;
+
+       if (!(vg->system_id = dm_pool_alloc(mem, NAME_LEN)))
+               return_0;
+
+       *vg->system_id = '\0';
+
+       if (vgd->vg_status & VG_EXPORTED)
+               vg->status |= EXPORTED_VG;
+
+       if (vgd->vg_status & VG_EXTENDABLE)
+               vg->status |= RESIZEABLE_VG;
+
+       if (vgd->vg_access & VG_READ)
+               vg->status |= LVM_READ;
+
+       if (vgd->vg_access & VG_WRITE)
+               vg->status |= LVM_WRITE;
+
+       if (vgd->vg_access & VG_CLUSTERED)
+               vg->status |= CLUSTERED;
+
+       if (vgd->vg_access & VG_SHARED)
+               vg->status |= SHARED;
+
+       vg->extent_size = vgd->pe_size;
+       vg->extent_count = vgd->pe_total;
+       vg->free_count = vgd->pe_total;
+       vg->max_lv = vgd->lv_max;
+       vg->max_pv = vgd->pv_max;
+       vg->alloc = ALLOC_NORMAL;
+
+       return 1;
+}
+
+int export_vg(struct vg_disk *vgd, struct volume_group *vg)
+{
+       memset(vgd, 0, sizeof(*vgd));
+       memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN);
+
+       if (vg->status & LVM_READ)
+               vgd->vg_access |= VG_READ;
+
+       if (vg->status & LVM_WRITE)
+               vgd->vg_access |= VG_WRITE;
+
+       if (vg_is_clustered(vg))
+               vgd->vg_access |= VG_CLUSTERED;
+
+       if (vg->status & SHARED)
+               vgd->vg_access |= VG_SHARED;
+
+       if (vg_is_exported(vg))
+               vgd->vg_status |= VG_EXPORTED;
+
+       if (vg_is_resizeable(vg))
+               vgd->vg_status |= VG_EXTENDABLE;
+
+       vgd->lv_max = vg->max_lv;
+       vgd->lv_cur = vg_visible_lvs(vg) + snapshot_count(vg);
+
+       vgd->pv_max = vg->max_pv;
+       vgd->pv_cur = vg->pv_count;
+
+       vgd->pe_size = vg->extent_size;
+       vgd->pe_total = vg->extent_count;
+       vgd->pe_allocated = vg->extent_count - vg->free_count;
+
+       return 1;
+}
+
+int import_lv(struct cmd_context *cmd, struct dm_pool *mem,
+             struct logical_volume *lv, struct lv_disk *lvd)
+{
+       if (!(lv->name = _create_lv_name(mem, (char *)lvd->lv_name)))
+               return_0;
+
+       lv->status |= VISIBLE_LV;
+
+       if (lvd->lv_status & LV_SPINDOWN)
+               lv->status |= SPINDOWN_LV;
+
+       if (lvd->lv_status & LV_PERSISTENT_MINOR) {
+               lv->status |= FIXED_MINOR;
+               lv->minor = MINOR(lvd->lv_dev);
+               lv->major = MAJOR(lvd->lv_dev);
+       } else {
+               lv->major = -1;
+               lv->minor = -1;
+       }
+
+       if (lvd->lv_access & LV_READ)
+               lv->status |= LVM_READ;
+
+       if (lvd->lv_access & LV_WRITE)
+               lv->status |= LVM_WRITE;
+
+       if (lvd->lv_badblock)
+               lv->status |= BADBLOCK_ON;
+
+       /* Drop the unused LV_STRICT here */
+       if (lvd->lv_allocation & LV_CONTIGUOUS)
+               lv->alloc = ALLOC_CONTIGUOUS;
+       else
+               lv->alloc = ALLOC_NORMAL;
+
+       if (!lvd->lv_read_ahead)
+               lv->read_ahead = cmd->default_settings.read_ahead;
+       else
+               lv->read_ahead = lvd->lv_read_ahead;
+
+       lv->size = lvd->lv_size;
+       lv->le_count = lvd->lv_allocated_le;
+
+       return 1;
+}
+
+static void _export_lv(struct lv_disk *lvd, struct volume_group *vg,
+                      struct logical_volume *lv, const char *dev_dir)
+{
+       memset(lvd, 0, sizeof(*lvd));
+       snprintf((char *)lvd->lv_name, sizeof(lvd->lv_name), "%s%s/%s",
+                dev_dir, vg->name, lv->name);
+
+       strcpy((char *)lvd->vg_name, vg->name);
+
+       if (lv->status & LVM_READ)
+               lvd->lv_access |= LV_READ;
+
+       if (lv->status & LVM_WRITE)
+               lvd->lv_access |= LV_WRITE;
+
+       if (lv->status & SPINDOWN_LV)
+               lvd->lv_status |= LV_SPINDOWN;
+
+       if (lv->status & FIXED_MINOR) {
+               lvd->lv_status |= LV_PERSISTENT_MINOR;
+               lvd->lv_dev = MKDEV(lv->major, lv->minor);
+       } else {
+               lvd->lv_dev = MKDEV(LVM_BLK_MAJOR, lvnum_from_lvid(&lv->lvid));
+       }
+
+       if (lv->read_ahead == DM_READ_AHEAD_AUTO ||
+           lv->read_ahead == DM_READ_AHEAD_NONE)
+               lvd->lv_read_ahead = 0;
+       else
+               lvd->lv_read_ahead = lv->read_ahead;
+
+       lvd->lv_stripes =
+           dm_list_item(lv->segments.n, struct lv_segment)->area_count;
+       lvd->lv_stripesize =
+           dm_list_item(lv->segments.n, struct lv_segment)->stripe_size;
+
+       lvd->lv_size = lv->size;
+       lvd->lv_allocated_le = lv->le_count;
+
+       if (lv->status & BADBLOCK_ON)
+               lvd->lv_badblock = LV_BADBLOCK_ON;
+
+       if (lv->alloc == ALLOC_CONTIGUOUS)
+               lvd->lv_allocation |= LV_CONTIGUOUS;
+}
+
+int export_extents(struct disk_list *dl, uint32_t lv_num,
+                  struct logical_volume *lv, struct physical_volume *pv)
+{
+       struct pe_disk *ped;
+       struct lv_segment *seg;
+       uint32_t pe, s;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               for (s = 0; s < seg->area_count; s++) {
+                       if (!(seg->segtype->flags & SEG_FORMAT1_SUPPORT)) {
+                               log_error("Segment type %s in LV %s: "
+                                         "unsupported by format1",
+                                         seg->segtype->name, lv->name);
+                               return 0;
+                       }
+                       if (seg_type(seg, s) != AREA_PV) {
+                               log_error("Non-PV stripe found in LV %s: "
+                                         "unsupported by format1", lv->name);
+                               return 0;
+                       }
+                       if (seg_pv(seg, s) != pv)
+                               continue;       /* not our pv */
+
+                       for (pe = 0; pe < (seg->len / seg->area_count); pe++) {
+                               ped = &dl->extents[pe + seg_pe(seg, s)];
+                               ped->lv_num = lv_num;
+                               ped->le_num = (seg->le / seg->area_count) + pe +
+                                   s * (lv->le_count / seg->area_count);
+                       }
+               }
+       }
+
+       return 1;
+}
+
+int import_pvs(const struct format_type *fmt, struct dm_pool *mem,
+              struct volume_group *vg, struct dm_list *pvds)
+{
+       struct disk_list *dl;
+       struct pv_list *pvl;
+
+       vg->pv_count = 0;
+       dm_list_iterate_items(dl, pvds) {
+               if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
+                   !(pvl->pv = dm_pool_alloc(mem, sizeof(*pvl->pv))))
+                       return_0;
+
+               if (!import_pv(fmt, mem, dl->dev, vg, pvl->pv, &dl->pvd, &dl->vgd))
+                       return_0;
+
+               pvl->pv->fmt = fmt;
+               add_pvl_to_vgs(vg, pvl);
+       }
+
+       return 1;
+}
+
+static struct logical_volume *_add_lv(struct dm_pool *mem,
+                                     struct volume_group *vg,
+                                     struct lv_disk *lvd)
+{
+       struct logical_volume *lv;
+
+       if (!(lv = alloc_lv(mem)))
+               return_NULL;
+
+       lvid_from_lvnum(&lv->lvid, &vg->id, lvd->lv_number);
+
+       if (!import_lv(vg->cmd, mem, lv, lvd)) 
+               goto_bad;
+
+       if (!link_lv_to_vg(vg, lv))
+               goto_bad;
+
+       return lv;
+bad:
+       dm_pool_free(mem, lv);
+       return NULL;
+}
+
+int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds)
+{
+       struct disk_list *dl;
+       struct lvd_list *ll;
+       struct lv_disk *lvd;
+
+       dm_list_iterate_items(dl, pvds) {
+               dm_list_iterate_items(ll, &dl->lvds) {
+                       lvd = &ll->lvd;
+
+                       if (!find_lv(vg, (char *)lvd->lv_name) &&
+                           !_add_lv(mem, vg, lvd))
+                               return_0;
+               }
+       }
+
+       return 1;
+}
+
+/* FIXME: tidy */
+int export_lvs(struct disk_list *dl, struct volume_group *vg,
+              struct physical_volume *pv, const char *dev_dir)
+{
+       int r = 0;
+       struct lv_list *ll;
+       struct lvd_list *lvdl;
+       size_t len;
+       uint32_t lv_num;
+       struct dm_hash_table *lvd_hash;
+
+       if (!_check_vg_name(vg->name))
+               return_0;
+
+       if (!(lvd_hash = dm_hash_create(32)))
+               return_0;
+
+       /*
+        * setup the pv's extents array
+        */
+       len = sizeof(struct pe_disk) * dl->pvd.pe_total;
+       if (!(dl->extents = dm_pool_zalloc(dl->mem, len)))
+               goto_out;
+
+       dm_list_iterate_items(ll, &vg->lvs) {
+               if (ll->lv->status & SNAPSHOT)
+                       continue;
+
+               if (!(lvdl = dm_pool_alloc(dl->mem, sizeof(*lvdl))))
+                       goto_out;
+
+               _export_lv(&lvdl->lvd, vg, ll->lv, dev_dir);
+
+               lv_num = lvnum_from_lvid(&ll->lv->lvid);
+               lvdl->lvd.lv_number = lv_num;
+
+               if (!dm_hash_insert(lvd_hash, ll->lv->name, &lvdl->lvd))
+                       goto_out;
+
+               if (!export_extents(dl, lv_num + 1, ll->lv, pv))
+                       goto_out;
+
+               if (lv_is_origin(ll->lv))
+                       lvdl->lvd.lv_access |= LV_SNAPSHOT_ORG;
+
+               if (lv_is_cow(ll->lv)) {
+                       lvdl->lvd.lv_access |= LV_SNAPSHOT;
+                       lvdl->lvd.lv_chunk_size = ll->lv->snapshot->chunk_size;
+                       lvdl->lvd.lv_snapshot_minor =
+                           lvnum_from_lvid(&ll->lv->snapshot->origin->lvid);
+               }
+
+               dm_list_add(&dl->lvds, &lvdl->list);
+               dl->pvd.lv_cur++;
+       }
+
+       r = 1;
+
+      out:
+       dm_hash_destroy(lvd_hash);
+       return r;
+}
+
+/*
+ * FIXME: More inefficient code.
+ */
+int import_snapshots(struct dm_pool *mem __attribute__((unused)), struct volume_group *vg,
+                    struct dm_list *pvds)
+{
+       struct logical_volume *lvs[MAX_LV];
+       struct disk_list *dl;
+       struct lvd_list *ll;
+       struct lv_disk *lvd;
+       int lvnum;
+       struct logical_volume *org, *cow;
+
+       /* build an index of lv numbers */
+       memset(lvs, 0, sizeof(lvs));
+       dm_list_iterate_items(dl, pvds) {
+               dm_list_iterate_items(ll, &dl->lvds) {
+                       lvd = &ll->lvd;
+
+                       lvnum = lvd->lv_number;
+
+                       if (lvnum >= MAX_LV) {
+                               log_error("Logical volume number "
+                                         "out of bounds.");
+                               return 0;
+                       }
+
+                       if (!lvs[lvnum] &&
+                           !(lvs[lvnum] = find_lv(vg, (char *)lvd->lv_name))) {
+                               log_error("Couldn't find logical volume '%s'.",
+                                         lvd->lv_name);
+                               return 0;
+                       }
+               }
+       }
+
+       /*
+        * Now iterate through yet again adding the snapshots.
+        */
+       dm_list_iterate_items(dl, pvds) {
+               dm_list_iterate_items(ll, &dl->lvds) {
+                       lvd = &ll->lvd;
+
+                       if (!(lvd->lv_access & LV_SNAPSHOT))
+                               continue;
+
+                       lvnum = lvd->lv_number;
+                       cow = lvs[lvnum];
+                       if (!(org = lvs[lvd->lv_snapshot_minor])) {
+                               log_error("Couldn't find origin logical volume "
+                                         "for snapshot '%s'.", lvd->lv_name);
+                               return 0;
+                       }
+
+                       /* we may have already added this snapshot */
+                       if (lv_is_cow(cow))
+                               continue;
+
+                       /* insert the snapshot */
+                       if (!vg_add_snapshot(org, cow, NULL,
+                                            org->le_count,
+                                            lvd->lv_chunk_size)) {
+                               log_error("Couldn't add snapshot.");
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+int export_uuids(struct disk_list *dl, struct volume_group *vg)
+{
+       struct uuid_list *ul;
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (!(ul = dm_pool_alloc(dl->mem, sizeof(*ul))))
+                       return_0;
+
+               memset(ul->uuid, 0, sizeof(ul->uuid));
+               memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN);
+
+               dm_list_add(&dl->uuids, &ul->list);
+       }
+       return 1;
+}
+
+/*
+ * This calculates the nasty pv_number field
+ * used by LVM1.
+ */
+void export_numbers(struct dm_list *pvds, struct volume_group *vg __attribute__((unused)))
+{
+       struct disk_list *dl;
+       int pv_num = 1;
+
+       dm_list_iterate_items(dl, pvds)
+               dl->pvd.pv_number = pv_num++;
+}
+
+/*
+ * Calculate vg_disk->pv_act.
+ */
+void export_pv_act(struct dm_list *pvds)
+{
+       struct disk_list *dl;
+       int act = 0;
+
+       dm_list_iterate_items(dl, pvds)
+               if (dl->pvd.pv_status & PV_ACTIVE)
+                       act++;
+
+       dm_list_iterate_items(dl, pvds)
+               dl->vgd.pv_act = act;
+}
+
+int export_vg_number(struct format_instance *fid, struct dm_list *pvds,
+                    const char *vg_name, struct dev_filter *filter)
+{
+       struct disk_list *dl;
+       int vg_num;
+
+       if (!get_free_vg_number(fid, filter, vg_name, &vg_num))
+               return_0;
+
+       dm_list_iterate_items(dl, pvds)
+               dl->vgd.vg_number = vg_num;
+
+       return 1;
+}
diff --git a/lib/format1/import-extents.c b/lib/format1/import-extents.c
new file mode 100644 (file)
index 0000000..99723ee
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "disk-rep.h"
+#include "lv_alloc.h"
+#include "display.h"
+#include "segtype.h"
+
+/*
+ * After much thought I have decided it is easier,
+ * and probably no less efficient, to convert the
+ * pe->le map to a full le->pe map, and then
+ * process this to get the segments form that
+ * we're after.  Any code which goes directly from
+ * the pe->le map to segments would be gladly
+ * accepted, if it is less complicated than this
+ * file.
+ */
+struct pe_specifier {
+       struct physical_volume *pv;
+       uint32_t pe;
+};
+
+struct lv_map {
+       struct logical_volume *lv;
+       uint32_t stripes;
+       uint32_t stripe_size;
+       struct pe_specifier *map;
+};
+
+static struct dm_hash_table *_create_lv_maps(struct dm_pool *mem,
+                                         struct volume_group *vg)
+{
+       struct dm_hash_table *maps = dm_hash_create(32);
+       struct lv_list *ll;
+       struct lv_map *lvm;
+
+       if (!maps) {
+               log_error("Unable to create hash table for holding "
+                         "extent maps.");
+               return NULL;
+       }
+
+       dm_list_iterate_items(ll, &vg->lvs) {
+               if (ll->lv->status & SNAPSHOT)
+                       continue;
+
+               if (!(lvm = dm_pool_alloc(mem, sizeof(*lvm))))
+                       goto_bad;
+
+               lvm->lv = ll->lv;
+               if (!(lvm->map = dm_pool_zalloc(mem, sizeof(*lvm->map)
+                                            * ll->lv->le_count)))
+                       goto_bad;
+
+               if (!dm_hash_insert(maps, ll->lv->name, lvm))
+                       goto_bad;
+       }
+
+       return maps;
+
+      bad:
+       dm_hash_destroy(maps);
+       return NULL;
+}
+
+static int _fill_lv_array(struct lv_map **lvs,
+                         struct dm_hash_table *maps, struct disk_list *dl)
+{
+       struct lvd_list *ll;
+       struct lv_map *lvm;
+
+       memset(lvs, 0, sizeof(*lvs) * MAX_LV);
+
+       dm_list_iterate_items(ll, &dl->lvds) {
+               if (!(lvm = dm_hash_lookup(maps, strrchr((char *)ll->lvd.lv_name, '/')
+                                       + 1))) {
+                       log_error("Physical volume (%s) contains an "
+                                 "unknown logical volume (%s).",
+                               dev_name(dl->dev), ll->lvd.lv_name);
+                       return 0;
+               }
+
+               lvm->stripes = ll->lvd.lv_stripes;
+               lvm->stripe_size = ll->lvd.lv_stripesize;
+
+               lvs[ll->lvd.lv_number] = lvm;
+       }
+
+       return 1;
+}
+
+static int _fill_maps(struct dm_hash_table *maps, struct volume_group *vg,
+                     struct dm_list *pvds)
+{
+       struct disk_list *dl;
+       struct physical_volume *pv;
+       struct lv_map *lvms[MAX_LV], *lvm;
+       struct pe_disk *e;
+       uint32_t i, lv_num, le;
+
+       dm_list_iterate_items(dl, pvds) {
+               pv = find_pv(vg, dl->dev);
+               e = dl->extents;
+
+               /* build an array of lv's for this pv */
+               if (!_fill_lv_array(lvms, maps, dl))
+                       return_0;
+
+               for (i = 0; i < dl->pvd.pe_total; i++) {
+                       lv_num = e[i].lv_num;
+
+                       if (lv_num == UNMAPPED_EXTENT)
+                               continue;
+
+                       else {
+                               lv_num--;
+                               lvm = lvms[lv_num];
+
+                               if (!lvm) {
+                                       log_error("Invalid LV in extent map "
+                                                 "(PV %s, PE %" PRIu32
+                                                 ", LV %" PRIu32
+                                                 ", LE %" PRIu32 ")",
+                                                 dev_name(pv->dev), i,
+                                                 lv_num, e[i].le_num);
+                                       return 0;
+                               }
+
+                               le = e[i].le_num;
+
+                               if (le >= lvm->lv->le_count) {
+                                       log_error("logical extent number "
+                                                 "out of bounds");
+                                       return 0;
+                               }
+
+                               if (lvm->map[le].pv) {
+                                       log_error("logical extent (%u) "
+                                                 "already mapped.", le);
+                                       return 0;
+                               }
+
+                               lvm->map[le].pv = pv;
+                               lvm->map[le].pe = i;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+static int _check_single_map(struct lv_map *lvm)
+{
+       uint32_t i;
+
+       for (i = 0; i < lvm->lv->le_count; i++) {
+               if (!lvm->map[i].pv) {
+                       log_error("Logical volume (%s) contains an incomplete "
+                                 "mapping table.", lvm->lv->name);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _check_maps_are_complete(struct dm_hash_table *maps)
+{
+       struct dm_hash_node *n;
+       struct lv_map *lvm;
+
+       for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
+               lvm = (struct lv_map *) dm_hash_get_data(maps, n);
+
+               if (!_check_single_map(lvm))
+                       return_0;
+       }
+       return 1;
+}
+
+static uint32_t _area_length(struct lv_map *lvm, uint32_t le)
+{
+       uint32_t len = 0;
+
+       do
+               len++;
+       while ((lvm->map[le + len].pv == lvm->map[le].pv) &&
+                (lvm->map[le].pv &&
+                 lvm->map[le + len].pe == lvm->map[le].pe + len));
+
+       return len;
+}
+
+static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
+{
+       uint32_t le = 0, len;
+       struct lv_segment *seg;
+       struct segment_type *segtype;
+
+       if (!(segtype = get_segtype_from_string(cmd, "striped")))
+               return_0;
+
+       while (le < lvm->lv->le_count) {
+               len = _area_length(lvm, le);
+
+               if (!(seg = alloc_lv_segment(cmd->mem, segtype, lvm->lv, le,
+                                            len, 0, 0, NULL, 1, len, 0, 0, 0, NULL))) {
+                       log_error("Failed to allocate linear segment.");
+                       return 0;
+               }
+
+               if (!set_lv_segment_area_pv(seg, 0, lvm->map[le].pv,
+                                           lvm->map[le].pe))
+                       return_0;
+
+               dm_list_add(&lvm->lv->segments, &seg->list);
+
+               le += seg->len;
+       }
+
+       return 1;
+}
+
+static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
+                        uint32_t area_len, uint32_t base_le,
+                        uint32_t total_area_len)
+{
+       uint32_t st;
+
+       /*
+        * Is the next physical extent in every stripe adjacent to the last?
+        */
+       for (st = 0; st < area_count; st++)
+               if ((lvm->map[base_le + st * total_area_len + area_len].pv !=
+                    lvm->map[base_le + st * total_area_len].pv) ||
+                   (lvm->map[base_le + st * total_area_len].pv &&
+                    lvm->map[base_le + st * total_area_len + area_len].pe !=
+                    lvm->map[base_le + st * total_area_len].pe + area_len))
+                       return 0;
+
+       return 1;
+}
+
+static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
+{
+       uint32_t st, first_area_le = 0, total_area_len;
+       uint32_t area_len;
+       struct lv_segment *seg;
+       struct segment_type *segtype;
+
+       /*
+        * Work out overall striped length
+        */
+       if (lvm->lv->le_count % lvm->stripes) {
+               log_error("Number of stripes (%u) incompatible "
+                         "with logical extent count (%u) for %s",
+                         lvm->stripes, lvm->lv->le_count, lvm->lv->name);
+       }
+
+       total_area_len = lvm->lv->le_count / lvm->stripes;
+
+       if (!(segtype = get_segtype_from_string(cmd, "striped")))
+               return_0;
+
+       while (first_area_le < total_area_len) {
+               area_len = 1;
+
+               /*
+                * Find how many extents are contiguous in all stripes
+                * and so can form part of this segment
+                */
+               while (_check_stripe(lvm, lvm->stripes,
+                                    area_len, first_area_le, total_area_len))
+                       area_len++;
+
+               if (!(seg = alloc_lv_segment(cmd->mem, segtype, lvm->lv,
+                                            lvm->stripes * first_area_le,
+                                            lvm->stripes * area_len,
+                                            0, lvm->stripe_size, NULL,
+                                            lvm->stripes,
+                                            area_len, 0, 0, 0, NULL))) {
+                       log_error("Failed to allocate striped segment.");
+                       return 0;
+               }
+
+               /*
+                * Set up start positions of each stripe in this segment
+                */
+               for (st = 0; st < seg->area_count; st++)
+                       if (!set_lv_segment_area_pv(seg, st,
+                             lvm->map[first_area_le + st * total_area_len].pv,
+                             lvm->map[first_area_le + st * total_area_len].pe))
+                               return_0;
+
+               dm_list_add(&lvm->lv->segments, &seg->list);
+
+               first_area_le += area_len;
+       }
+
+       return 1;
+}
+
+static int _build_segments(struct cmd_context *cmd, struct lv_map *lvm)
+{
+       return (lvm->stripes > 1 ? _read_stripes(cmd, lvm) :
+               _read_linear(cmd, lvm));
+}
+
+static int _build_all_segments(struct cmd_context *cmd, struct dm_hash_table *maps)
+{
+       struct dm_hash_node *n;
+       struct lv_map *lvm;
+
+       for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
+               lvm = (struct lv_map *) dm_hash_get_data(maps, n);
+               if (!_build_segments(cmd, lvm))
+                       return_0;
+       }
+
+       return 1;
+}
+
+int import_extents(struct cmd_context *cmd, struct volume_group *vg,
+                  struct dm_list *pvds)
+{
+       int r = 0;
+       struct dm_pool *scratch = dm_pool_create("lvm1 import_extents", 10 * 1024);
+       struct dm_hash_table *maps;
+
+       if (!scratch)
+               return_0;
+
+       if (!(maps = _create_lv_maps(scratch, vg))) {
+               log_error("Couldn't allocate logical volume maps.");
+               goto out;
+       }
+
+       if (!_fill_maps(maps, vg, pvds)) {
+               log_error("Couldn't fill logical volume maps.");
+               goto out;
+       }
+
+       if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG))
+               goto_out;
+
+       if (!_build_all_segments(cmd, maps)) {
+               log_error("Couldn't build extent segments.");
+               goto out;
+       }
+       r = 1;
+
+      out:
+       if (maps)
+               dm_hash_destroy(maps);
+       dm_pool_destroy(scratch);
+       return r;
+}
diff --git a/lib/format1/layout.c b/lib/format1/layout.c
new file mode 100644 (file)
index 0000000..de9b206
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+
+/*
+ * Only works with powers of 2.
+ */
+static uint32_t _round_up(uint32_t n, uint32_t size)
+{
+       size--;
+       return (n + size) & ~size;
+}
+
+/* Unused.
+static uint32_t _div_up(uint32_t n, uint32_t size)
+{
+       return _round_up(n, size) / size;
+}
+*/
+
+/*
+ * Each chunk of metadata should be aligned to
+ * METADATA_ALIGN.
+ */
+static uint32_t _next_base(struct data_area *area)
+{
+       return _round_up(area->base + area->size, METADATA_ALIGN);
+}
+
+/*
+ * Quick calculation based on pe_start.
+ */
+static int _adjust_pe_on_disk(struct pv_disk *pvd)
+{
+       uint32_t pe_start = pvd->pe_start << SECTOR_SHIFT;
+
+       if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
+               return 0;
+
+       pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
+       return 1;
+}
+
+static void _calc_simple_layout(struct pv_disk *pvd)
+{
+       pvd->pv_on_disk.base = METADATA_BASE;
+       pvd->pv_on_disk.size = PV_SIZE;
+
+       pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
+       pvd->vg_on_disk.size = VG_SIZE;
+
+       pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
+       pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
+
+       pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
+       pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
+
+       pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
+       pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
+}
+
+static int _check_vg_limits(struct disk_list *dl)
+{
+       if (dl->vgd.lv_max > MAX_LV) {
+               log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
+                         "for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
+                         dl->pvd.vg_name);
+               return 0;
+       }
+
+       if (dl->vgd.pv_max > MAX_PV) {
+               log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
+                         "for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
+                         dl->pvd.vg_name);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * This assumes pe_count and pe_start have already
+ * been calculated correctly.
+ */
+int calculate_layout(struct disk_list *dl)
+{
+       struct pv_disk *pvd = &dl->pvd;
+
+       _calc_simple_layout(pvd);
+       if (!_adjust_pe_on_disk(pvd)) {
+               log_error("Insufficient space for metadata and PE's.");
+               return 0;
+       }
+
+       if (!_check_vg_limits(dl))
+               return 0;
+
+       return 1;
+}
+
+/*
+ * The number of extents that can fit on a disk is metadata format dependant.
+ * pe_start is any existing value for pe_start
+ */
+int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
+                          uint32_t max_extent_count, uint64_t pe_start)
+{
+       struct pv_disk *pvd = dm_malloc(sizeof(*pvd));
+       uint32_t end;
+
+       if (!pvd)
+               return_0;
+
+       /*
+        * Guess how many extents will fit, bearing in mind that
+        * one is going to be knocked off at the start of the
+        * next loop.
+        */
+       if (max_extent_count)
+               pvd->pe_total = max_extent_count + 1;
+       else
+               pvd->pe_total = (pv->size / extent_size);
+
+       if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
+               log_error("Too few extents on %s.  Try smaller extent size.",
+                         pv_dev_name(pv));
+               dm_free(pvd);
+               return 0;
+       }
+
+       do {
+               pvd->pe_total--;
+               _calc_simple_layout(pvd);
+               end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size +
+                       SECTOR_SIZE - 1) >> SECTOR_SHIFT);
+
+               if (pe_start && end < pe_start)
+                       end = pe_start;
+
+               pvd->pe_start = _round_up(end, LVM1_PE_ALIGN);
+
+       } while ((pvd->pe_start + (pvd->pe_total * extent_size))
+                > pv->size);
+
+       if (pvd->pe_total > MAX_PE_TOTAL) {
+               log_error("Metadata extent limit (%u) exceeded for %s - "
+                         "%u required", MAX_PE_TOTAL, pv_dev_name(pv),
+                         pvd->pe_total);
+               dm_free(pvd);
+               return 0;
+       }
+
+       pv->pe_count = pvd->pe_total;
+       pv->pe_start = pvd->pe_start;
+       /* We can't set pe_size here without breaking LVM1 compatibility */
+       dm_free(pvd);
+       return 1;
+}
diff --git a/lib/format1/lvm1-label.c b/lib/format1/lvm1-label.c
new file mode 100644 (file)
index 0000000..07596a5
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "lvm1-label.h"
+#include "disk-rep.h"
+#include "label.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "format1.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static void _not_supported(const char *op)
+{
+       log_error("The '%s' operation is not supported for the lvm1 labeller.",
+                 op);
+}
+
+static int _lvm1_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector)
+{
+       struct pv_disk *pvd = (struct pv_disk *) buf;
+       uint32_t version;
+
+       /* LVM1 label must always be in first sector */
+       if (sector)
+               return 0;
+
+       version = xlate16(pvd->version);
+
+       if (pvd->id[0] == 'H' && pvd->id[1] == 'M' &&
+           (version == 1 || version == 2))
+               return 1;
+
+       return 0;
+}
+
+static int _lvm1_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused)))
+{
+       _not_supported("write");
+       return 0;
+}
+
+static int _lvm1_read(struct labeller *l, struct device *dev, void *buf,
+                struct label **label)
+{
+       struct pv_disk *pvd = (struct pv_disk *) buf;
+       struct vg_disk vgd;
+       struct lvmcache_info *info;
+       const char *vgid = FMT_LVM1_ORPHAN_VG_NAME;
+       const char *vgname = FMT_LVM1_ORPHAN_VG_NAME;
+       unsigned exported = 0;
+
+       munge_pvd(dev, pvd);
+
+       if (*pvd->vg_name) {
+               if (!read_vgd(dev, &vgd, pvd))
+                       return_0;
+               vgid = (char *) vgd.vg_uuid;
+               vgname = (char *) pvd->vg_name;
+               exported = pvd->pv_status & VG_EXPORTED;
+       }
+
+       if (!(info = lvmcache_add(l, (char *)pvd->pv_uuid, dev, vgname, vgid,
+                                 exported)))
+               return_0;
+       *label = info->label;
+
+       info->device_size = xlate32(pvd->pv_size) << SECTOR_SHIFT;
+       dm_list_init(&info->mdas);
+
+       info->status &= ~CACHE_INVALID;
+
+       return 1;
+}
+
+static int _lvm1_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
+{
+       strcpy(label->type, "LVM1");
+
+       return 1;
+}
+
+static void _lvm1_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused)))
+{
+}
+
+static void _lvm1_destroy(struct labeller *l)
+{
+       dm_free(l);
+}
+
+struct label_ops _lvm1_ops = {
+       .can_handle = _lvm1_can_handle,
+       .write = _lvm1_write,
+       .read = _lvm1_read,
+       .verify = _lvm1_can_handle,
+       .initialise_label = _lvm1_initialise_label,
+       .destroy_label = _lvm1_destroy_label,
+       .destroy = _lvm1_destroy,
+};
+
+struct labeller *lvm1_labeller_create(struct format_type *fmt)
+{
+       struct labeller *l;
+
+       if (!(l = dm_malloc(sizeof(*l)))) {
+               log_error("Couldn't allocate labeller object.");
+               return NULL;
+       }
+
+       l->ops = &_lvm1_ops;
+       l->private = (const void *) fmt;
+
+       return l;
+}
diff --git a/lib/format1/lvm1-label.h b/lib/format1/lvm1-label.h
new file mode 100644 (file)
index 0000000..70d314d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_LVM1_LABEL_H
+#define _LVM_LVM1_LABEL_H
+
+#include "metadata.h"
+
+struct labeller *lvm1_labeller_create(struct format_type *fmt);
+
+#endif
diff --git a/lib/format1/vg_number.c b/lib/format1/vg_number.c
new file mode 100644 (file)
index 0000000..47570f5
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+
+/*
+ * FIXME: Quick hack.  We can use caching to
+ * prevent a total re-read, even so vg_number
+ * causes the tools to check *every* pv.  Yuck.
+ * Put in separate file so it wouldn't contaminate
+ * other code.
+ */
+int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter,
+                      const char *candidate_vg, int *result)
+{
+       struct dm_list all_pvs;
+       struct disk_list *dl;
+       struct dm_pool *mem = dm_pool_create("lvm1 vg_number", 10 * 1024);
+       int numbers[MAX_VG], i, r = 0;
+
+       dm_list_init(&all_pvs);
+
+       if (!mem)
+               return_0;
+
+       if (!read_pvs_in_vg(fid->fmt, NULL, filter, mem, &all_pvs))
+               goto_out;
+
+       memset(numbers, 0, sizeof(numbers));
+
+       dm_list_iterate_items(dl, &all_pvs) {
+               if (!*dl->pvd.vg_name || !strcmp((char *)dl->pvd.vg_name, candidate_vg))
+                       continue;
+
+               numbers[dl->vgd.vg_number] = 1;
+       }
+
+       for (i = 0; i < MAX_VG; i++) {
+               if (!numbers[i]) {
+                       r = 1;
+                       *result = i;
+                       break;
+               }
+       }
+
+      out:
+       dm_pool_destroy(mem);
+       return r;
+}
diff --git a/lib/format_pool/.exported_symbols b/lib/format_pool/.exported_symbols
new file mode 100644 (file)
index 0000000..e9fac2e
--- /dev/null
@@ -0,0 +1 @@
+init_format
diff --git a/lib/format_pool/Makefile.in b/lib/format_pool/Makefile.in
new file mode 100644 (file)
index 0000000..be5195c
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+       disk_rep.c \
+       format_pool.c \
+       import_export.c \
+       pool_label.c
+
+LIB_SHARED = liblvm2formatpool.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
diff --git a/lib/format_pool/disk_rep.c b/lib/format_pool/disk_rep.c
new file mode 100644 (file)
index 0000000..ca8bfd7
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "lvmcache.h"
+#include "filter.h"
+#include "xlate.h"
+#include "disk_rep.h"
+
+#include <assert.h>
+
+/* FIXME: memcpy might not be portable */
+#define CPIN_8(x, y, z) {memcpy((x), (y), (z));}
+#define CPOUT_8(x, y, z) {memcpy((y), (x), (z));}
+#define CPIN_16(x, y) {(x) = xlate16_be((y));}
+#define CPOUT_16(x, y) {(y) = xlate16_be((x));}
+#define CPIN_32(x, y) {(x) = xlate32_be((y));}
+#define CPOUT_32(x, y) {(y) = xlate32_be((x));}
+#define CPIN_64(x, y) {(x) = xlate64_be((y));}
+#define CPOUT_64(x, y) {(y) = xlate64_be((x));}
+
+static int __read_pool_disk(const struct format_type *fmt, struct device *dev,
+                           struct dm_pool *mem __attribute__((unused)), struct pool_list *pl,
+                           const char *vg_name __attribute__((unused)))
+{
+       char buf[512] __attribute__((aligned(8)));
+
+       /* FIXME: Need to check the cache here first */
+       if (!dev_read(dev, UINT64_C(0), 512, buf)) {
+               log_very_verbose("Failed to read PV data from %s",
+                                dev_name(dev));
+               return 0;
+       }
+
+       if (!read_pool_label(pl, fmt->labeller, dev, buf, NULL))
+               return_0;
+
+       return 1;
+}
+
+static void _add_pl_to_list(struct dm_list *head, struct pool_list *data)
+{
+       struct pool_list *pl;
+
+       dm_list_iterate_items(pl, head) {
+               if (id_equal(&data->pv_uuid, &pl->pv_uuid)) {
+                       char uuid[ID_LEN + 7] __attribute__((aligned(8)));
+
+                       id_write_format(&pl->pv_uuid, uuid, ID_LEN + 7);
+
+                       if (!dev_subsystem_part_major(data->dev)) {
+                               log_very_verbose("Ignoring duplicate PV %s on "
+                                                "%s", uuid,
+                                                dev_name(data->dev));
+                               return;
+                       }
+                       log_very_verbose("Duplicate PV %s - using %s %s",
+                                        uuid, dev_subsystem_name(data->dev),
+                                        dev_name(data->dev));
+                       dm_list_del(&pl->list);
+                       break;
+               }
+       }
+       dm_list_add(head, &data->list);
+}
+
+int read_pool_label(struct pool_list *pl, struct labeller *l,
+                   struct device *dev, char *buf, struct label **label)
+{
+       struct lvmcache_info *info;
+       struct id pvid;
+       struct id vgid;
+       char uuid[ID_LEN + 7] __attribute__((aligned(8)));
+       struct pool_disk *pd = &pl->pd;
+
+       pool_label_in(pd, buf);
+
+       get_pool_pv_uuid(&pvid, pd);
+       id_write_format(&pvid, uuid, ID_LEN + 7);
+       log_debug("Calculated uuid %s for %s", uuid, dev_name(dev));
+
+       get_pool_vg_uuid(&vgid, pd);
+       id_write_format(&vgid, uuid, ID_LEN + 7);
+       log_debug("Calculated uuid %s for %s", uuid, pd->pl_pool_name);
+
+       if (!(info = lvmcache_add(l, (char *) &pvid, dev, pd->pl_pool_name,
+                                 (char *) &vgid, 0)))
+               return_0;
+       if (label)
+               *label = info->label;
+
+       info->device_size = xlate32_be(pd->pl_blocks) << SECTOR_SHIFT;
+       dm_list_init(&info->mdas);
+
+       info->status &= ~CACHE_INVALID;
+
+       pl->dev = dev;
+       pl->pv = NULL;
+       memcpy(&pl->pv_uuid, &pvid, sizeof(pvid));
+
+       return 1;
+}
+
+/**
+ * pool_label_out - copies a pool_label_t into a char buffer
+ * @pl: ptr to a pool_label_t struct
+ * @buf: ptr to raw space where label info will be copied
+ *
+ * This function is important because it takes care of all of
+ * the endian issues when copying to disk.  This way, when
+ * machines of different architectures are used, they will
+ * be able to interpret ondisk labels correctly.  Always use
+ * this function before writing to disk.
+ */
+void pool_label_out(struct pool_disk *pl, void *buf)
+{
+       struct pool_disk *bufpl = (struct pool_disk *) buf;
+
+       CPOUT_64(pl->pl_magic, bufpl->pl_magic);
+       CPOUT_64(pl->pl_pool_id, bufpl->pl_pool_id);
+       CPOUT_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE);
+       CPOUT_32(pl->pl_version, bufpl->pl_version);
+       CPOUT_32(pl->pl_subpools, bufpl->pl_subpools);
+       CPOUT_32(pl->pl_sp_id, bufpl->pl_sp_id);
+       CPOUT_32(pl->pl_sp_devs, bufpl->pl_sp_devs);
+       CPOUT_32(pl->pl_sp_devid, bufpl->pl_sp_devid);
+       CPOUT_32(pl->pl_sp_type, bufpl->pl_sp_type);
+       CPOUT_64(pl->pl_blocks, bufpl->pl_blocks);
+       CPOUT_32(pl->pl_striping, bufpl->pl_striping);
+       CPOUT_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs);
+       CPOUT_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid);
+       CPOUT_32(pl->pl_sp_weight, bufpl->pl_sp_weight);
+       CPOUT_32(pl->pl_minor, bufpl->pl_minor);
+       CPOUT_32(pl->pl_padding, bufpl->pl_padding);
+       CPOUT_8(pl->pl_reserve, bufpl->pl_reserve, 184);
+}
+
+/**
+ * pool_label_in - copies a char buffer into a pool_label_t
+ * @pl: ptr to a pool_label_t struct
+ * @buf: ptr to raw space where label info is copied from
+ *
+ * This function is important because it takes care of all of
+ * the endian issues when information from disk is about to be
+ * used.  This way, when machines of different architectures
+ * are used, they will be able to interpret ondisk labels
+ * correctly.  Always use this function before using labels that
+ * were read from disk.
+ */
+void pool_label_in(struct pool_disk *pl, void *buf)
+{
+       struct pool_disk *bufpl = (struct pool_disk *) buf;
+
+       CPIN_64(pl->pl_magic, bufpl->pl_magic);
+       CPIN_64(pl->pl_pool_id, bufpl->pl_pool_id);
+       CPIN_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE);
+       CPIN_32(pl->pl_version, bufpl->pl_version);
+       CPIN_32(pl->pl_subpools, bufpl->pl_subpools);
+       CPIN_32(pl->pl_sp_id, bufpl->pl_sp_id);
+       CPIN_32(pl->pl_sp_devs, bufpl->pl_sp_devs);
+       CPIN_32(pl->pl_sp_devid, bufpl->pl_sp_devid);
+       CPIN_32(pl->pl_sp_type, bufpl->pl_sp_type);
+       CPIN_64(pl->pl_blocks, bufpl->pl_blocks);
+       CPIN_32(pl->pl_striping, bufpl->pl_striping);
+       CPIN_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs);
+       CPIN_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid);
+       CPIN_32(pl->pl_sp_weight, bufpl->pl_sp_weight);
+       CPIN_32(pl->pl_minor, bufpl->pl_minor);
+       CPIN_32(pl->pl_padding, bufpl->pl_padding);
+       CPIN_8(pl->pl_reserve, bufpl->pl_reserve, 184);
+}
+
+static char _calc_char(unsigned int id)
+{
+       /*
+        * [0-9A-Za-z!#] - 64 printable chars (6-bits)
+        */
+
+       if (id < 10)
+               return id + 48;
+       if (id < 36)
+               return (id - 10) + 65;
+       if (id < 62)
+               return (id - 36) + 97;
+       if (id == 62)
+               return '!';
+       if (id == 63)
+               return '#';
+
+       return '%';
+}
+
+void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid)
+{
+       int i;
+       unsigned shifter = 0x003F;
+
+       assert(ID_LEN == 32);
+       memset(uuid, 0, ID_LEN);
+       strcat(uuid, "POOL0000000000");
+
+       /* We grab the entire 64 bits (+2 that get shifted in) */
+       for (i = 13; i < 24; i++) {
+               uuid[i] = _calc_char(((unsigned) poolid) & shifter);
+               poolid = poolid >> 6;
+       }
+
+       /* We grab the entire 32 bits (+4 that get shifted in) */
+       for (i = 24; i < 30; i++) {
+               uuid[i] = _calc_char((unsigned) (spid & shifter));
+               spid = spid >> 6;
+       }
+
+       /*
+        * Since we can only have 128 devices, we only worry about the
+        * last 12 bits
+        */
+       for (i = 30; i < 32; i++) {
+               uuid[i] = _calc_char((unsigned) (devid & shifter));
+               devid = devid >> 6;
+       }
+
+}
+
+static int _read_vg_pds(const struct format_type *fmt, struct dm_pool *mem,
+                       struct lvmcache_vginfo *vginfo, struct dm_list *head,
+                       uint32_t *devcount)
+{
+       struct lvmcache_info *info;
+       struct pool_list *pl = NULL;
+       struct dm_pool *tmpmem;
+
+       uint32_t sp_count = 0;
+       uint32_t *sp_devs = NULL;
+       uint32_t i;
+
+       /* FIXME: maybe should return a different error in memory
+        * allocation failure */
+       if (!(tmpmem = dm_pool_create("pool read_vg", 512)))
+               return_0;
+
+       dm_list_iterate_items(info, &vginfo->infos) {
+               if (info->dev &&
+                   !(pl = read_pool_disk(fmt, info->dev, mem, vginfo->vgname)))
+                           break;
+               /*
+                * We need to keep track of the total expected number
+                * of devices per subpool
+                */
+               if (!sp_count) {
+                       /* FIXME pl left uninitialised if !info->dev */
+                       if (!pl) {
+                               log_error(INTERNAL_ERROR "device is missing");
+                               dm_pool_destroy(tmpmem);
+                               return 0;
+                       }
+                       sp_count = pl->pd.pl_subpools;
+                       if (!(sp_devs =
+                             dm_pool_zalloc(tmpmem,
+                                         sizeof(uint32_t) * sp_count))) {
+                               log_error("Unable to allocate %d 32-bit uints",
+                                         sp_count);
+                               dm_pool_destroy(tmpmem);
+                               return 0;
+                       }
+               }
+               /*
+                * watch out for a pool label with a different subpool
+                * count than the original - give up if it does
+                */
+               if (sp_count != pl->pd.pl_subpools)
+                       break;
+
+               _add_pl_to_list(head, pl);
+
+               if (sp_count > pl->pd.pl_sp_id && sp_devs[pl->pd.pl_sp_id] == 0)
+                       sp_devs[pl->pd.pl_sp_id] = pl->pd.pl_sp_devs;
+       }
+
+       *devcount = 0;
+       for (i = 0; i < sp_count; i++)
+               *devcount += sp_devs[i];
+
+       dm_pool_destroy(tmpmem);
+
+       if (pl && *pl->pd.pl_pool_name)
+               return 1;
+
+       return 0;
+
+}
+
+int read_pool_pds(const struct format_type *fmt, const char *vg_name,
+                 struct dm_pool *mem, struct dm_list *pdhead)
+{
+       struct lvmcache_vginfo *vginfo;
+       uint32_t totaldevs;
+       int full_scan = -1;
+
+       do {
+               /*
+                * If the cache scanning doesn't work, this will never work
+                */
+               if (vg_name && (vginfo = vginfo_from_vgname(vg_name, NULL)) &&
+                   vginfo->infos.n) {
+
+                       if (_read_vg_pds(fmt, mem, vginfo, pdhead, &totaldevs)) {
+                               /*
+                                * If we found all the devices we were
+                                * expecting, return success
+                                */
+                               if (dm_list_size(pdhead) == totaldevs)
+                                       return 1;
+
+                               /*
+                                * accept partial pool if we've done a full
+                                * rescan of the cache
+                                */
+                               if (full_scan > 0)
+                                       return 1;
+                       }
+               }
+               /* Failed */
+               dm_list_init(pdhead);
+
+               full_scan++;
+               if (full_scan > 1) {
+                       log_debug("No devices for vg %s found in cache",
+                                 vg_name);
+                       return 0;
+               }
+               lvmcache_label_scan(fmt->cmd, full_scan);
+
+       } while (1);
+
+}
+
+struct pool_list *read_pool_disk(const struct format_type *fmt,
+                                struct device *dev, struct dm_pool *mem,
+                                const char *vg_name)
+{
+       struct pool_list *pl;
+
+       if (!dev_open(dev))
+               return_NULL;
+
+       if (!(pl = dm_pool_zalloc(mem, sizeof(*pl)))) {
+               log_error("Unable to allocate pool list structure");
+               return 0;
+       }
+
+       if (!__read_pool_disk(fmt, dev, mem, pl, vg_name))
+               return_NULL;
+
+       if (!dev_close(dev))
+               stack;
+
+       return pl;
+
+}
diff --git a/lib/format_pool/disk_rep.h b/lib/format_pool/disk_rep.h
new file mode 100644 (file)
index 0000000..fff7343
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef DISK_REP_FORMAT_POOL_H
+#define DISK_REP_FORMAT_POOL_H
+
+#include "label.h"
+#include "metadata.h"
+
+#define MINOR_OFFSET 65536
+
+/* From NSP.cf */
+#define NSPMajorVersion        4
+#define NSPMinorVersion        1
+#define NSPUpdateLevel 3
+
+/* From pool_std.h */
+#define POOL_NAME_SIZE          (256)
+#define POOL_MAGIC             0x011670
+#define POOL_MAJOR              (121)
+#define POOL_MAX_DEVICES       128
+
+/* When checking for version matching, the first two numbers **
+** are important for metadata formats, a.k.a pool labels.   **
+** All the numbers are important when checking if the user  **
+** space tools match up with the kernel module............. */
+#define POOL_VERSION           (NSPMajorVersion << 16 | \
+                                NSPMinorVersion <<  8 | \
+                                NSPUpdateLevel)
+
+/* Pool label is at the head of every pool disk partition */
+#define SIZEOF_POOL_LABEL       (8192)
+
+/* in sectors */
+#define POOL_PE_SIZE     (SIZEOF_POOL_LABEL >> SECTOR_SHIFT)
+#define POOL_PE_START    (SIZEOF_POOL_LABEL >> SECTOR_SHIFT)
+
+/* Helper fxns */
+#define get_pool_vg_uuid(id, pd) do { get_pool_uuid((char *)(id), \
+                                                    (pd)->pl_pool_id, 0, 0); \
+                                    } while(0)
+#define get_pool_pv_uuid(id, pd) do { get_pool_uuid((char *)(id), \
+                                                    (pd)->pl_pool_id, \
+                                                    (pd)->pl_sp_id, \
+                                                    (pd)->pl_sp_devid); \
+                                    } while(0)
+#define get_pool_lv_uuid(id, pd) do { get_pool_uuid((char *)&(id)[0], \
+                                                    (pd)->pl_pool_id, 0, 0); \
+                                      get_pool_uuid((char*)&(id)[1], \
+                                                    (pd)->pl_pool_id, 0, 0); \
+                                    } while(0)
+
+struct pool_disk;
+struct pool_list;
+struct user_subpool;
+struct user_device;
+
+struct pool_disk {
+       uint64_t pl_magic;      /* Pool magic number */
+       uint64_t pl_pool_id;    /* Unique pool identifier */
+       char pl_pool_name[POOL_NAME_SIZE];      /* Name of pool */
+       uint32_t pl_version;    /* Pool version */
+       uint32_t pl_subpools;   /* Number of subpools in this pool */
+       uint32_t pl_sp_id;      /* Subpool number within pool */
+       uint32_t pl_sp_devs;    /* Number of data partitions in this subpool */
+       uint32_t pl_sp_devid;   /* Partition number within subpool */
+       uint32_t pl_sp_type;    /* Partition type */
+       uint64_t pl_blocks;     /* Number of blocks in this partition */
+       uint32_t pl_striping;   /* Striping size within subpool */
+       /*
+        * If the number of DMEP devices is zero, then the next field **
+        * ** (pl_sp_dmepid) becomes the subpool ID for redirection.  In **
+        * ** other words, if this subpool does not have the capability  **
+        * ** to do DMEP, then it must specify which subpool will do it  **
+        * ** in it's place
+        */
+
+       /*
+        * While the next 3 field are no longer used, they must stay to keep **
+        * ** backward compatibility...........................................
+        */
+       uint32_t pl_sp_dmepdevs;/* Number of dmep devices in this subpool */
+       uint32_t pl_sp_dmepid;  /* Dmep device number within subpool */
+       uint32_t pl_sp_weight;  /* if dmep dev, pref to using it */
+
+       uint32_t pl_minor;      /* the pool minor number */
+       uint32_t pl_padding;    /* reminder - think about alignment */
+
+       /*
+        * Even though we're zeroing out 8k at the front of the disk before
+        * writing the label, putting this in
+        */
+       char pl_reserve[184];   /* bump the structure size out to 512 bytes */
+};
+
+struct pool_list {
+       struct dm_list list;
+       struct pool_disk pd;
+       struct physical_volume *pv;
+       struct id pv_uuid;
+       struct device *dev;
+};
+
+struct user_subpool {
+       uint32_t initialized;
+       uint32_t id;
+       uint32_t striping;
+       uint32_t num_devs;
+       uint32_t type;
+       uint32_t dummy;
+       struct user_device *devs;
+};
+
+struct user_device {
+       uint32_t initialized;
+       uint32_t sp_id;
+       uint32_t devid;
+       uint32_t dummy;
+       uint64_t blocks;
+       struct physical_volume *pv;
+};
+
+int read_pool_label(struct pool_list *pl, struct labeller *l,
+                   struct device *dev, char *buf, struct label **label);
+void pool_label_out(struct pool_disk *pl, void *buf);
+void pool_label_in(struct pool_disk *pl, void *buf);
+void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid);
+int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls);
+int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem,
+                   struct dm_list *pls);
+int import_pool_pvs(const struct format_type *fmt, struct volume_group *vg,
+                   struct dm_pool *mem, struct dm_list *pls);
+int import_pool_pv(const struct format_type *fmt, struct dm_pool *mem,
+                  struct volume_group *vg, struct physical_volume *pv,
+                  struct pool_list *pl);
+int import_pool_segments(struct dm_list *lvs, struct dm_pool *mem,
+                        struct user_subpool *usp, int sp_count);
+int read_pool_pds(const struct format_type *fmt, const char *vgname,
+                 struct dm_pool *mem, struct dm_list *head);
+struct pool_list *read_pool_disk(const struct format_type *fmt,
+                                struct device *dev, struct dm_pool *mem,
+                                const char *vg_name);
+
+#endif                         /* DISK_REP_POOL_FORMAT_H */
diff --git a/lib/format_pool/format_pool.c b/lib/format_pool/format_pool.c
new file mode 100644 (file)
index 0000000..730da87
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "limits.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvmcache.h"
+#include "disk_rep.h"
+#include "format_pool.h"
+#include "pool_label.h"
+
+/* Must be called after pvs are imported */
+static struct user_subpool *_build_usp(struct dm_list *pls, struct dm_pool *mem,
+                                      int *sps)
+{
+       struct pool_list *pl;
+       struct user_subpool *usp = NULL, *cur_sp = NULL;
+       struct user_device *cur_dev = NULL;
+
+       /*
+        * FIXME: Need to do some checks here - I'm tempted to add a
+        * user_pool structure and build the entire thing to check against.
+        */
+       dm_list_iterate_items(pl, pls) {
+               *sps = pl->pd.pl_subpools;
+               if (!usp && (!(usp = dm_pool_zalloc(mem, sizeof(*usp) * (*sps))))) {
+                       log_error("Unable to allocate %d subpool structures",
+                                 *sps);
+                       return 0;
+               }
+
+               if (cur_sp != &usp[pl->pd.pl_sp_id]) {
+                       cur_sp = &usp[pl->pd.pl_sp_id];
+
+                       cur_sp->id = pl->pd.pl_sp_id;
+                       cur_sp->striping = pl->pd.pl_striping;
+                       cur_sp->num_devs = pl->pd.pl_sp_devs;
+                       cur_sp->type = pl->pd.pl_sp_type;
+                       cur_sp->initialized = 1;
+               }
+
+               if (!cur_sp->devs &&
+                   (!(cur_sp->devs =
+                      dm_pool_zalloc(mem,
+                                  sizeof(*usp->devs) * pl->pd.pl_sp_devs)))) {
+
+                       log_error("Unable to allocate %d pool_device "
+                                 "structures", pl->pd.pl_sp_devs);
+                       return 0;
+               }
+
+               cur_dev = &cur_sp->devs[pl->pd.pl_sp_devid];
+               cur_dev->sp_id = cur_sp->id;
+               cur_dev->devid = pl->pd.pl_sp_id;
+               cur_dev->blocks = pl->pd.pl_blocks;
+               cur_dev->pv = pl->pv;
+               cur_dev->initialized = 1;
+       }
+
+       return usp;
+}
+
+static int _check_usp(const char *vgname, struct user_subpool *usp, int sp_count)
+{
+       int i;
+       unsigned j;
+
+       for (i = 0; i < sp_count; i++) {
+               if (!usp[i].initialized) {
+                       log_error("Missing subpool %d in pool %s", i, vgname);
+                       return 0;
+               }
+               for (j = 0; j < usp[i].num_devs; j++) {
+                       if (!usp[i].devs[j].initialized) {
+                               log_error("Missing device %u for subpool %d"
+                                         " in pool %s", j, i, vgname);
+                               return 0;
+                       }
+
+               }
+       }
+
+       return 1;
+}
+
+static struct volume_group *_build_vg_from_pds(struct format_instance
+                                              *fid, struct dm_pool *mem,
+                                              struct dm_list *pds)
+{
+       struct dm_pool *smem = fid->fmt->cmd->mem;
+       struct volume_group *vg = NULL;
+       struct user_subpool *usp = NULL;
+       int sp_count;
+
+       if (!(vg = dm_pool_zalloc(smem, sizeof(*vg)))) {
+               log_error("Unable to allocate volume group structure");
+               return NULL;
+       }
+
+       vg->cmd = fid->fmt->cmd;
+       vg->vgmem = mem;
+       vg->fid = fid;
+       vg->name = NULL;
+       vg->status = 0;
+       vg->extent_count = 0;
+       vg->pv_count = 0;
+       vg->seqno = 1;
+       vg->system_id = NULL;
+       dm_list_init(&vg->pvs);
+       dm_list_init(&vg->lvs);
+       dm_list_init(&vg->tags);
+       dm_list_init(&vg->removed_pvs);
+
+       if (!import_pool_vg(vg, smem, pds))
+               return_NULL;
+
+       if (!import_pool_pvs(fid->fmt, vg, smem, pds))
+               return_NULL;
+
+       if (!import_pool_lvs(vg, smem, pds))
+               return_NULL;
+
+       /*
+        * I need an intermediate subpool structure that contains all the
+        * relevant info for this.  Then i can iterate through the subpool
+        * structures for checking, and create the segments
+        */
+       if (!(usp = _build_usp(pds, mem, &sp_count)))
+               return_NULL;
+
+       /*
+        * check the subpool structures - we can't handle partial VGs in
+        * the pool format, so this will error out if we're missing PVs
+        */
+       if (!_check_usp(vg->name, usp, sp_count))
+               return_NULL;
+
+       if (!import_pool_segments(&vg->lvs, smem, usp, sp_count))
+               return_NULL;
+
+       return vg;
+}
+
+static struct volume_group *_pool_vg_read(struct format_instance *fid,
+                                    const char *vg_name,
+                                    struct metadata_area *mda __attribute__((unused)))
+{
+       struct dm_pool *mem = dm_pool_create("pool vg_read", VG_MEMPOOL_CHUNK);
+       struct dm_list pds;
+       struct volume_group *vg = NULL;
+
+       dm_list_init(&pds);
+
+       /* We can safely ignore the mda passed in */
+
+       if (!mem)
+               return_NULL;
+
+       /* Strip dev_dir if present */
+       vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir);
+
+       /* Read all the pvs in the vg */
+       if (!read_pool_pds(fid->fmt, vg_name, mem, &pds))
+               goto_out;
+
+       /* Do the rest of the vg stuff */
+       if (!(vg = _build_vg_from_pds(fid, mem, &pds)))
+               goto_out;
+
+       return vg;
+out:
+       dm_pool_destroy(mem);
+       return NULL;
+}
+
+static int _pool_pv_setup(const struct format_type *fmt __attribute__((unused)),
+                         uint64_t pe_start __attribute__((unused)),
+                         uint32_t extent_count __attribute__((unused)),
+                         uint32_t extent_size __attribute__((unused)),
+                         unsigned long data_alignment __attribute__((unused)),
+                         unsigned long data_alignment_offset __attribute__((unused)),
+                         int pvmetadatacopies __attribute__((unused)),
+                         uint64_t pvmetadatasize __attribute__((unused)),
+                         unsigned metadataignore __attribute__((unused)),
+                         struct dm_list *mdas __attribute__((unused)),
+                         struct physical_volume *pv __attribute__((unused)),
+                         struct volume_group *vg __attribute__((unused)))
+{
+       return 1;
+}
+
+static int _pool_pv_read(const struct format_type *fmt, const char *pv_name,
+                        struct physical_volume *pv,
+                        struct dm_list *mdas __attribute__((unused)),
+                        int scan_label_only __attribute__((unused)))
+{
+       struct dm_pool *mem = dm_pool_create("pool pv_read", 1024);
+       struct pool_list *pl;
+       struct device *dev;
+       int r = 0;
+
+       log_very_verbose("Reading physical volume data %s from disk", pv_name);
+
+       if (!mem)
+               return_0;
+
+       if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
+               goto_out;
+
+       /*
+        * I need to read the disk and populate a pv structure here
+        * I'll probably need to abstract some of this later for the
+        * vg_read code
+        */
+       if (!(pl = read_pool_disk(fmt, dev, mem, NULL)))
+               goto_out;
+
+       if (!import_pool_pv(fmt, fmt->cmd->mem, NULL, pv, pl))
+               goto_out;
+
+       pv->fmt = fmt;
+
+       r = 1;
+
+      out:
+       dm_pool_destroy(mem);
+       return r;
+}
+
+/* *INDENT-OFF* */
+static struct metadata_area_ops _metadata_format_pool_ops = {
+       .vg_read = _pool_vg_read,
+};
+/* *INDENT-ON* */
+
+static struct format_instance *_pool_create_instance(const struct format_type *fmt,
+                                               const char *vgname __attribute__((unused)),
+                                               const char *vgid __attribute__((unused)),
+                                               void *private __attribute__((unused)))
+{
+       struct format_instance *fid;
+       struct metadata_area *mda;
+
+       if (!(fid = dm_pool_zalloc(fmt->cmd->mem, sizeof(*fid)))) {
+               log_error("Unable to allocate format instance structure for "
+                         "pool format");
+               return NULL;
+       }
+
+       fid->fmt = fmt;
+       dm_list_init(&fid->metadata_areas_in_use);
+       dm_list_init(&fid->metadata_areas_ignored);
+
+       /* Define a NULL metadata area */
+       if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda)))) {
+               log_error("Unable to allocate metadata area structure "
+                         "for pool format");
+               dm_pool_free(fmt->cmd->mem, fid);
+               return NULL;
+       }
+
+       mda->ops = &_metadata_format_pool_ops;
+       mda->metadata_locn = NULL;
+       mda->status = 0;
+       dm_list_add(&fid->metadata_areas_in_use, &mda->list);
+
+       return fid;
+}
+
+static void _pool_destroy_instance(struct format_instance *fid __attribute__((unused)))
+{
+}
+
+static void _pool_destroy(struct format_type *fmt)
+{
+       dm_free(fmt);
+}
+
+/* *INDENT-OFF* */
+static struct format_handler _format_pool_ops = {
+       .pv_read = _pool_pv_read,
+       .pv_setup = _pool_pv_setup,
+       .create_instance = _pool_create_instance,
+       .destroy_instance = _pool_destroy_instance,
+       .destroy = _pool_destroy,
+};
+/* *INDENT-ON */
+
+#ifdef POOL_INTERNAL
+struct format_type *init_pool_format(struct cmd_context *cmd)
+#else                          /* Shared */
+struct format_type *init_format(struct cmd_context *cmd);
+struct format_type *init_format(struct cmd_context *cmd)
+#endif
+{
+       struct format_type *fmt = dm_malloc(sizeof(*fmt));
+
+       if (!fmt) {
+               log_error("Unable to allocate format type structure for pool "
+                         "format");
+               return NULL;
+       }
+
+       fmt->cmd = cmd;
+       fmt->ops = &_format_pool_ops;
+       fmt->name = FMT_POOL_NAME;
+       fmt->alias = NULL;
+       fmt->orphan_vg_name = FMT_POOL_ORPHAN_VG_NAME;
+       fmt->features = 0;
+       fmt->private = NULL;
+
+       if (!(fmt->labeller = pool_labeller_create(fmt))) {
+               log_error("Couldn't create pool label handler.");
+               return NULL;
+       }
+
+       if (!(label_register_handler(FMT_POOL_NAME, fmt->labeller))) {
+               log_error("Couldn't register pool label handler.");
+               return NULL;
+       }
+
+       log_very_verbose("Initialised format: %s", fmt->name);
+
+       return fmt;
+}
diff --git a/lib/format_pool/format_pool.h b/lib/format_pool/format_pool.h
new file mode 100644 (file)
index 0000000..0d4b973
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FORMAT_POOL_H
+#define _LVM_FORMAT_POOL_H
+
+#include "metadata.h"
+
+#define FMT_POOL_NAME "pool"
+#define FMT_POOL_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_POOL_NAME)
+
+#ifdef POOL_INTERNAL
+struct format_type *init_pool_format(struct cmd_context *cmd);
+#endif
+
+#endif
diff --git a/lib/format_pool/import_export.c b/lib/format_pool/import_export.c
new file mode 100644 (file)
index 0000000..7884247
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "lvmcache.h"
+#include "disk_rep.h"
+#include "sptype_names.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "str_list.h"
+#include "display.h"
+#include "segtype.h"
+#include "toolcontext.h"
+
+/* This file contains only imports at the moment... */
+
+int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls)
+{
+       struct pool_list *pl;
+
+       dm_list_iterate_items(pl, pls) {
+               vg->extent_count +=
+                   ((pl->pd.pl_blocks) / POOL_PE_SIZE);
+
+               vg->free_count = vg->extent_count;
+
+               if (vg->name)
+                       continue;
+
+               vg->name = dm_pool_strdup(mem, pl->pd.pl_pool_name);
+               get_pool_vg_uuid(&vg->id, &pl->pd);
+               vg->extent_size = POOL_PE_SIZE;
+               vg->status |= LVM_READ | LVM_WRITE | CLUSTERED | SHARED;
+               vg->max_lv = 1;
+               vg->max_pv = POOL_MAX_DEVICES;
+               vg->alloc = ALLOC_NORMAL;
+       }
+
+       return 1;
+}
+
+int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls)
+{
+       struct pool_list *pl;
+       struct logical_volume *lv;
+
+       if (!(lv = alloc_lv(mem)))
+               return_0;
+
+       lv->status = 0;
+       lv->alloc = ALLOC_NORMAL;
+       lv->size = 0;
+       lv->name = NULL;
+       lv->le_count = 0;
+       lv->read_ahead = vg->cmd->default_settings.read_ahead;
+
+       dm_list_iterate_items(pl, pls) {
+               lv->size += pl->pd.pl_blocks;
+
+               if (lv->name)
+                       continue;
+
+               if (!(lv->name = dm_pool_strdup(mem, pl->pd.pl_pool_name)))
+                       return_0;
+
+               get_pool_lv_uuid(lv->lvid.id, &pl->pd);
+               log_debug("Calculated lv uuid for lv %s: %s", lv->name,
+                         lv->lvid.s);
+
+               lv->status |= VISIBLE_LV | LVM_READ | LVM_WRITE;
+               lv->major = POOL_MAJOR;
+
+               /* for pool a minor of 0 is dynamic */
+               if (pl->pd.pl_minor) {
+                       lv->status |= FIXED_MINOR;
+                       lv->minor = pl->pd.pl_minor + MINOR_OFFSET;
+               } else {
+                       lv->minor = -1;
+               }
+       }
+
+       lv->le_count = lv->size / POOL_PE_SIZE;
+
+       return link_lv_to_vg(vg, lv);
+}
+
+int import_pool_pvs(const struct format_type *fmt, struct volume_group *vg,
+                   struct dm_pool *mem, struct dm_list *pls)
+{
+       struct pv_list *pvl;
+       struct pool_list *pl;
+
+       dm_list_iterate_items(pl, pls) {
+               if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
+                       log_error("Unable to allocate pv list structure");
+                       return 0;
+               }
+               if (!(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv)))) {
+                       log_error("Unable to allocate pv structure");
+                       return 0;
+               }
+               if (!import_pool_pv(fmt, mem, vg, pvl->pv, pl)) {
+                       return 0;
+               }
+               pl->pv = pvl->pv;
+               pvl->mdas = NULL;
+               pvl->pe_ranges = NULL;
+               add_pvl_to_vgs(vg, pvl);
+       }
+
+       return 1;
+}
+
+int import_pool_pv(const struct format_type *fmt, struct dm_pool *mem,
+                  struct volume_group *vg, struct physical_volume *pv,
+                  struct pool_list *pl)
+{
+       struct pool_disk *pd = &pl->pd;
+
+       memset(pv, 0, sizeof(*pv));
+
+       get_pool_pv_uuid(&pv->id, pd);
+       pv->fmt = fmt;
+
+       pv->dev = pl->dev;
+       if (!(pv->vg_name = dm_pool_strdup(mem, pd->pl_pool_name))) {
+               log_error("Unable to duplicate vg_name string");
+               return 0;
+       }
+       if (vg != NULL)
+               memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
+       pv->status = 0;
+       pv->size = pd->pl_blocks;
+       pv->pe_size = POOL_PE_SIZE;
+       pv->pe_start = POOL_PE_START;
+       pv->pe_count = pv->size / POOL_PE_SIZE;
+       pv->pe_alloc_count = 0;
+       pv->pe_align = 0;
+
+       dm_list_init(&pv->tags);
+       dm_list_init(&pv->segments);
+
+       if (!alloc_pv_segment_whole_pv(mem, pv))
+               return_0;
+
+       return 1;
+}
+
+static const char *_cvt_sptype(uint32_t sptype)
+{
+       int i;
+       for (i = 0; sptype_names[i].name[0]; i++) {
+               if (sptype == sptype_names[i].label) {
+                       break;
+               }
+       }
+       log_debug("Found sptype %X and converted it to %s",
+                 sptype, sptype_names[i].name);
+       return sptype_names[i].name;
+}
+
+static int _add_stripe_seg(struct dm_pool *mem,
+                          struct user_subpool *usp, struct logical_volume *lv,
+                          uint32_t *le_cur)
+{
+       struct lv_segment *seg;
+       struct segment_type *segtype;
+       unsigned j;
+       uint32_t area_len;
+
+       if (usp->striping & (usp->striping - 1)) {
+               log_error("Stripe size must be a power of 2");
+               return 0;
+       }
+
+       area_len = (usp->devs[0].blocks) / POOL_PE_SIZE;
+
+       if (!(segtype = get_segtype_from_string(lv->vg->cmd,
+                                                    "striped")))
+               return_0;
+
+       if (!(seg = alloc_lv_segment(mem, segtype, lv, *le_cur,
+                                    area_len * usp->num_devs, 0,
+                                    usp->striping, NULL, usp->num_devs,
+                                    area_len, 0, 0, 0, NULL))) {
+               log_error("Unable to allocate striped lv_segment structure");
+               return 0;
+       }
+
+       for (j = 0; j < usp->num_devs; j++)
+               if (!set_lv_segment_area_pv(seg, j, usp->devs[j].pv, 0))
+                       return_0;
+
+       /* add the subpool type to the segment tag list */
+       if (!str_list_add(mem, &seg->tags, _cvt_sptype(usp->type))) {
+               log_error("Allocation failed for str_list.");
+               return 0;
+       }
+
+       dm_list_add(&lv->segments, &seg->list);
+
+       *le_cur += seg->len;
+
+       return 1;
+}
+
+static int _add_linear_seg(struct dm_pool *mem,
+                          struct user_subpool *usp, struct logical_volume *lv,
+                          uint32_t *le_cur)
+{
+       struct lv_segment *seg;
+       struct segment_type *segtype;
+       unsigned j;
+       uint32_t area_len;
+
+       if (!(segtype = get_segtype_from_string(lv->vg->cmd, "striped")))
+               return_0;
+
+       for (j = 0; j < usp->num_devs; j++) {
+               area_len = (usp->devs[j].blocks) / POOL_PE_SIZE;
+
+               if (!(seg = alloc_lv_segment(mem, segtype, lv, *le_cur,
+                                            area_len, 0, usp->striping,
+                                            NULL, 1, area_len,
+                                            POOL_PE_SIZE, 0, 0, NULL))) {
+                       log_error("Unable to allocate linear lv_segment "
+                                 "structure");
+                       return 0;
+               }
+
+               /* add the subpool type to the segment tag list */
+               if (!str_list_add(mem, &seg->tags, _cvt_sptype(usp->type))) {
+                       log_error("Allocation failed for str_list.");
+                       return 0;
+               }
+
+               if (!set_lv_segment_area_pv(seg, 0, usp->devs[j].pv, 0))
+                       return_0;
+               dm_list_add(&lv->segments, &seg->list);
+
+               *le_cur += seg->len;
+       }
+
+       return 1;
+}
+
+int import_pool_segments(struct dm_list *lvs, struct dm_pool *mem,
+                        struct user_subpool *usp, int subpools)
+{
+       struct lv_list *lvl;
+       struct logical_volume *lv;
+       uint32_t le_cur = 0;
+       int i;
+
+       dm_list_iterate_items(lvl, lvs) {
+               lv = lvl->lv;
+
+               if (lv->status & SNAPSHOT)
+                       continue;
+
+               for (i = 0; i < subpools; i++) {
+                       if (usp[i].striping) {
+                               if (!_add_stripe_seg(mem, &usp[i], lv, &le_cur))
+                                       return_0;
+                       } else {
+                               if (!_add_linear_seg(mem, &usp[i], lv, &le_cur))
+                                       return_0;
+                       }
+               }
+       }
+
+       return 1;
+}
diff --git a/lib/format_pool/pool_label.c b/lib/format_pool/pool_label.c
new file mode 100644 (file)
index 0000000..2d34e60
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "disk_rep.h"
+#include "pool_label.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static void _pool_not_supported(const char *op)
+{
+       log_error("The '%s' operation is not supported for the pool labeller.",
+                 op);
+}
+
+static int _pool_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector)
+{
+
+       struct pool_disk pd;
+
+       /*
+        * POOL label must always be in first sector
+        */
+       if (sector)
+               return 0;
+
+       pool_label_in(&pd, buf);
+
+       /* can ignore 8 rightmost bits for ondisk format check */
+       if ((pd.pl_magic == POOL_MAGIC) &&
+           (pd.pl_version >> 8 == POOL_VERSION >> 8))
+               return 1;
+
+       return 0;
+}
+
+static int _pool_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused)))
+{
+       _pool_not_supported("write");
+       return 0;
+}
+
+static int _pool_read(struct labeller *l, struct device *dev, void *buf,
+                struct label **label)
+{
+       struct pool_list pl;
+
+       return read_pool_label(&pl, l, dev, buf, label);
+}
+
+static int _pool_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
+{
+       strcpy(label->type, "POOL");
+
+       return 1;
+}
+
+static void _pool_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused)))
+{
+}
+
+static void _label_pool_destroy(struct labeller *l)
+{
+       dm_free(l);
+}
+
+struct label_ops _pool_ops = {
+      .can_handle = _pool_can_handle,
+      .write = _pool_write,
+      .read = _pool_read,
+      .verify = _pool_can_handle,
+      .initialise_label = _pool_initialise_label,
+      .destroy_label = _pool_destroy_label,
+      .destroy = _label_pool_destroy,
+};
+
+struct labeller *pool_labeller_create(struct format_type *fmt)
+{
+       struct labeller *l;
+
+       if (!(l = dm_malloc(sizeof(*l)))) {
+               log_error("Couldn't allocate labeller object.");
+               return NULL;
+       }
+
+       l->ops = &_pool_ops;
+       l->private = (const void *) fmt;
+
+       return l;
+}
diff --git a/lib/format_pool/pool_label.h b/lib/format_pool/pool_label.h
new file mode 100644 (file)
index 0000000..b7dc371
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_POOL_LABEL_H
+#define _LVM_POOL_LABEL_H
+
+#include "metadata.h"
+
+struct labeller *pool_labeller_create(struct format_type *fmt);
+
+#endif
diff --git a/lib/format_pool/sptype_names.h b/lib/format_pool/sptype_names.h
new file mode 100644 (file)
index 0000000..fbfe33d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef SPTYPE_NAMES_H
+#define SPTYPE_NAMES_H
+
+/* This must be kept up to date with sistina/pool/module/pool_sptypes.h */
+
+/*  Generic Labels  */
+#define SPTYPE_DATA                (0x00000000)
+
+/*  GFS specific labels  */
+#define SPTYPE_GFS_DATA            (0x68011670)
+#define SPTYPE_GFS_JOURNAL         (0x69011670)
+
+struct sptype_name {
+       const char *name;
+       uint32_t label;
+};
+
+static const struct sptype_name sptype_names[] = {
+       {"data",        SPTYPE_DATA},
+
+       {"gfs_data",    SPTYPE_GFS_DATA},
+       {"gfs_journal", SPTYPE_GFS_JOURNAL},
+
+       {"", 0x0}               /*  This must be the last flag.  */
+};
+
+#endif
diff --git a/lib/format_text/archive.c b/lib/format_text/archive.c
new file mode 100644 (file)
index 0000000..43425dc
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "format-text.h"
+
+#include "config.h"
+#include "import-export.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "toolcontext.h"
+
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <time.h>
+
+#define SECS_PER_DAY 86400     /* 24*60*60 */
+
+/*
+ * The format instance is given a directory path upon creation.
+ * Each file in this directory whose name is of the form
+ * '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
+ * contains a description of a single volume group.
+ *
+ * The prefix ($1 from the above regex) of the config file gives
+ * the volume group name.
+ *
+ * Backup files that have expired will be removed.
+ */
+
+/*
+ * A list of these is built up for our volume group.  Ordered
+ * with the least recent at the head.
+ */
+struct archive_file {
+       struct dm_list list;
+
+       const char *path;
+       uint32_t index;
+};
+
+/*
+ * Extract vg name and version number from a filename.
+ */
+static int _split_vg(const char *filename, char *vgname, size_t vgsize,
+                    uint32_t *ix)
+{
+       size_t len, vg_len;
+       const char *dot, *underscore;
+
+       len = strlen(filename);
+       if (len < 7)
+               return 0;
+
+       dot = (filename + len - 3);
+       if (strcmp(".vg", dot))
+               return 0;
+
+       if (!(underscore = strrchr(filename, '_')))
+               return 0;
+
+       if (sscanf(underscore + 1, "%u", ix) != 1)
+               return 0;
+
+       vg_len = underscore - filename;
+       if (vg_len + 1 > vgsize)
+               return 0;
+
+       strncpy(vgname, filename, vg_len);
+       vgname[vg_len] = '\0';
+
+       return 1;
+}
+
+static void _insert_archive_file(struct dm_list *head, struct archive_file *b)
+{
+       struct archive_file *bf = NULL;
+
+       if (dm_list_empty(head)) {
+               dm_list_add(head, &b->list);
+               return;
+       }
+
+       /* index reduces through list */
+       dm_list_iterate_items(bf, head) {
+               if (b->index > bf->index) {
+                       dm_list_add(&bf->list, &b->list);
+                       return;
+               }
+       }
+
+       dm_list_add_h(&bf->list, &b->list);
+}
+
+static char *_join_file_to_dir(struct dm_pool *mem, const char *dir, const char *name)
+{
+       if (!dm_pool_begin_object(mem, 32) ||
+           !dm_pool_grow_object(mem, dir, strlen(dir)) ||
+           !dm_pool_grow_object(mem, "/", 1) ||
+           !dm_pool_grow_object(mem, name, strlen(name)) ||
+           !dm_pool_grow_object(mem, "\0", 1))
+               return_NULL;
+
+       return dm_pool_end_object(mem);
+}
+
+/*
+ * Returns a list of archive_files.
+ */
+static struct dm_list *_scan_archive(struct dm_pool *mem,
+                                 const char *vgname, const char *dir)
+{
+       int i, count;
+       uint32_t ix;
+       char vgname_found[64], *path;
+       struct dirent **dirent;
+       struct archive_file *af;
+       struct dm_list *results;
+
+       if (!(results = dm_pool_alloc(mem, sizeof(*results))))
+               return_NULL;
+
+       dm_list_init(results);
+
+       /* Sort fails beyond 5-digit indexes */
+       if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) {
+               log_error("Couldn't scan the archive directory (%s).", dir);
+               return 0;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (!strcmp(dirent[i]->d_name, ".") ||
+                   !strcmp(dirent[i]->d_name, ".."))
+                       continue;
+
+               /* check the name is the correct format */
+               if (!_split_vg(dirent[i]->d_name, vgname_found,
+                              sizeof(vgname_found), &ix))
+                       continue;
+
+               /* is it the vg we're interested in ? */
+               if (strcmp(vgname, vgname_found))
+                       continue;
+
+               if (!(path = _join_file_to_dir(mem, dir, dirent[i]->d_name)))
+                       goto_out;
+
+               /*
+                * Create a new archive_file.
+                */
+               if (!(af = dm_pool_alloc(mem, sizeof(*af)))) {
+                       log_error("Couldn't create new archive file.");
+                       results = NULL;
+                       goto out;
+               }
+
+               af->index = ix;
+               af->path = path;
+
+               /*
+                * Insert it to the correct part of the list.
+                */
+               _insert_archive_file(results, af);
+       }
+
+      out:
+       for (i = 0; i < count; i++)
+               free(dirent[i]);
+       free(dirent);
+
+       return results;
+}
+
+static void _remove_expired(struct dm_list *archives, uint32_t archives_size,
+                           uint32_t retain_days, uint32_t min_archive)
+{
+       struct archive_file *bf;
+       struct stat sb;
+       time_t retain_time;
+
+       /* Make sure there are enough archives to even bother looking for
+        * expired ones... */
+       if (archives_size <= min_archive)
+               return;
+
+       /* Convert retain_days into the time after which we must retain */
+       retain_time = time(NULL) - (time_t) retain_days *SECS_PER_DAY;
+
+       /* Assume list is ordered newest first (by index) */
+       dm_list_iterate_back_items(bf, archives) {
+               /* Get the mtime of the file and unlink if too old */
+               if (stat(bf->path, &sb)) {
+                       log_sys_error("stat", bf->path);
+                       continue;
+               }
+
+               if (sb.st_mtime > retain_time)
+                       return;
+
+               log_very_verbose("Expiring archive %s", bf->path);
+               if (unlink(bf->path))
+                       log_sys_error("unlink", bf->path);
+
+               /* Don't delete any more if we've reached the minimum */
+               if (--archives_size <= min_archive)
+                       return;
+       }
+}
+
+int archive_vg(struct volume_group *vg,
+              const char *dir, const char *desc,
+              uint32_t retain_days, uint32_t min_archive)
+{
+       int i, fd, rnum, renamed = 0;
+       uint32_t ix = 0;
+       struct archive_file *last;
+       FILE *fp = NULL;
+       char temp_file[PATH_MAX], archive_name[PATH_MAX];
+       struct dm_list *archives;
+
+       /*
+        * Write the vg out to a temporary file.
+        */
+       if (!create_temp_name(dir, temp_file, sizeof(temp_file), &fd,
+                             &vg->cmd->rand_seed)) {
+               log_error("Couldn't create temporary archive name.");
+               return 0;
+       }
+
+       if (!(fp = fdopen(fd, "w"))) {
+               log_error("Couldn't create FILE object for archive.");
+               if (close(fd))
+                       log_sys_error("close", temp_file);
+               return 0;
+       }
+
+       if (!text_vg_export_file(vg, desc, fp)) {
+               if (fclose(fp))
+                       log_sys_error("fclose", temp_file);
+               return_0;
+       }
+
+       if (lvm_fclose(fp, temp_file))
+               return_0; /* Leave file behind as evidence of failure */
+
+       /*
+        * Now we want to rename this file to <vg>_index.vg.
+        */
+       if (!(archives = _scan_archive(vg->cmd->mem, vg->name, dir)))
+               return_0;
+
+       if (dm_list_empty(archives))
+               ix = 0;
+       else {
+               last = dm_list_item(dm_list_first(archives), struct archive_file);
+               ix = last->index + 1;
+       }
+
+       rnum = rand_r(&vg->cmd->rand_seed);
+
+       for (i = 0; i < 10; i++) {
+               if (dm_snprintf(archive_name, sizeof(archive_name),
+                                "%s/%s_%05u-%d.vg",
+                                dir, vg->name, ix, rnum) < 0) {
+                       log_error("Archive file name too long.");
+                       return 0;
+               }
+
+               if ((renamed = lvm_rename(temp_file, archive_name)))
+                       break;
+
+               ix++;
+       }
+
+       if (!renamed)
+               log_error("Archive rename failed for %s", temp_file);
+
+       _remove_expired(archives, dm_list_size(archives) + renamed, retain_days,
+                       min_archive);
+
+       return 1;
+}
+
+static void _display_archive(struct cmd_context *cmd, struct archive_file *af)
+{
+       struct volume_group *vg = NULL;
+       struct format_instance *tf;
+       time_t when;
+       char *desc;
+       void *context;
+
+       log_print(" ");
+       log_print("File:\t\t%s", af->path);
+
+       if (!(context = create_text_context(cmd, af->path, NULL)) ||
+           !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
+                                                        NULL, context))) {
+               log_error("Couldn't create text instance object.");
+               return;
+       }
+
+       /*
+        * Read the archive file to ensure that it is valid, and
+        * retrieve the archive time and description.
+        */
+       /* FIXME Use variation on _vg_read */
+       if (!(vg = text_vg_import_file(tf, af->path, &when, &desc))) {
+               log_error("Unable to read archive file.");
+               tf->fmt->ops->destroy_instance(tf);
+               return;
+       }
+
+       log_print("VG name:    \t%s", vg->name);
+       log_print("Description:\t%s", desc ? : "<No description>");
+       log_print("Backup Time:\t%s", ctime(&when));
+
+       free_vg(vg);
+       tf->fmt->ops->destroy_instance(tf);
+}
+
+int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname)
+{
+       struct dm_list *archives;
+       struct archive_file *af;
+
+       if (!(archives = _scan_archive(cmd->mem, vgname, dir)))
+               return_0;
+
+       if (dm_list_empty(archives))
+               log_print("No archives found in %s.", dir);
+
+       dm_list_iterate_back_items(af, archives)
+               _display_archive(cmd, af);
+
+       dm_pool_free(cmd->mem, archives);
+
+       return 1;
+}
+
+int archive_list_file(struct cmd_context *cmd, const char *file)
+{
+       struct archive_file af;
+
+       af.path = file;
+
+       if (!path_exists(af.path)) {
+               log_error("Archive file %s not found.", af.path);
+               return 0;
+       }
+
+       _display_archive(cmd, &af);
+
+       return 1;
+}
+
+int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname)
+{
+       struct archive_file af;
+
+       if (!(af.path = _join_file_to_dir(cmd->mem, dir, vgname)))
+               return_0;
+
+       if (path_exists(af.path))
+               _display_archive(cmd, &af);
+
+       return 1;
+}
diff --git a/lib/format_text/archiver.c b/lib/format_text/archiver.c
new file mode 100644 (file)
index 0000000..ef85c6c
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "archiver.h"
+#include "format-text.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "lvmcache.h"
+#include "toolcontext.h"
+#include "locking.h"
+
+#include <unistd.h>
+
+struct archive_params {
+       int enabled;
+       char *dir;
+       unsigned int keep_days;
+       unsigned int keep_number;
+};
+
+struct backup_params {
+       int enabled;
+       char *dir;
+};
+
+int archive_init(struct cmd_context *cmd, const char *dir,
+                unsigned int keep_days, unsigned int keep_min,
+                int enabled)
+{
+       archive_exit(cmd);
+
+       if (!(cmd->archive_params = dm_pool_zalloc(cmd->libmem,
+                                               sizeof(*cmd->archive_params)))) {
+               log_error("archive_params alloc failed");
+               return 0;
+       }
+
+       cmd->archive_params->dir = NULL;
+
+       if (!*dir)
+               return 1;
+
+       if (!(cmd->archive_params->dir = dm_strdup(dir))) {
+               log_error("Couldn't copy archive directory name.");
+               return 0;
+       }
+
+       cmd->archive_params->keep_days = keep_days;
+       cmd->archive_params->keep_number = keep_min;
+       archive_enable(cmd, enabled);
+
+       return 1;
+}
+
+void archive_exit(struct cmd_context *cmd)
+{
+       if (!cmd->archive_params)
+               return;
+       if (cmd->archive_params->dir)
+               dm_free(cmd->archive_params->dir);
+       memset(cmd->archive_params, 0, sizeof(*cmd->archive_params));
+}
+
+void archive_enable(struct cmd_context *cmd, int flag)
+{
+       cmd->archive_params->enabled = flag;
+}
+
+static char *_build_desc(struct dm_pool *mem, const char *line, int before)
+{
+       size_t len = strlen(line) + 32;
+       char *buffer;
+
+       if (!(buffer = dm_pool_zalloc(mem, strlen(line) + 32)))
+               return_NULL;
+
+       if (snprintf(buffer, len,
+                    "Created %s executing '%s'",
+                    before ? "*before*" : "*after*", line) < 0)
+               return_NULL;
+
+       return buffer;
+}
+
+static int __archive(struct volume_group *vg)
+{
+       char *desc;
+
+       if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 1)))
+               return_0;
+
+       return archive_vg(vg, vg->cmd->archive_params->dir, desc,
+                         vg->cmd->archive_params->keep_days,
+                         vg->cmd->archive_params->keep_number);
+}
+
+int archive(struct volume_group *vg)
+{
+       if (!vg->cmd->archive_params->enabled || !vg->cmd->archive_params->dir)
+               return 1;
+
+       if (test_mode()) {
+               log_verbose("Test mode: Skipping archiving of volume group.");
+               return 1;
+       }
+
+       if (!dm_create_dir(vg->cmd->archive_params->dir))
+               return 0;
+
+       /* Trap a read-only file system */
+       if ((access(vg->cmd->archive_params->dir, R_OK | W_OK | X_OK) == -1) &&
+            (errno == EROFS))
+               return 0;
+
+       log_verbose("Archiving volume group \"%s\" metadata (seqno %u).", vg->name,
+                   vg->seqno);
+       if (!__archive(vg)) {
+               log_error("Volume group \"%s\" metadata archive failed.",
+                         vg->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+int archive_display(struct cmd_context *cmd, const char *vg_name)
+{
+       int r1, r2;
+
+       r1 = archive_list(cmd, cmd->archive_params->dir, vg_name);
+       r2 = backup_list(cmd, cmd->backup_params->dir, vg_name);
+
+       return r1 && r2;
+}
+
+int archive_display_file(struct cmd_context *cmd, const char *file)
+{
+       int r;
+
+       r = archive_list_file(cmd, file);
+
+       return r;
+}
+
+int backup_init(struct cmd_context *cmd, const char *dir,
+               int enabled)
+{
+       backup_exit(cmd);
+
+       if (!(cmd->backup_params = dm_pool_zalloc(cmd->libmem,
+                                              sizeof(*cmd->backup_params)))) {
+               log_error("backup_params alloc failed");
+               return 0;
+       }
+
+       cmd->backup_params->dir = NULL;
+       if (!*dir)
+               return 1;
+
+       if (!(cmd->backup_params->dir = dm_strdup(dir))) {
+               log_error("Couldn't copy backup directory name.");
+               return 0;
+       }
+       backup_enable(cmd, enabled);
+
+       return 1;
+}
+
+void backup_exit(struct cmd_context *cmd)
+{
+       if (!cmd->backup_params)
+               return;
+       if (cmd->backup_params->dir)
+               dm_free(cmd->backup_params->dir);
+       memset(cmd->backup_params, 0, sizeof(*cmd->backup_params));
+}
+
+void backup_enable(struct cmd_context *cmd, int flag)
+{
+       cmd->backup_params->enabled = flag;
+}
+
+static int __backup(struct volume_group *vg)
+{
+       char name[PATH_MAX];
+       char *desc;
+
+       if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 0)))
+               return_0;
+
+       if (dm_snprintf(name, sizeof(name), "%s/%s",
+                        vg->cmd->backup_params->dir, vg->name) < 0) {
+               log_error("Failed to generate volume group metadata backup "
+                         "filename.");
+               return 0;
+       }
+
+       return backup_to_file(name, desc, vg);
+}
+
+int backup_locally(struct volume_group *vg)
+{
+       if (!vg->cmd->backup_params->enabled || !vg->cmd->backup_params->dir) {
+               log_warn("WARNING: This metadata update is NOT backed up");
+               return 1;
+       }
+
+       if (test_mode()) {
+               log_verbose("Test mode: Skipping volume group backup.");
+               return 1;
+       }
+
+       if (!dm_create_dir(vg->cmd->backup_params->dir))
+               return 0;
+
+       /* Trap a read-only file system */
+       if ((access(vg->cmd->backup_params->dir, R_OK | W_OK | X_OK) == -1) &&
+           (errno == EROFS))
+               return 0;
+
+       if (!__backup(vg)) {
+               log_error("Backup of volume group %s metadata failed.",
+                         vg->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+int backup(struct volume_group *vg)
+{
+       if (vg_is_clustered(vg))
+               remote_backup_metadata(vg);
+
+       return backup_locally(vg);
+}
+
+int backup_remove(struct cmd_context *cmd, const char *vg_name)
+{
+       char path[PATH_MAX];
+
+       if (dm_snprintf(path, sizeof(path), "%s/%s",
+                        cmd->backup_params->dir, vg_name) < 0) {
+               log_error("Failed to generate backup filename (for removal).");
+               return 0;
+       }
+
+       /*
+        * Let this fail silently.
+        */
+       unlink(path);
+       return 1;
+}
+
+struct volume_group *backup_read_vg(struct cmd_context *cmd,
+                                   const char *vg_name, const char *file)
+{
+       struct volume_group *vg = NULL;
+       struct format_instance *tf;
+       struct metadata_area *mda;
+       void *context;
+
+       if (!(context = create_text_context(cmd, file,
+                                           cmd->cmd_line)) ||
+           !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
+                                                        NULL, context))) {
+               log_error("Couldn't create text format object.");
+               return NULL;
+       }
+
+       dm_list_iterate_items(mda, &tf->metadata_areas_in_use) {
+               if (!(vg = mda->ops->vg_read(tf, vg_name, mda)))
+                       stack;
+               break;
+       }
+
+       tf->fmt->ops->destroy_instance(tf);
+       return vg;
+}
+
+/* ORPHAN and VG locks held before calling this */
+int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+       struct lvmcache_info *info;
+
+       /*
+        * FIXME: Check that the PVs referenced in the backup are
+        * not members of other existing VGs.
+        */
+
+       /* Attempt to write out using currently active format */
+       if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg->name,
+                                                      NULL, NULL))) {
+               log_error("Failed to allocate format instance");
+               return 0;
+       }
+
+       /*
+        * Setting vg->old_name to a blank value will explicitly
+        * disable any attempt to check VG name in existing metadata.
+       */
+       vg->old_name = dm_pool_strdup(vg->vgmem, "");
+
+       /* Add any metadata areas on the PVs */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+               if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
+                       log_error("PV %s missing from cache",
+                                 pv_dev_name(pv));
+                       return 0;
+               }
+               if (cmd->fmt != info->fmt) {
+                       log_error("PV %s is a different format (seqno %s)",
+                                 pv_dev_name(pv), info->fmt->name);
+                       return 0;
+               }
+               if (!vg->fid->fmt->ops->
+                   pv_setup(vg->fid->fmt, UINT64_C(0), 0, 0, 0, 0, 0UL,
+                            UINT64_C(0), 0,
+                            &vg->fid->metadata_areas_in_use, pv, vg)) {
+                       log_error("Format-specific setup for %s failed",
+                                 pv_dev_name(pv));
+                       return 0;
+               }
+       }
+
+       if (!vg_write(vg) || !vg_commit(vg))
+               return_0;
+
+       return 1;
+}
+
+/* ORPHAN and VG locks held before calling this */
+int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
+                            const char *file)
+{
+       struct volume_group *vg;
+       int missing_pvs, r = 0;
+
+       /*
+        * Read in the volume group from the text file.
+        */
+       if (!(vg = backup_read_vg(cmd, vg_name, file)))
+               return_0;
+
+       missing_pvs = vg_missing_pv_count(vg);
+       if (missing_pvs == 0)
+               r = backup_restore_vg(cmd, vg);
+       else
+               log_error("Cannot restore Volume Group %s with %i PVs "
+                         "marked as missing.", vg->name, missing_pvs);
+
+       free_vg(vg);
+       return r;
+}
+
+int backup_restore(struct cmd_context *cmd, const char *vg_name)
+{
+       char path[PATH_MAX];
+
+       if (dm_snprintf(path, sizeof(path), "%s/%s",
+                        cmd->backup_params->dir, vg_name) < 0) {
+               log_error("Failed to generate backup filename (for restore).");
+               return 0;
+       }
+
+       return backup_restore_from_file(cmd, vg_name, path);
+}
+
+int backup_to_file(const char *file, const char *desc, struct volume_group *vg)
+{
+       int r = 0;
+       struct format_instance *tf;
+       struct metadata_area *mda;
+       void *context;
+       struct cmd_context *cmd;
+
+       cmd = vg->cmd;
+
+       log_verbose("Creating volume group backup \"%s\" (seqno %u).", file, vg->seqno);
+
+       if (!(context = create_text_context(cmd, file, desc)) ||
+           !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
+                                                        NULL, context))) {
+               log_error("Couldn't create backup object.");
+               return 0;
+       }
+
+       if (!dm_list_size(&tf->metadata_areas_in_use)) {
+               log_error(INTERNAL_ERROR "No in use metadata areas to write.");
+               return 0;
+       }
+
+       /* Write and commit the metadata area */
+       dm_list_iterate_items(mda, &tf->metadata_areas_in_use) {
+               if (!(r = mda->ops->vg_write(tf, vg, mda))) {
+                       stack;
+                       continue;
+               }
+               if (mda->ops->vg_commit &&
+                   !(r = mda->ops->vg_commit(tf, vg, mda))) {
+                       stack;
+               }
+       }
+
+       tf->fmt->ops->destroy_instance(tf);
+       return r;
+}
+
+/*
+ * Update backup (and archive) if they're out-of-date or don't exist.
+ */
+void check_current_backup(struct volume_group *vg)
+{
+       char path[PATH_MAX];
+       struct volume_group *vg_backup;
+       int old_suppress;
+
+       if (vg_is_exported(vg))
+               return;
+
+       if (dm_snprintf(path, sizeof(path), "%s/%s",
+                        vg->cmd->backup_params->dir, vg->name) < 0) {
+               log_debug("Failed to generate backup filename.");
+               return;
+       }
+
+       old_suppress = log_suppress(1);
+       /* Up-to-date backup exists? */
+       if ((vg_backup = backup_read_vg(vg->cmd, vg->name, path)) &&
+           (vg->seqno == vg_backup->seqno) &&
+           (id_equal(&vg->id, &vg_backup->id))) {
+               log_suppress(old_suppress);
+               free_vg(vg_backup);
+               return;
+       }
+       log_suppress(old_suppress);
+
+       if (vg_backup) {
+               archive(vg_backup);
+               free_vg(vg_backup);
+       }
+       archive(vg);
+       backup_locally(vg);
+}
diff --git a/lib/format_text/archiver.h b/lib/format_text/archiver.h
new file mode 100644 (file)
index 0000000..7346f93
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TOOL_ARCHIVE_H
+#define _LVM_TOOL_ARCHIVE_H
+
+#include "metadata-exported.h"
+
+/*
+ * There are two operations that come under the general area of
+ * backups.  'Archiving' occurs just before a volume group
+ * configuration is changed.  The user may configure when
+ * archived files are expired.  Typically archives will be stored
+ * in /etc/lvm/archive.
+ *
+ * A 'backup' is a redundant copy of the *current* volume group
+ * configuration.  As such it should be taken just after the
+ * volume group is changed.  Only 1 backup file will exist.
+ * Typically backups will be stored in /etc/lvm/backups.
+ */
+
+int archive_init(struct cmd_context *cmd, const char *dir,
+                unsigned int keep_days, unsigned int keep_min,
+                int enabled);
+void archive_exit(struct cmd_context *cmd);
+
+void archive_enable(struct cmd_context *cmd, int flag);
+int archive(struct volume_group *vg);
+int archive_display(struct cmd_context *cmd, const char *vg_name);
+int archive_display_file(struct cmd_context *cmd, const char *file);
+
+int backup_init(struct cmd_context *cmd, const char *dir, int enabled);
+void backup_exit(struct cmd_context *cmd);
+
+void backup_enable(struct cmd_context *cmd, int flag);
+int backup(struct volume_group *vg);
+int backup_locally(struct volume_group *vg);
+int backup_remove(struct cmd_context *cmd, const char *vg_name);
+
+struct volume_group *backup_read_vg(struct cmd_context *cmd,
+                                   const char *vg_name, const char *file);
+int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg);
+int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
+                            const char *file);
+int backup_restore(struct cmd_context *cmd, const char *vg_name);
+
+int backup_to_file(const char *file, const char *desc, struct volume_group *vg);
+
+void check_current_backup(struct volume_group *vg);
+
+#endif
diff --git a/lib/format_text/export.c b/lib/format_text/export.c
new file mode 100644 (file)
index 0000000..8ddfa85
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "import-export.h"
+#include "metadata.h"
+#include "display.h"
+#include "lvm-string.h"
+#include "segtype.h"
+#include "text_export.h"
+#include "lvm-version.h"
+
+#include <stdarg.h>
+#include <time.h>
+#include <sys/utsname.h>
+
+struct formatter;
+typedef int (*out_with_comment_fn) (struct formatter * f, const char *comment,
+                                   const char *fmt, va_list ap);
+typedef int (*nl_fn) (struct formatter * f);
+
+/*
+ * Macro for formatted output.
+ * out_with_comment_fn returns -1 if data didn't fit and buffer was expanded.
+ * Then argument list is reset and out_with_comment_fn is called again.
+ */
+#define _out_with_comment(f, buffer, fmt, ap) \
+       do { \
+               va_start(ap, fmt); \
+               r = f->out_with_comment(f, buffer, fmt, ap); \
+               va_end(ap); \
+       } while (r == -1)
+
+/*
+ * The first half of this file deals with
+ * exporting the vg, ie. writing it to a file.
+ */
+struct formatter {
+       struct dm_pool *mem;    /* pv names allocated from here */
+       struct dm_hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */
+
+       union {
+               FILE *fp;       /* where we're writing to */
+               struct {
+                       char *start;
+                       uint32_t size;
+                       uint32_t used;
+               } buf;
+       } data;
+
+       out_with_comment_fn out_with_comment;
+       nl_fn nl;
+
+       int indent;             /* current level of indentation */
+       int error;
+       int header;             /* 1 => comments at start; 0 => end */
+};
+
+static struct utsname _utsname;
+
+static void _init(void)
+{
+       static int _initialised = 0;
+
+       if (_initialised)
+               return;
+
+       if (uname(&_utsname)) {
+               log_error("uname failed: %s", strerror(errno));
+               memset(&_utsname, 0, sizeof(_utsname));
+       }
+
+       _initialised = 1;
+}
+
+/*
+ * Formatting functions.
+ */
+
+#define MAX_INDENT 5
+static void _inc_indent(struct formatter *f)
+{
+       if (++f->indent > MAX_INDENT)
+               f->indent = MAX_INDENT;
+}
+
+static void _dec_indent(struct formatter *f)
+{
+       if (!f->indent--) {
+               log_error(INTERNAL_ERROR "problem tracking indentation");
+               f->indent = 0;
+       }
+}
+
+/*
+ * Newline function for prettier layout.
+ */
+static int _nl_file(struct formatter *f)
+{
+       fprintf(f->data.fp, "\n");
+
+       return 1;
+}
+
+static int _extend_buffer(struct formatter *f)
+{
+       char *newbuf;
+
+       log_debug("Doubling metadata output buffer to %" PRIu32,
+                 f->data.buf.size * 2);
+       if (!(newbuf = dm_realloc(f->data.buf.start,
+                                  f->data.buf.size * 2))) {
+               log_error("Buffer reallocation failed.");
+               return 0;
+       }
+       f->data.buf.start = newbuf;
+       f->data.buf.size *= 2;
+
+       return 1;
+}
+
+static int _nl_raw(struct formatter *f)
+{
+       /* If metadata doesn't fit, extend buffer */
+       if ((f->data.buf.used + 2 > f->data.buf.size) &&
+           (!_extend_buffer(f)))
+               return_0;
+
+       *(f->data.buf.start + f->data.buf.used) = '\n';
+       f->data.buf.used += 1;
+
+       *(f->data.buf.start + f->data.buf.used) = '\0';
+
+       return 1;
+}
+
+#define COMMENT_TAB 6
+static int _out_with_comment_file(struct formatter *f, const char *comment,
+                                 const char *fmt, va_list ap)
+{
+       int i;
+       char white_space[MAX_INDENT + 1];
+
+       if (ferror(f->data.fp))
+               return 0;
+
+       for (i = 0; i < f->indent; i++)
+               white_space[i] = '\t';
+       white_space[i] = '\0';
+       fputs(white_space, f->data.fp);
+       i = vfprintf(f->data.fp, fmt, ap);
+
+       if (comment) {
+               /*
+                * line comments up if possible.
+                */
+               i += 8 * f->indent;
+               i /= 8;
+               i++;
+
+               do
+                       fputc('\t', f->data.fp);
+
+               while (++i < COMMENT_TAB);
+
+               fputs(comment, f->data.fp);
+       }
+       fputc('\n', f->data.fp);
+
+       return 1;
+}
+
+static int _out_with_comment_raw(struct formatter *f,
+                                const char *comment __attribute__((unused)),
+                                const char *fmt, va_list ap)
+{
+       int n;
+
+       n = vsnprintf(f->data.buf.start + f->data.buf.used,
+                     f->data.buf.size - f->data.buf.used, fmt, ap);
+
+       /* If metadata doesn't fit, extend buffer */
+       if (n < 0 || (n + f->data.buf.used + 2 > f->data.buf.size)) {
+               if (!_extend_buffer(f))
+                       return_0;
+               return -1; /* Retry */
+       }
+
+       f->data.buf.used += n;
+
+       outnl(f);
+
+       return 1;
+}
+
+/*
+ * Formats a string, converting a size specified
+ * in 512-byte sectors to a more human readable
+ * form (eg, megabytes).  We may want to lift this
+ * for other code to use.
+ */
+static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
+{
+       static const char *_units[] = {
+               "Kilobytes",
+               "Megabytes",
+               "Gigabytes",
+               "Terabytes",
+               "Petabytes",
+               "Exabytes",
+               NULL
+       };
+
+       int i;
+       double d = (double) sectors;
+
+       /* to convert to K */
+       d /= 2.0;
+
+       for (i = 0; (d > 1024.0) && _units[i]; i++)
+               d /= 1024.0;
+
+       return dm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
+}
+
+/* increment indention level */
+void out_inc_indent(struct formatter *f)
+{
+       _inc_indent(f);
+}
+
+/* decrement indention level */
+void out_dec_indent(struct formatter *f)
+{
+       _dec_indent(f);
+}
+
+/* insert new line */
+int out_newline(struct formatter *f)
+{
+       return f->nl(f);
+}
+
+/*
+ * Appends a comment giving a size in more easily
+ * readable form (eg, 4M instead of 8096).
+ */
+int out_size(struct formatter *f, uint64_t size, const char *fmt, ...)
+{
+       char buffer[64];
+       va_list ap;
+       int r;
+
+       if (!_sectors_to_units(size, buffer, sizeof(buffer)))
+               return 0;
+
+       _out_with_comment(f, buffer, fmt, ap);
+
+       return r;
+}
+
+/*
+ * Appends a comment indicating that the line is
+ * only a hint.
+ */
+int out_hint(struct formatter *f, const char *fmt, ...)
+{
+       va_list ap;
+       int r;
+
+       _out_with_comment(f, "# Hint only", fmt, ap);
+
+       return r;
+}
+
+/*
+ * The normal output function with comment
+ */
+int out_text_with_comment(struct formatter *f, const char *comment, const char *fmt, ...)
+{
+       va_list ap;
+       int r;
+
+       _out_with_comment(f, comment, fmt, ap);
+
+       return r;
+}
+
+/*
+ * The normal output function.
+ */
+int out_text(struct formatter *f, const char *fmt, ...)
+{
+       va_list ap;
+       int r;
+
+       _out_with_comment(f, NULL, fmt, ap);
+
+       return r;
+}
+
+static int _out_line(const char *line, void *_f) {
+       struct formatter *f = (struct formatter *) _f;
+       return out_text(f, "%s", line);
+}
+
+int out_config_node(struct formatter *f, const struct config_node *cn)
+{
+       return write_config_node(cn, _out_line, f);
+}
+
+static int _print_header(struct formatter *f,
+                        const char *desc)
+{
+       char *buf;
+       time_t t;
+
+       t = time(NULL);
+
+       outf(f, "# Generated by LVM2 version %s: %s", LVM_VERSION, ctime(&t));
+       outf(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\"");
+       outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE);
+       outnl(f);
+
+       if (!(buf = alloca(escaped_len(desc)))) {
+               log_error("temporary stack allocation for description"
+                         "string failed");
+               return 0;
+       }
+       outf(f, "description = \"%s\"", escape_double_quotes(buf, desc));
+       outnl(f);
+       outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename,
+            _utsname.sysname, _utsname.nodename, _utsname.release,
+            _utsname.version, _utsname.machine);
+       outf(f, "creation_time = %lu\t# %s", t, ctime(&t));
+
+       return 1;
+}
+
+static int _print_flag_config(struct formatter *f, uint64_t status, int type)
+{
+       char buffer[4096];
+       if (!print_flags(status, type | STATUS_FLAG, buffer, sizeof(buffer)))
+               return_0;
+       outf(f, "status = %s", buffer);
+
+       if (!print_flags(status, type, buffer, sizeof(buffer)))
+               return_0;
+       outf(f, "flags = %s", buffer);
+
+       return 1;
+}
+
+
+static int _out_tags(struct formatter *f, struct dm_list *tags)
+{
+       char *tag_buffer;
+
+       if (!dm_list_empty(tags)) {
+               if (!(tag_buffer = alloc_printed_tags(tags)))
+                       return_0;
+               if (!out_text(f, "tags = %s", tag_buffer)) {
+                       dm_free(tag_buffer);
+                       return_0;
+               }
+               dm_free(tag_buffer);
+       }
+
+       return 1;
+}
+
+static int _print_vg(struct formatter *f, struct volume_group *vg)
+{
+       char buffer[4096];
+
+       if (!id_write_format(&vg->id, buffer, sizeof(buffer)))
+               return_0;
+
+       outf(f, "id = \"%s\"", buffer);
+
+       outf(f, "seqno = %u", vg->seqno);
+
+       if (!_print_flag_config(f, vg->status, VG_FLAGS))
+               return_0;
+
+       if (!_out_tags(f, &vg->tags))
+               return_0;
+
+       if (vg->system_id && *vg->system_id)
+               outf(f, "system_id = \"%s\"", vg->system_id);
+
+       outsize(f, (uint64_t) vg->extent_size, "extent_size = %u",
+               vg->extent_size);
+       outf(f, "max_lv = %u", vg->max_lv);
+       outf(f, "max_pv = %u", vg->max_pv);
+
+       /* Default policy is NORMAL; INHERIT is meaningless */
+       if (vg->alloc != ALLOC_NORMAL && vg->alloc != ALLOC_INHERIT) {
+               outnl(f);
+               outf(f, "allocation_policy = \"%s\"",
+                    get_alloc_string(vg->alloc));
+       }
+       outf(f, "metadata_copies = %u", vg->mda_copies);
+
+       return 1;
+}
+
+/*
+ * Get the pv%d name from the formatters hash
+ * table.
+ */
+static const char *_get_pv_name_from_uuid(struct formatter *f, char *uuid)
+{
+       return dm_hash_lookup(f->pv_names, uuid);
+}
+
+static const char *_get_pv_name(struct formatter *f, struct physical_volume *pv)
+{
+       char uuid[64] __attribute__((aligned(8)));
+
+       if (!pv || !id_write_format(&pv->id, uuid, sizeof(uuid)))
+               return_NULL;
+
+       return _get_pv_name_from_uuid(f, uuid);
+}
+
+static int _print_pvs(struct formatter *f, struct volume_group *vg)
+{
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+       char buffer[4096];
+       char *buf;
+       const char *name;
+
+       outf(f, "physical_volumes {");
+       _inc_indent(f);
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+
+               if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
+                       return_0;
+
+               if (!(name = _get_pv_name_from_uuid(f, buffer)))
+                       return_0;
+
+               outnl(f);
+               outf(f, "%s {", name);
+               _inc_indent(f);
+
+               outf(f, "id = \"%s\"", buffer);
+
+               if (!(buf = alloca(escaped_len(pv_dev_name(pv))))) {
+                       log_error("temporary stack allocation for device name"
+                                 "string failed");
+                       return 0;
+               }
+
+               outhint(f, "device = \"%s\"",
+                       escape_double_quotes(buf, pv_dev_name(pv)));
+               outnl(f);
+
+               if (!_print_flag_config(f, pv->status, PV_FLAGS))
+                       return_0;
+
+               if (!_out_tags(f, &pv->tags))
+                       return_0;
+
+               outsize(f, pv->size, "dev_size = %" PRIu64, pv->size);
+
+               outf(f, "pe_start = %" PRIu64, pv->pe_start);
+               outsize(f, vg->extent_size * (uint64_t) pv->pe_count,
+                       "pe_count = %u", pv->pe_count);
+
+               _dec_indent(f);
+               outf(f, "}");
+       }
+
+       _dec_indent(f);
+       outf(f, "}");
+       return 1;
+}
+
+static int _print_segment(struct formatter *f, struct volume_group *vg,
+                         int count, struct lv_segment *seg)
+{
+       outf(f, "segment%u {", count);
+       _inc_indent(f);
+
+       outf(f, "start_extent = %u", seg->le);
+       outsize(f, (uint64_t) seg->len * vg->extent_size,
+               "extent_count = %u", seg->len);
+
+       outnl(f);
+       outf(f, "type = \"%s\"", seg->segtype->name);
+
+       if (!_out_tags(f, &seg->tags))
+               return_0;
+
+       if (seg->segtype->ops->text_export &&
+           !seg->segtype->ops->text_export(seg, f))
+               return_0;
+
+       _dec_indent(f);
+       outf(f, "}");
+
+       return 1;
+}
+
+int out_areas(struct formatter *f, const struct lv_segment *seg,
+             const char *type)
+{
+       const char *name;
+       unsigned int s;
+
+       outnl(f);
+
+       outf(f, "%ss = [", type);
+       _inc_indent(f);
+
+       for (s = 0; s < seg->area_count; s++) {
+               switch (seg_type(seg, s)) {
+               case AREA_PV:
+                       if (!(name = _get_pv_name(f, seg_pv(seg, s))))
+                               return_0;
+
+                       outf(f, "\"%s\", %u%s", name,
+                            seg_pe(seg, s),
+                            (s == seg->area_count - 1) ? "" : ",");
+                       break;
+               case AREA_LV:
+                       outf(f, "\"%s\", %u%s",
+                            seg_lv(seg, s)->name,
+                            seg_le(seg, s),
+                            (s == seg->area_count - 1) ? "" : ",");
+                       break;
+               case AREA_UNASSIGNED:
+                       return 0;
+               }
+       }
+
+       _dec_indent(f);
+       outf(f, "]");
+       return 1;
+}
+
+static int _print_lv(struct formatter *f, struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+       char buffer[4096];
+       int seg_count;
+
+       outnl(f);
+       outf(f, "%s {", lv->name);
+       _inc_indent(f);
+
+       /* FIXME: Write full lvid */
+       if (!id_write_format(&lv->lvid.id[1], buffer, sizeof(buffer)))
+               return_0;
+
+       outf(f, "id = \"%s\"", buffer);
+
+       if (!_print_flag_config(f, lv->status, LV_FLAGS))
+               return_0;
+
+       if (!_out_tags(f, &lv->tags))
+               return_0;
+
+       if (lv->alloc != ALLOC_INHERIT)
+               outf(f, "allocation_policy = \"%s\"",
+                    get_alloc_string(lv->alloc));
+
+       switch (lv->read_ahead) {
+       case DM_READ_AHEAD_NONE:
+               outfc(f, "# None", "read_ahead = -1");
+               break;
+       case DM_READ_AHEAD_AUTO:
+               /* No output - use default */
+               break;
+       default:
+               outf(f, "read_ahead = %u", lv->read_ahead);
+       }
+
+       if (lv->major >= 0)
+               outf(f, "major = %d", lv->major);
+       if (lv->minor >= 0)
+               outf(f, "minor = %d", lv->minor);
+       outf(f, "segment_count = %u", dm_list_size(&lv->segments));
+       outnl(f);
+
+       seg_count = 1;
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (!_print_segment(f, lv->vg, seg_count++, seg))
+                       return_0;
+       }
+
+       _dec_indent(f);
+       outf(f, "}");
+
+       return 1;
+}
+
+static int _print_lvs(struct formatter *f, struct volume_group *vg)
+{
+       struct lv_list *lvl;
+
+       /*
+        * Don't bother with an lv section if there are no lvs.
+        */
+       if (dm_list_empty(&vg->lvs))
+               return 1;
+
+       outf(f, "logical_volumes {");
+       _inc_indent(f);
+
+       /*
+        * Write visible LVs first
+        */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (!(lv_is_visible(lvl->lv)))
+                       continue;
+               if (!_print_lv(f, lvl->lv))
+                       return_0;
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if ((lv_is_visible(lvl->lv)))
+                       continue;
+               if (!_print_lv(f, lvl->lv))
+                       return_0;
+       }
+
+       _dec_indent(f);
+       outf(f, "}");
+
+       return 1;
+}
+
+/*
+ * In the text format we refer to pv's as 'pv1',
+ * 'pv2' etc.  This function builds a hash table
+ * to enable a quick lookup from device -> name.
+ */
+static int _build_pv_names(struct formatter *f, struct volume_group *vg)
+{
+       int count = 0;
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+       char buffer[32], *uuid, *name;
+
+       if (!(f->mem = dm_pool_create("text pv_names", 512)))
+               return_0;
+
+       if (!(f->pv_names = dm_hash_create(128)))
+               return_0;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+
+               /* FIXME But skip if there's already an LV called pv%d ! */
+               if (dm_snprintf(buffer, sizeof(buffer), "pv%d", count++) < 0)
+                       return_0;
+
+               if (!(name = dm_pool_strdup(f->mem, buffer)))
+                       return_0;
+
+               if (!(uuid = dm_pool_zalloc(f->mem, 64)) ||
+                  !id_write_format(&pv->id, uuid, 64))
+                       return_0;
+
+               if (!dm_hash_insert(f->pv_names, uuid, name))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _text_vg_export(struct formatter *f,
+                          struct volume_group *vg, const char *desc)
+{
+       int r = 0;
+
+       if (!_build_pv_names(f, vg))
+               goto_out;
+
+       if (f->header && !_print_header(f, desc))
+               goto_out;
+
+       if (!out_text(f, "%s {", vg->name))
+               goto_out;
+
+       _inc_indent(f);
+
+       if (!_print_vg(f, vg))
+               goto_out;
+
+       outnl(f);
+       if (!_print_pvs(f, vg))
+               goto_out;
+
+       outnl(f);
+       if (!_print_lvs(f, vg))
+               goto_out;
+
+       _dec_indent(f);
+       if (!out_text(f, "}"))
+               goto_out;
+
+       if (!f->header && !_print_header(f, desc))
+               goto_out;
+
+       r = 1;
+
+      out:
+       if (f->mem)
+               dm_pool_destroy(f->mem);
+
+       if (f->pv_names)
+               dm_hash_destroy(f->pv_names);
+
+       return r;
+}
+
+int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp)
+{
+       struct formatter *f;
+       int r;
+
+       _init();
+
+       if (!(f = dm_zalloc(sizeof(*f))))
+               return_0;
+
+       f->data.fp = fp;
+       f->indent = 0;
+       f->header = 1;
+       f->out_with_comment = &_out_with_comment_file;
+       f->nl = &_nl_file;
+
+       r = _text_vg_export(f, vg, desc);
+       if (r)
+               r = !ferror(f->data.fp);
+       dm_free(f);
+       return r;
+}
+
+/* Returns amount of buffer used incl. terminating NUL */
+int text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf)
+{
+       struct formatter *f;
+       int r = 0;
+
+       _init();
+
+       if (!(f = dm_zalloc(sizeof(*f))))
+               return_0;
+
+       f->data.buf.size = 65536;       /* Initial metadata limit */
+       if (!(f->data.buf.start = dm_malloc(f->data.buf.size))) {
+               log_error("text_export buffer allocation failed");
+               goto out;
+       }
+
+       f->indent = 0;
+       f->header = 0;
+       f->out_with_comment = &_out_with_comment_raw;
+       f->nl = &_nl_raw;
+
+       if (!_text_vg_export(f, vg, desc)) {
+               dm_free(f->data.buf.start);
+               goto_out;
+       }
+
+       r = f->data.buf.used + 1;
+       *buf = f->data.buf.start;
+
+      out:
+       dm_free(f);
+       return r;
+}
+
+int export_vg_to_buffer(struct volume_group *vg, char **buf)
+{
+       return text_vg_export_raw(vg, "", buf);
+}
+
+#undef outf
+#undef outnl
diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c
new file mode 100644 (file)
index 0000000..1d2a611
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "lvm-string.h"
+
+/*
+ * Bitsets held in the 'status' flags get
+ * converted into arrays of strings.
+ */
+struct flag {
+       const uint64_t mask;
+       const char *description;
+       int kind;
+};
+
+static const struct flag _vg_flags[] = {
+       {EXPORTED_VG, "EXPORTED", STATUS_FLAG},
+       {RESIZEABLE_VG, "RESIZEABLE", STATUS_FLAG},
+       {PVMOVE, "PVMOVE", STATUS_FLAG},
+       {LVM_READ, "READ", STATUS_FLAG},
+       {LVM_WRITE, "WRITE", STATUS_FLAG},
+       {CLUSTERED, "CLUSTERED", STATUS_FLAG},
+       {SHARED, "SHARED", STATUS_FLAG},
+       {PARTIAL_VG, NULL, 0},
+       {PRECOMMITTED, NULL, 0},
+       {0, NULL, 0}
+};
+
+static const struct flag _pv_flags[] = {
+       {ALLOCATABLE_PV, "ALLOCATABLE", STATUS_FLAG},
+       {EXPORTED_VG, "EXPORTED", STATUS_FLAG},
+       {MISSING_PV, "MISSING", COMPATIBLE_FLAG},
+       {0, NULL, 0}
+};
+
+static const struct flag _lv_flags[] = {
+       {LVM_READ, "READ", STATUS_FLAG},
+       {LVM_WRITE, "WRITE", STATUS_FLAG},
+       {FIXED_MINOR, "FIXED_MINOR", STATUS_FLAG},
+       {VISIBLE_LV, "VISIBLE", STATUS_FLAG},
+       {PVMOVE, "PVMOVE", STATUS_FLAG},
+       {LOCKED, "LOCKED", STATUS_FLAG},
+       {MIRROR_NOTSYNCED, "NOTSYNCED", STATUS_FLAG},
+       {MIRROR_IMAGE, NULL, 0},
+       {MIRROR_LOG, NULL, 0},
+       {MIRRORED, NULL, 0},
+       {VIRTUAL, NULL, 0},
+       {SNAPSHOT, NULL, 0},
+       {MERGING, NULL, 0},
+       {ACTIVATE_EXCL, NULL, 0},
+       {CONVERTING, NULL, 0},
+       {PARTIAL_LV, NULL, 0},
+       {POSTORDER_FLAG, NULL, 0},
+       {VIRTUAL_ORIGIN, NULL, 0},
+       {REPLICATOR, NULL, 0},
+       {REPLICATOR_LOG, NULL, 0},
+       {0, NULL, 0}
+};
+
+static const struct flag *_get_flags(int type)
+{
+       switch (type & ~STATUS_FLAG) {
+       case VG_FLAGS:
+               return _vg_flags;
+
+       case PV_FLAGS:
+               return _pv_flags;
+
+       case LV_FLAGS:
+               return _lv_flags;
+       }
+
+       log_error("Unknown flag set requested.");
+       return NULL;
+}
+
+/*
+ * Converts a bitset to an array of string values,
+ * using one of the tables defined at the top of
+ * the file.
+ */
+int print_flags(uint64_t status, int type, char *buffer, size_t size)
+{
+       int f, first = 1;
+       const struct flag *flags;
+
+       if (!(flags = _get_flags(type)))
+               return_0;
+
+       if (!emit_to_buffer(&buffer, &size, "["))
+               return 0;
+
+       for (f = 0; flags[f].mask; f++) {
+               if (status & flags[f].mask) {
+                       status &= ~flags[f].mask;
+
+                       if ((type & STATUS_FLAG) != flags[f].kind)
+                               continue;
+
+                       /* Internal-only flag? */
+                       if (!flags[f].description)
+                               continue;
+
+                       if (!first) {
+                               if (!emit_to_buffer(&buffer, &size, ", "))
+                                       return 0;
+                       } else
+                               first = 0;
+       
+                       if (!emit_to_buffer(&buffer, &size, "\"%s\"",
+                           flags[f].description))
+                               return 0;
+               }
+       }
+
+       if (!emit_to_buffer(&buffer, &size, "]"))
+               return 0;
+
+       if (status)
+               log_error("Metadata inconsistency: Not all flags successfully "
+                         "exported.");
+
+       return 1;
+}
+
+int read_flags(uint64_t *status, int type, const struct config_value *cv)
+{
+       int f;
+       uint64_t s = UINT64_C(0);
+       const struct flag *flags;
+
+       if (!(flags = _get_flags(type)))
+               return_0;
+
+       if (cv->type == CFG_EMPTY_ARRAY)
+               goto out;
+
+       while (cv) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Status value is not a string.");
+                       return 0;
+               }
+
+               for (f = 0; flags[f].description; f++)
+                       if (!strcmp(flags[f].description, cv->v.str)) {
+                               s |= flags[f].mask;
+                               break;
+                       }
+
+               if (type == VG_FLAGS && !strcmp(cv->v.str, "PARTIAL")) {
+                       /*
+                        * Exception: We no longer write this flag out, but it
+                        * might be encountered in old backup files, so restore
+                        * it in that case. It is never part of live metadata
+                        * though, so only vgcfgrestore needs to be concerned
+                        * by this case.
+                        */
+                       s |= PARTIAL_VG;
+               } else if (!flags[f].description && (type & STATUS_FLAG)) {
+                       log_error("Unknown status flag '%s'.", cv->v.str);
+                       return 0;
+               }
+
+               cv = cv->next;
+       }
+
+      out:
+       *status |= s;
+       return 1;
+}
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
new file mode 100644 (file)
index 0000000..c186757
--- /dev/null
@@ -0,0 +1,2210 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "format-text.h"
+#include "import-export.h"
+#include "device.h"
+#include "lvm-file.h"
+#include "config.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "uuid.h"
+#include "layout.h"
+#include "crc.h"
+#include "xlate.h"
+#include "label.h"
+#include "memlock.h"
+#include "lvmcache.h"
+
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <limits.h>
+#include <dirent.h>
+#include <ctype.h>
+
+static struct format_instance *_text_create_text_instance(const struct format_type
+                                                    *fmt, const char *vgname,
+                                                    const char *vgid,
+                                                    void *context);
+
+struct text_fid_context {
+       char *raw_metadata_buf;
+       uint32_t raw_metadata_buf_size;
+};
+
+struct dir_list {
+       struct dm_list list;
+       char dir[0];
+};
+
+struct raw_list {
+       struct dm_list list;
+       struct device_area dev_area;
+};
+
+struct text_context {
+       char *path_live;        /* Path to file holding live metadata */
+       char *path_edit;        /* Path to file holding edited metadata */
+       char *desc;             /* Description placed inside file */
+};
+
+int rlocn_is_ignored(const struct raw_locn *rlocn)
+{
+       return (rlocn->flags & RAW_LOCN_IGNORED ? 1 : 0);
+}
+
+void rlocn_set_ignored(struct raw_locn *rlocn, unsigned mda_ignored)
+{
+       if (mda_ignored)
+               rlocn->flags |= RAW_LOCN_IGNORED;
+       else
+               rlocn->flags &= ~RAW_LOCN_IGNORED;
+}
+
+/*
+ * NOTE: Currently there can be only one vg per text file.
+ */
+
+static int _text_vg_setup(struct format_instance *fid __attribute__((unused)),
+                         struct volume_group *vg)
+{
+       if (vg->extent_size & (vg->extent_size - 1)) {
+               log_error("Extent size must be power of 2");
+               return 0;
+       }
+
+       return 1;
+}
+
+static uint64_t _mda_free_sectors_raw(struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+
+       return mdac->free_sectors;
+}
+
+static uint64_t _mda_total_sectors_raw(struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+
+       return mdac->area.size >> SECTOR_SHIFT;
+}
+
+/*
+ * Check if metadata area belongs to vg
+ */
+static int _mda_in_vg_raw(struct format_instance *fid __attribute__((unused)),
+                            struct volume_group *vg, struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs)
+               if (pvl->pv->dev == mdac->area.dev)
+                       return 1;
+
+       return 0;
+}
+
+static unsigned _mda_locns_match_raw(struct metadata_area *mda1,
+                                    struct metadata_area *mda2)
+{
+       struct mda_context *mda1c = (struct mda_context *) mda1->metadata_locn;
+       struct mda_context *mda2c = (struct mda_context *) mda2->metadata_locn;
+
+       if ((mda1c->area.dev == mda2c->area.dev) &&
+           (mda1c->area.start == mda2c->area.start) &&
+           (mda1c->area.size == mda2c->area.size))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * For circular region between region_start and region_start + region_size,
+ * back up one SECTOR_SIZE from 'region_ptr' and return the value.
+ * This allows reverse traversal through text metadata area to find old
+ * metadata.
+ *
+ * Parameters:
+ *   region_start: start of the region (bytes)
+ *   region_size: size of the region (bytes)
+ *   region_ptr: pointer within the region (bytes)
+ *   NOTE: region_start <= region_ptr <= region_start + region_size
+ */
+static uint64_t _get_prev_sector_circular(uint64_t region_start,
+                                         uint64_t region_size,
+                                         uint64_t region_ptr)
+{
+       if (region_ptr >= region_start + SECTOR_SIZE)
+               return region_ptr - SECTOR_SIZE;
+       else
+               return (region_start + region_size - SECTOR_SIZE);
+}
+
+/*
+ * Analyze a metadata area for old metadata records in the circular buffer.
+ * This function just looks through and makes a first pass at the data in
+ * the sectors for particular things.
+ * FIXME: do something with each metadata area (try to extract vg, write
+ * raw data to file, etc)
+ */
+static int _pv_analyze_mda_raw (const struct format_type * fmt,
+                               struct metadata_area *mda)
+{
+       struct mda_header *mdah;
+       struct raw_locn *rlocn;
+       uint64_t area_start;
+       uint64_t area_size;
+       uint64_t prev_sector, prev_sector2;
+       uint64_t latest_mrec_offset;
+       uint64_t offset;
+       uint64_t offset2;
+       size_t size;
+       size_t size2;
+       char *buf=NULL;
+       struct device_area *area;
+       struct mda_context *mdac;
+       int r=0;
+
+       mdac = (struct mda_context *) mda->metadata_locn;
+
+       log_print("Found text metadata area: offset=%" PRIu64 ", size=%"
+                 PRIu64, mdac->area.start, mdac->area.size);
+       area = &mdac->area;
+
+       if (!dev_open(area->dev))
+               return_0;
+
+       if (!(mdah = raw_read_mda_header(fmt, area)))
+               goto_out;
+
+       rlocn = mdah->raw_locns;
+
+       /*
+        * The device area includes the metadata header as well as the
+        * records, so remove the metadata header from the start and size
+        */
+       area_start = area->start + MDA_HEADER_SIZE;
+       area_size = area->size - MDA_HEADER_SIZE;
+       latest_mrec_offset = rlocn->offset + area->start;
+
+       /*
+        * Start searching at rlocn (point of live metadata) and go
+        * backwards.
+        */
+       prev_sector = _get_prev_sector_circular(area_start, area_size,
+                                              latest_mrec_offset);
+       offset = prev_sector;
+       size = SECTOR_SIZE;
+       offset2 = size2 = 0;
+
+       while (prev_sector != latest_mrec_offset) {
+               prev_sector2 = prev_sector;
+               prev_sector = _get_prev_sector_circular(area_start, area_size,
+                                                       prev_sector);
+               if (prev_sector > prev_sector2)
+                       goto_out;
+               /*
+                * FIXME: for some reason, the whole metadata region from
+                * area->start to area->start+area->size is not used.
+                * Only ~32KB seems to contain valid metadata records
+                * (LVM2 format - format_text).  As a result, I end up with
+                * "maybe_config_section" returning true when there's no valid
+                * metadata in a sector (sectors with all nulls).
+                */
+               if (!(buf = dm_pool_alloc(fmt->cmd->mem, size + size2)))
+                       goto_out;
+
+               if (!dev_read_circular(area->dev, offset, size,
+                                      offset2, size2, buf))
+                       goto_out;
+
+               /*
+                * FIXME: We could add more sophisticated metadata detection
+                */
+               if (maybe_config_section(buf, size + size2)) {
+                       /* FIXME: Validate region, pull out timestamp?, etc */
+                       /* FIXME: Do something with this region */
+                       log_verbose ("Found LVM2 metadata record at "
+                                    "offset=%"PRIu64", size=%"PRIsize_t", "
+                                    "offset2=%"PRIu64" size2=%"PRIsize_t,
+                                    offset, size, offset2, size2);
+                       offset = prev_sector;
+                       size = SECTOR_SIZE;
+                       offset2 = size2 = 0;
+               } else {
+                       /*
+                        * Not a complete metadata record, assume we have
+                        * metadata and just increase the size and offset.
+                        * Start the second region if the previous sector is
+                        * wrapping around towards the end of the disk.
+                        */
+                       if (prev_sector > offset) {
+                               offset2 = prev_sector;
+                               size2 += SECTOR_SIZE;
+                       } else {
+                               offset = prev_sector;
+                               size += SECTOR_SIZE;
+                       }
+               }
+               dm_pool_free(fmt->cmd->mem, buf);
+               buf = NULL;
+       }
+
+       r = 1;
+ out:
+       if (buf)
+               dm_pool_free(fmt->cmd->mem, buf);
+       if (!dev_close(area->dev))
+               stack;
+       return r;
+}
+
+
+
+static int _text_lv_setup(struct format_instance *fid __attribute__((unused)),
+                         struct logical_volume *lv)
+{
+/******** FIXME Any LV size restriction?
+       uint64_t max_size = UINT_MAX;
+
+       if (lv->size > max_size) {
+               char *dummy = display_size(max_size);
+               log_error("logical volumes cannot be larger than %s", dummy);
+               dm_free(dummy);
+               return 0;
+       }
+*/
+
+       if (!*lv->lvid.s && !lvid_create(&lv->lvid, &lv->vg->id)) {
+               log_error("Random lvid creation failed for %s/%s.",
+                         lv->vg->name, lv->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _xlate_mdah(struct mda_header *mdah)
+{
+       struct raw_locn *rl;
+
+       mdah->version = xlate32(mdah->version);
+       mdah->start = xlate64(mdah->start);
+       mdah->size = xlate64(mdah->size);
+
+       rl = &mdah->raw_locns[0];
+       while (rl->offset) {
+               rl->checksum = xlate32(rl->checksum);
+               rl->offset = xlate64(rl->offset);
+               rl->size = xlate64(rl->size);
+               rl++;
+       }
+}
+
+struct mda_header *raw_read_mda_header(const struct format_type *fmt,
+                                      struct device_area *dev_area)
+{
+       struct mda_header *mdah;
+
+       if (!(mdah = dm_pool_alloc(fmt->cmd->mem, MDA_HEADER_SIZE))) {
+               log_error("struct mda_header allocation failed");
+               return NULL;
+       }
+
+       if (!dev_read(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, mdah))
+               goto_bad;
+
+       if (mdah->checksum_xl != xlate32(calc_crc(INITIAL_CRC, (uint8_t *)mdah->magic,
+                                                 MDA_HEADER_SIZE -
+                                                 sizeof(mdah->checksum_xl)))) {
+               log_error("Incorrect metadata area header checksum on %s"
+                         " at offset %"PRIu64, dev_name(dev_area->dev),
+                         dev_area->start);
+               goto bad;
+       }
+
+       _xlate_mdah(mdah);
+
+       if (strncmp((char *)mdah->magic, FMTT_MAGIC, sizeof(mdah->magic))) {
+               log_error("Wrong magic number in metadata area header on %s"
+                         " at offset %"PRIu64, dev_name(dev_area->dev),
+                         dev_area->start);
+               goto bad;
+       }
+
+       if (mdah->version != FMTT_VERSION) {
+               log_error("Incompatible metadata area header version: %d on %s"
+                         " at offset %"PRIu64, mdah->version,
+                         dev_name(dev_area->dev), dev_area->start);
+               goto bad;
+       }
+
+       if (mdah->start != dev_area->start) {
+               log_error("Incorrect start sector in metadata area header: %"
+                         PRIu64" on %s at offset %"PRIu64, mdah->start,
+                         dev_name(dev_area->dev), dev_area->start);
+               goto bad;
+       }
+
+       return mdah;
+
+bad:
+       dm_pool_free(fmt->cmd->mem, mdah);
+       return NULL;
+}
+
+static int _raw_write_mda_header(const struct format_type *fmt,
+                                struct device *dev,
+                                uint64_t start_byte, struct mda_header *mdah)
+{
+       strncpy((char *)mdah->magic, FMTT_MAGIC, sizeof(mdah->magic));
+       mdah->version = FMTT_VERSION;
+       mdah->start = start_byte;
+
+       _xlate_mdah(mdah);
+       mdah->checksum_xl = xlate32(calc_crc(INITIAL_CRC, (uint8_t *)mdah->magic,
+                                            MDA_HEADER_SIZE -
+                                            sizeof(mdah->checksum_xl)));
+
+       if (!dev_write(dev, start_byte, MDA_HEADER_SIZE, mdah))
+               return_0;
+
+       return 1;
+}
+
+static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area,
+                                      struct mda_header *mdah,
+                                      const char *vgname,
+                                      int *precommitted)
+{
+       size_t len;
+       char vgnamebuf[NAME_LEN + 2] __attribute__((aligned(8)));
+       struct raw_locn *rlocn, *rlocn_precommitted;
+       struct lvmcache_info *info;
+
+       rlocn = mdah->raw_locns;        /* Slot 0 */
+       rlocn_precommitted = rlocn + 1; /* Slot 1 */
+
+       /* Should we use precommitted metadata? */
+       if (*precommitted && rlocn_precommitted->size &&
+           (rlocn_precommitted->offset != rlocn->offset)) {
+               rlocn = rlocn_precommitted;
+       } else
+               *precommitted = 0;
+
+       /* Do not check non-existent metadata. */
+       if (!rlocn->offset && !rlocn->size)
+               return NULL;
+
+       /*
+        * Don't try to check existing metadata
+        * if given vgname is an empty string.
+        */
+       if (!*vgname)
+               return rlocn;
+
+       /* FIXME Loop through rlocns two-at-a-time.  List null-terminated. */
+       /* FIXME Ignore if checksum incorrect!!! */
+       if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset,
+                     sizeof(vgnamebuf), vgnamebuf))
+               goto_bad;
+
+       if (!strncmp(vgnamebuf, vgname, len = strlen(vgname)) &&
+           (isspace(vgnamebuf[len]) || vgnamebuf[len] == '{'))
+               return rlocn;
+       else
+               log_debug("Volume group name found in metadata does "
+                         "not match expected name %s.", vgname);
+
+      bad:
+       if ((info = info_from_pvid(dev_area->dev->pvid, 0)))
+               lvmcache_update_vgname_and_id(info, FMT_TEXT_ORPHAN_VG_NAME,
+                                             FMT_TEXT_ORPHAN_VG_NAME, 0, NULL);
+
+       return NULL;
+}
+
+/*
+ * Determine offset for uncommitted metadata
+ */
+static uint64_t _next_rlocn_offset(struct raw_locn *rlocn,
+                                  struct mda_header *mdah)
+{
+       if (!rlocn)
+               /* Find an empty slot */
+               /* FIXME Assume only one VG per mdah for now */
+               return MDA_HEADER_SIZE;
+
+       /* Start of free space - round up to next sector; circular */
+       return ((rlocn->offset + rlocn->size +
+               (SECTOR_SIZE - rlocn->size % SECTOR_SIZE) -
+               MDA_HEADER_SIZE) % (mdah->size - MDA_HEADER_SIZE))
+              + MDA_HEADER_SIZE;
+}
+
+static int _raw_holds_vgname(struct format_instance *fid,
+                            struct device_area *dev_area, const char *vgname)
+{
+       int r = 0;
+       int noprecommit = 0;
+       struct mda_header *mdah;
+
+       if (!dev_open(dev_area->dev))
+               return_0;
+
+       if (!(mdah = raw_read_mda_header(fid->fmt, dev_area)))
+               return_0;
+
+       if (_find_vg_rlocn(dev_area, mdah, vgname, &noprecommit))
+               r = 1;
+
+       if (!dev_close(dev_area->dev))
+               stack;
+
+       return r;
+}
+
+static struct volume_group *_vg_read_raw_area(struct format_instance *fid,
+                                             const char *vgname,
+                                             struct device_area *area,
+                                             int precommitted)
+{
+       struct volume_group *vg = NULL;
+       struct raw_locn *rlocn;
+       struct mda_header *mdah;
+       time_t when;
+       char *desc;
+       uint32_t wrap = 0;
+
+       if (!(mdah = raw_read_mda_header(fid->fmt, area)))
+               goto_out;
+
+       if (!(rlocn = _find_vg_rlocn(area, mdah, vgname, &precommitted))) {
+               log_debug("VG %s not found on %s", vgname, dev_name(area->dev));
+               goto out;
+       }
+
+       if (rlocn->offset + rlocn->size > mdah->size)
+               wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
+
+       if (wrap > rlocn->offset) {
+               log_error("VG %s metadata too large for circular buffer",
+                         vgname);
+               goto out;
+       }
+
+       /* FIXME 64-bit */
+       if (!(vg = text_vg_import_fd(fid, NULL, area->dev,
+                                    (off_t) (area->start + rlocn->offset),
+                                    (uint32_t) (rlocn->size - wrap),
+                                    (off_t) (area->start + MDA_HEADER_SIZE),
+                                    wrap, calc_crc, rlocn->checksum, &when,
+                                    &desc)))
+               goto_out;
+       log_debug("Read %s %smetadata (%u) from %s at %" PRIu64 " size %"
+                 PRIu64, vg->name, precommitted ? "pre-commit " : "",
+                 vg->seqno, dev_name(area->dev),
+                 area->start + rlocn->offset, rlocn->size);
+
+       if (precommitted)
+               vg->status |= PRECOMMITTED;
+
+      out:
+       return vg;
+}
+
+static struct volume_group *_vg_read_raw(struct format_instance *fid,
+                                        const char *vgname,
+                                        struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct volume_group *vg;
+
+       if (!dev_open(mdac->area.dev))
+               return_NULL;
+
+       vg = _vg_read_raw_area(fid, vgname, &mdac->area, 0);
+
+       if (!dev_close(mdac->area.dev))
+               stack;
+
+       return vg;
+}
+
+static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid,
+                                                  const char *vgname,
+                                                  struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct volume_group *vg;
+
+       if (!dev_open(mdac->area.dev))
+               return_NULL;
+
+       vg = _vg_read_raw_area(fid, vgname, &mdac->area, 1);
+
+       if (!dev_close(mdac->area.dev))
+               stack;
+
+       return vg;
+}
+
+static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
+                        struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
+       struct raw_locn *rlocn;
+       struct mda_header *mdah;
+       struct pv_list *pvl;
+       int r = 0;
+       uint64_t new_wrap = 0, old_wrap = 0, new_end;
+       int found = 0;
+       int noprecommit = 0;
+
+       /* Ignore any mda on a PV outside the VG. vgsplit relies on this */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (pvl->pv->dev == mdac->area.dev) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found)
+               return 1;
+
+       if (!dev_open(mdac->area.dev))
+               return_0;
+
+       if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area)))
+               goto_out;
+
+       rlocn = _find_vg_rlocn(&mdac->area, mdah,
+                       vg->old_name ? vg->old_name : vg->name, &noprecommit);
+       mdac->rlocn.offset = _next_rlocn_offset(rlocn, mdah);
+
+       if (!fidtc->raw_metadata_buf &&
+           !(fidtc->raw_metadata_buf_size =
+                       text_vg_export_raw(vg, "", &fidtc->raw_metadata_buf))) {
+               log_error("VG %s metadata writing failed", vg->name);
+               goto out;
+       }
+
+       mdac->rlocn.size = fidtc->raw_metadata_buf_size;
+
+       if (mdac->rlocn.offset + mdac->rlocn.size > mdah->size)
+               new_wrap = (mdac->rlocn.offset + mdac->rlocn.size) - mdah->size;
+
+       if (rlocn && (rlocn->offset + rlocn->size > mdah->size))
+               old_wrap = (rlocn->offset + rlocn->size) - mdah->size;
+
+       new_end = new_wrap ? new_wrap + MDA_HEADER_SIZE :
+                           mdac->rlocn.offset + mdac->rlocn.size;
+
+       if ((new_wrap && old_wrap) ||
+           (rlocn && (new_wrap || old_wrap) && (new_end > rlocn->offset)) ||
+           (mdac->rlocn.size >= mdah->size)) {
+               log_error("VG %s metadata too large for circular buffer",
+                         vg->name);
+               goto out;
+       }
+
+       log_debug("Writing %s metadata to %s at %" PRIu64 " len %" PRIu64,
+                 vg->name, dev_name(mdac->area.dev), mdac->area.start +
+                 mdac->rlocn.offset, mdac->rlocn.size - new_wrap);
+
+       /* Write text out, circularly */
+       if (!dev_write(mdac->area.dev, mdac->area.start + mdac->rlocn.offset,
+                      (size_t) (mdac->rlocn.size - new_wrap),
+                      fidtc->raw_metadata_buf))
+               goto_out;
+
+       if (new_wrap) {
+               log_debug("Writing metadata to %s at %" PRIu64 " len %" PRIu64,
+                         dev_name(mdac->area.dev), mdac->area.start +
+                         MDA_HEADER_SIZE, new_wrap);
+
+               if (!dev_write(mdac->area.dev,
+                              mdac->area.start + MDA_HEADER_SIZE,
+                              (size_t) new_wrap,
+                              fidtc->raw_metadata_buf +
+                              mdac->rlocn.size - new_wrap))
+                       goto_out;
+       }
+
+       mdac->rlocn.checksum = calc_crc(INITIAL_CRC, (uint8_t *)fidtc->raw_metadata_buf,
+                                       (uint32_t) (mdac->rlocn.size -
+                                                   new_wrap));
+       if (new_wrap)
+               mdac->rlocn.checksum = calc_crc(mdac->rlocn.checksum,
+                                               (uint8_t *)fidtc->raw_metadata_buf +
+                                               mdac->rlocn.size -
+                                               new_wrap, (uint32_t) new_wrap);
+
+       r = 1;
+
+      out:
+       if (!r) {
+               if (!dev_close(mdac->area.dev))
+                       stack;
+
+               if (fidtc->raw_metadata_buf) {
+                       dm_free(fidtc->raw_metadata_buf);
+                       fidtc->raw_metadata_buf = NULL;
+               }
+       }
+
+       return r;
+}
+
+static int _vg_commit_raw_rlocn(struct format_instance *fid,
+                               struct volume_group *vg,
+                               struct metadata_area *mda,
+                               int precommit)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
+       struct mda_header *mdah;
+       struct raw_locn *rlocn;
+       struct pv_list *pvl;
+       int r = 0;
+       int found = 0;
+       int noprecommit = 0;
+
+       /* Ignore any mda on a PV outside the VG. vgsplit relies on this */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (pvl->pv->dev == mdac->area.dev) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found)
+               return 1;
+
+       if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area)))
+               goto_out;
+
+       if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah,
+                                    vg->old_name ? vg->old_name : vg->name,
+                                    &noprecommit))) {
+               mdah->raw_locns[0].offset = 0;
+               mdah->raw_locns[0].size = 0;
+               mdah->raw_locns[0].checksum = 0;
+               mdah->raw_locns[1].offset = 0;
+               mdah->raw_locns[1].size = 0;
+               mdah->raw_locns[1].checksum = 0;
+               mdah->raw_locns[2].offset = 0;
+               mdah->raw_locns[2].size = 0;
+               mdah->raw_locns[2].checksum = 0;
+               rlocn = &mdah->raw_locns[0];
+       }
+
+       if (precommit)
+               rlocn++;
+       else {
+               /* If not precommitting, wipe the precommitted rlocn */
+               mdah->raw_locns[1].offset = 0;
+               mdah->raw_locns[1].size = 0;
+               mdah->raw_locns[1].checksum = 0;
+       }
+
+       /* Is there new metadata to commit? */
+       if (mdac->rlocn.size) {
+               rlocn->offset = mdac->rlocn.offset;
+               rlocn->size = mdac->rlocn.size;
+               rlocn->checksum = mdac->rlocn.checksum;
+               log_debug("%sCommitting %s metadata (%u) to %s header at %"
+                         PRIu64, precommit ? "Pre-" : "", vg->name, vg->seqno,
+                         dev_name(mdac->area.dev), mdac->area.start);
+       } else
+               log_debug("Wiping pre-committed %s metadata from %s "
+                         "header at %" PRIu64, vg->name,
+                         dev_name(mdac->area.dev), mdac->area.start);
+
+       rlocn_set_ignored(mdah->raw_locns, mda_is_ignored(mda));
+       if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
+                                  mdah)) {
+               dm_pool_free(fid->fmt->cmd->mem, mdah);
+               log_error("Failed to write metadata area header");
+               goto out;
+       }
+
+       r = 1;
+
+      out:
+       if (!precommit) {
+               if (!dev_close(mdac->area.dev))
+                       stack;
+               if (fidtc->raw_metadata_buf) {
+                       dm_free(fidtc->raw_metadata_buf);
+                       fidtc->raw_metadata_buf = NULL;
+               }
+       }
+
+       return r;
+}
+
+static int _vg_commit_raw(struct format_instance *fid, struct volume_group *vg,
+                         struct metadata_area *mda)
+{
+       return _vg_commit_raw_rlocn(fid, vg, mda, 0);
+}
+
+static int _vg_precommit_raw(struct format_instance *fid,
+                            struct volume_group *vg,
+                            struct metadata_area *mda)
+{
+       return _vg_commit_raw_rlocn(fid, vg, mda, 1);
+}
+
+/* Close metadata area devices */
+static int _vg_revert_raw(struct format_instance *fid, struct volume_group *vg,
+                         struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct pv_list *pvl;
+       int found = 0;
+
+       /* Ignore any mda on a PV outside the VG. vgsplit relies on this */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (pvl->pv->dev == mdac->area.dev) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found)
+               return 1;
+
+       /* Wipe pre-committed metadata */
+       mdac->rlocn.size = 0;
+       return _vg_commit_raw_rlocn(fid, vg, mda, 0);
+}
+
+static int _vg_remove_raw(struct format_instance *fid, struct volume_group *vg,
+                         struct metadata_area *mda)
+{
+       struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+       struct mda_header *mdah;
+       struct raw_locn *rlocn;
+       int r = 0;
+       int noprecommit = 0;
+
+       if (!dev_open(mdac->area.dev))
+               return_0;
+
+       if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area)))
+               goto_out;
+
+       if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, &noprecommit))) {
+               rlocn = &mdah->raw_locns[0];
+               mdah->raw_locns[1].offset = 0;
+       }
+
+       rlocn->offset = 0;
+       rlocn->size = 0;
+       rlocn->checksum = 0;
+       rlocn_set_ignored(mdah->raw_locns, mda_is_ignored(mda));
+
+       if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
+                                  mdah)) {
+               dm_pool_free(fid->fmt->cmd->mem, mdah);
+               log_error("Failed to write metadata area header");
+               goto out;
+       }
+
+       r = 1;
+
+      out:
+       if (!dev_close(mdac->area.dev))
+               stack;
+
+       return r;
+}
+
+static struct volume_group *_vg_read_file_name(struct format_instance *fid,
+                                              const char *vgname,
+                                              const char *read_path)
+{
+       struct volume_group *vg;
+       time_t when;
+       char *desc;
+
+       if (!(vg = text_vg_import_file(fid, read_path, &when, &desc)))
+               return_NULL;
+
+       /*
+        * Currently you can only have a single volume group per
+        * text file (this restriction may remain).  We need to
+        * check that it contains the correct volume group.
+        */
+       if (vgname && strcmp(vgname, vg->name)) {
+               free_vg(vg);
+               log_error("'%s' does not contain volume group '%s'.",
+                         read_path, vgname);
+               return NULL;
+       } else
+               log_debug("Read volume group %s from %s", vg->name, read_path);
+
+       return vg;
+}
+
+static struct volume_group *_vg_read_file(struct format_instance *fid,
+                                         const char *vgname,
+                                         struct metadata_area *mda)
+{
+       struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+       return _vg_read_file_name(fid, vgname, tc->path_live);
+}
+
+static struct volume_group *_vg_read_precommit_file(struct format_instance *fid,
+                                                   const char *vgname,
+                                                   struct metadata_area *mda)
+{
+       struct text_context *tc = (struct text_context *) mda->metadata_locn;
+       struct volume_group *vg;
+
+       if ((vg = _vg_read_file_name(fid, vgname, tc->path_edit)))
+               vg->status |= PRECOMMITTED;
+       else
+               vg = _vg_read_file_name(fid, vgname, tc->path_live);
+
+       return vg;
+}
+
+static int _vg_write_file(struct format_instance *fid __attribute__((unused)),
+                         struct volume_group *vg, struct metadata_area *mda)
+{
+       struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+       FILE *fp;
+       int fd;
+       char *slash;
+       char temp_file[PATH_MAX], temp_dir[PATH_MAX];
+
+       slash = strrchr(tc->path_edit, '/');
+
+       if (slash == 0)
+               strcpy(temp_dir, ".");
+       else if (slash - tc->path_edit < PATH_MAX) {
+               strncpy(temp_dir, tc->path_edit,
+                       (size_t) (slash - tc->path_edit));
+               temp_dir[slash - tc->path_edit] = '\0';
+
+       } else {
+               log_error("Text format failed to determine directory.");
+               return 0;
+       }
+
+       if (!create_temp_name(temp_dir, temp_file, sizeof(temp_file), &fd,
+                             &vg->cmd->rand_seed)) {
+               log_error("Couldn't create temporary text file name.");
+               return 0;
+       }
+
+       if (!(fp = fdopen(fd, "w"))) {
+               log_sys_error("fdopen", temp_file);
+               if (close(fd))
+                       log_sys_error("fclose", temp_file);
+               return 0;
+       }
+
+       log_debug("Writing %s metadata to %s", vg->name, temp_file);
+
+       if (!text_vg_export_file(vg, tc->desc, fp)) {
+               log_error("Failed to write metadata to %s.", temp_file);
+               if (fclose(fp))
+                       log_sys_error("fclose", temp_file);
+               return 0;
+       }
+
+       if (fsync(fd) && (errno != EROFS) && (errno != EINVAL)) {
+               log_sys_error("fsync", tc->path_edit);
+               if (fclose(fp))
+                       log_sys_error("fclose", tc->path_edit);
+               return 0;
+       }
+
+       if (lvm_fclose(fp, tc->path_edit))
+               return_0;
+
+       if (rename(temp_file, tc->path_edit)) {
+               log_debug("Renaming %s to %s", temp_file, tc->path_edit);
+               log_error("%s: rename to %s failed: %s", temp_file,
+                         tc->path_edit, strerror(errno));
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _vg_commit_file_backup(struct format_instance *fid __attribute__((unused)),
+                                 struct volume_group *vg,
+                                 struct metadata_area *mda)
+{
+       struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+       if (test_mode()) {
+               log_verbose("Test mode: Skipping committing %s metadata (%u)",
+                           vg->name, vg->seqno);
+               if (unlink(tc->path_edit)) {
+                       log_debug("Unlinking %s", tc->path_edit);
+                       log_sys_error("unlink", tc->path_edit);
+                       return 0;
+               }
+       } else {
+               log_debug("Committing %s metadata (%u)", vg->name, vg->seqno);
+               log_debug("Renaming %s to %s", tc->path_edit, tc->path_live);
+               if (rename(tc->path_edit, tc->path_live)) {
+                       log_error("%s: rename to %s failed: %s", tc->path_edit,
+                                 tc->path_live, strerror(errno));
+                       return 0;
+               }
+       }
+
+       sync_dir(tc->path_edit);
+
+       return 1;
+}
+
+static int _vg_commit_file(struct format_instance *fid, struct volume_group *vg,
+                          struct metadata_area *mda)
+{
+       struct text_context *tc = (struct text_context *) mda->metadata_locn;
+       char *slash;
+       char new_name[PATH_MAX];
+       size_t len;
+
+       if (!_vg_commit_file_backup(fid, vg, mda))
+               return 0;
+
+       /* vgrename? */
+       if ((slash = strrchr(tc->path_live, '/')))
+               slash = slash + 1;
+       else
+               slash = tc->path_live;
+
+       if (strcmp(slash, vg->name)) {
+               len = slash - tc->path_live;
+               strncpy(new_name, tc->path_live, len);
+               strcpy(new_name + len, vg->name);
+               log_debug("Renaming %s to %s", tc->path_live, new_name);
+               if (test_mode())
+                       log_verbose("Test mode: Skipping rename");
+               else {
+                       if (rename(tc->path_live, new_name)) {
+                               log_error("%s: rename to %s failed: %s",
+                                         tc->path_live, new_name,
+                                         strerror(errno));
+                               sync_dir(new_name);
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+static int _vg_remove_file(struct format_instance *fid __attribute__((unused)),
+                          struct volume_group *vg __attribute__((unused)),
+                          struct metadata_area *mda)
+{
+       struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+       if (path_exists(tc->path_edit) && unlink(tc->path_edit)) {
+               log_sys_error("unlink", tc->path_edit);
+               return 0;
+       }
+
+       if (path_exists(tc->path_live) && unlink(tc->path_live)) {
+               log_sys_error("unlink", tc->path_live);
+               return 0;
+       }
+
+       sync_dir(tc->path_live);
+
+       return 1;
+}
+
+static int _scan_file(const struct format_type *fmt, const char *vgname)
+{
+       struct dirent *dirent;
+       struct dir_list *dl;
+       struct dm_list *dir_list;
+       char *tmp;
+       DIR *d;
+       struct volume_group *vg;
+       struct format_instance *fid;
+       char path[PATH_MAX];
+       char *scanned_vgname;
+
+       dir_list = &((struct mda_lists *) fmt->private)->dirs;
+
+       dm_list_iterate_items(dl, dir_list) {
+               if (!(d = opendir(dl->dir))) {
+                       log_sys_error("opendir", dl->dir);
+                       continue;
+               }
+               while ((dirent = readdir(d)))
+                       if (strcmp(dirent->d_name, ".") &&
+                           strcmp(dirent->d_name, "..") &&
+                           (!(tmp = strstr(dirent->d_name, ".tmp")) ||
+                            tmp != dirent->d_name + strlen(dirent->d_name)
+                            - 4)) {
+                               scanned_vgname = dirent->d_name;
+
+                               /* If vgname supplied, only scan that one VG */
+                               if (vgname && strcmp(vgname, scanned_vgname))
+                                       continue;
+
+                               if (dm_snprintf(path, PATH_MAX, "%s/%s",
+                                                dl->dir, scanned_vgname) < 0) {
+                                       log_error("Name too long %s/%s",
+                                                 dl->dir, scanned_vgname);
+                                       break;
+                               }
+
+                               /* FIXME stat file to see if it's changed */
+                               fid = _text_create_text_instance(fmt, NULL, NULL,
+                                                           NULL);
+                               if ((vg = _vg_read_file_name(fid, scanned_vgname,
+                                                            path))) {
+                                       /* FIXME Store creation host in vg */
+                                       lvmcache_update_vg(vg, 0);
+                                       free_vg(vg);
+                               }
+                       }
+
+               if (closedir(d))
+                       log_sys_error("closedir", dl->dir);
+       }
+
+       return 1;
+}
+
+const char *vgname_from_mda(const struct format_type *fmt,
+                           struct mda_header *mdah,
+                           struct device_area *dev_area, struct id *vgid,
+                           uint64_t *vgstatus, char **creation_host,
+                           uint64_t *mda_free_sectors)
+{
+       struct raw_locn *rlocn;
+       uint32_t wrap = 0;
+       const char *vgname = NULL;
+       unsigned int len = 0;
+       char buf[NAME_LEN + 1] __attribute__((aligned(8)));
+       char uuid[64] __attribute__((aligned(8)));
+       uint64_t buffer_size, current_usage;
+
+       if (mda_free_sectors)
+               *mda_free_sectors = ((dev_area->size - MDA_HEADER_SIZE) / 2) >> SECTOR_SHIFT;
+
+       if (!mdah) {
+               log_error(INTERNAL_ERROR "vgname_from_mda called with NULL pointer for mda_header");
+               goto_out;
+       }
+
+       /* FIXME Cope with returning a list */
+       rlocn = mdah->raw_locns;
+
+       /*
+        * If no valid offset, do not try to search for vgname
+        */
+       if (!rlocn->offset)
+               goto out;
+
+       /* Do quick check for a vgname */
+       if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset,
+                     NAME_LEN, buf))
+               goto_out;
+
+       while (buf[len] && !isspace(buf[len]) && buf[len] != '{' &&
+              len < (NAME_LEN - 1))
+               len++;
+
+       buf[len] = '\0';
+
+       /* Ignore this entry if the characters aren't permissible */
+       if (!validate_name(buf))
+               goto_out;
+
+       /* We found a VG - now check the metadata */
+       if (rlocn->offset + rlocn->size > mdah->size)
+               wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
+
+       if (wrap > rlocn->offset) {
+               log_error("%s: metadata too large for circular buffer",
+                         dev_name(dev_area->dev));
+               goto out;
+       }
+
+       /* FIXME 64-bit */
+       if (!(vgname = text_vgname_import(fmt, dev_area->dev,
+                                         (off_t) (dev_area->start +
+                                                  rlocn->offset),
+                                         (uint32_t) (rlocn->size - wrap),
+                                         (off_t) (dev_area->start +
+                                                  MDA_HEADER_SIZE),
+                                         wrap, calc_crc, rlocn->checksum,
+                                         vgid, vgstatus, creation_host)))
+               goto_out;
+
+       /* Ignore this entry if the characters aren't permissible */
+       if (!validate_name(vgname)) {
+               vgname = NULL;
+               goto_out;
+       }
+
+       if (!id_write_format(vgid, uuid, sizeof(uuid))) {
+               vgname = NULL;
+               goto_out;
+       }
+
+       log_debug("%s: Found metadata at %" PRIu64 " size %" PRIu64
+                 " (in area at %" PRIu64 " size %" PRIu64
+                 ") for %s (%s)",
+                 dev_name(dev_area->dev), dev_area->start + rlocn->offset,
+                 rlocn->size, dev_area->start, dev_area->size, vgname, uuid);
+
+       if (mda_free_sectors) {
+               current_usage = (rlocn->size + SECTOR_SIZE - UINT64_C(1)) -
+                                (rlocn->size + SECTOR_SIZE - UINT64_C(1)) % SECTOR_SIZE;
+               buffer_size = mdah->size - MDA_HEADER_SIZE;
+
+               if (current_usage * 2 >= buffer_size)
+                       *mda_free_sectors = UINT64_C(0);
+               else
+                       *mda_free_sectors = ((buffer_size - 2 * current_usage) / 2) >> SECTOR_SHIFT;
+       }
+
+      out:
+       return vgname;
+}
+
+static int _scan_raw(const struct format_type *fmt, const char *vgname __attribute__((unused)))
+{
+       struct raw_list *rl;
+       struct dm_list *raw_list;
+       const char *scanned_vgname;
+       struct volume_group *vg;
+       struct format_instance fid;
+       struct id vgid;
+       uint64_t vgstatus;
+       struct mda_header *mdah;
+
+       raw_list = &((struct mda_lists *) fmt->private)->raws;
+
+       fid.fmt = fmt;
+       dm_list_init(&fid.metadata_areas_in_use);
+       dm_list_init(&fid.metadata_areas_ignored);
+
+       dm_list_iterate_items(rl, raw_list) {
+               /* FIXME We're reading mdah twice here... */
+               if (!dev_open(rl->dev_area.dev)) {
+                       stack;
+                       continue;
+               }
+
+               if (!(mdah = raw_read_mda_header(fmt, &rl->dev_area))) {
+                       stack;
+                       goto close_dev;
+               }
+
+               if ((scanned_vgname = vgname_from_mda(fmt, mdah,
+                                             &rl->dev_area, &vgid, &vgstatus,
+                                             NULL, NULL))) {
+                       vg = _vg_read_raw_area(&fid, scanned_vgname, &rl->dev_area, 0);
+                       if (vg)
+                               lvmcache_update_vg(vg, 0);
+
+               }
+       close_dev:
+               if (!dev_close(rl->dev_area.dev))
+                       stack;
+       }
+
+       return 1;
+}
+
+static int _text_scan(const struct format_type *fmt, const char *vgname)
+{
+       return (_scan_file(fmt, vgname) & _scan_raw(fmt, vgname));
+}
+
+/* For orphan, creates new mdas according to policy.
+   Always have an mda between end-of-label and pe_align() boundary */
+static int _mda_setup(const struct format_type *fmt,
+                     uint64_t pe_start, uint64_t pe_end,
+                     int pvmetadatacopies, uint64_t pvmetadatasize,
+                     unsigned metadataignore, struct dm_list *mdas,
+                     struct physical_volume *pv,
+                     struct volume_group *vg __attribute__((unused)))
+{
+       uint64_t mda_adjustment, disk_size, alignment, alignment_offset;
+       uint64_t start1, mda_size1;     /* First area - start of disk */
+       uint64_t start2, mda_size2;     /* Second area - end of disk */
+       uint64_t wipe_size = 8 << SECTOR_SHIFT;
+       size_t pagesize = lvm_getpagesize();
+
+       if (!pvmetadatacopies)
+               return 1;
+
+       alignment = pv->pe_align << SECTOR_SHIFT;
+       alignment_offset = pv->pe_align_offset << SECTOR_SHIFT;
+       disk_size = pv->size << SECTOR_SHIFT;
+       pe_start <<= SECTOR_SHIFT;
+       pe_end <<= SECTOR_SHIFT;
+
+       if (pe_end > disk_size) {
+               log_error("Physical extents end beyond end of device %s!",
+                         pv_dev_name(pv));
+               return 0;
+       }
+
+       /* Requested metadatasize */
+       mda_size1 = pvmetadatasize << SECTOR_SHIFT;
+
+       /* Place mda straight after label area at start of disk */
+       start1 = LABEL_SCAN_SIZE;
+
+       /* Unless the space available is tiny, round to PAGE_SIZE boundary */
+       if ((!pe_start && !pe_end) ||
+           ((pe_start > start1) && (pe_start - start1 >= MDA_SIZE_MIN))) {
+               mda_adjustment = start1 % pagesize;
+               if (mda_adjustment)
+                       start1 += (pagesize - mda_adjustment);
+       }
+
+       /* Round up to pe_align boundary */
+       mda_adjustment = (mda_size1 + start1) % alignment;
+       if (mda_adjustment) {
+               mda_size1 += (alignment - mda_adjustment);
+               /* Revert if it's now too large */
+               if (start1 + mda_size1 > disk_size)
+                       mda_size1 -= (alignment - mda_adjustment);
+       }
+
+       /* Add pe_align_offset if on pe_align boundary */
+       if (alignment_offset &&
+           (((start1 + mda_size1) % alignment) == 0)) {
+               mda_size1 += alignment_offset;
+               /* Revert if it's now too large */
+               if (start1 + mda_size1 > disk_size)
+                       mda_size1 -= alignment_offset;
+       }
+
+       /* Ensure it's not going to be bigger than the disk! */
+       if (start1 + mda_size1 > disk_size) {
+               log_warn("WARNING: metadata area fills disk leaving no "
+                        "space for data on %s.", pv_dev_name(pv));
+               /* Leave some free space for rounding */
+               /* Avoid empty data area as could cause tools problems */
+               mda_size1 = disk_size - start1 - alignment * 2;
+               if (start1 + mda_size1 > disk_size) {
+                       log_error("Insufficient space for first mda on %s",
+                                 pv_dev_name(pv));
+                       return 0;
+               }
+               /* Round up to pe_align boundary */
+               mda_adjustment = (mda_size1 + start1) % alignment;
+               if (mda_adjustment)
+                       mda_size1 += (alignment - mda_adjustment);
+               /* Only have 1 mda in this case */
+               pvmetadatacopies = 1;
+       }
+
+       /* If we already have PEs, avoid overlap */
+       if (pe_start || pe_end) {
+               if (pe_start <= start1)
+                       mda_size1 = 0;
+               else if (start1 + mda_size1 > pe_start)
+                       mda_size1 = pe_start - start1;
+       }
+
+       /* FIXME If creating new mdas, wipe them! */
+       if (mda_size1) {
+               if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start1,
+                            mda_size1, metadataignore))
+                       return 0;
+
+               if (!dev_set((struct device *) pv->dev, start1,
+                            (size_t) (mda_size1 >
+                                      wipe_size ? : mda_size1), 0)) {
+                       log_error("Failed to wipe new metadata area");
+                       return 0;
+               }
+
+               if (pvmetadatacopies == 1)
+                       return 1;
+       } else
+               start1 = 0;
+
+       /* A second copy at end of disk */
+       mda_size2 = pvmetadatasize << SECTOR_SHIFT;
+
+       /* Ensure it's not going to be bigger than the disk! */
+       if (mda_size2 > disk_size)
+               mda_size2 = disk_size - start1 - mda_size1;
+
+       mda_adjustment = (disk_size - mda_size2) % alignment;
+       if (mda_adjustment)
+               mda_size2 += mda_adjustment;
+
+       start2 = disk_size - mda_size2;
+
+       /* If we already have PEs, avoid overlap */
+       if (pe_start || pe_end) {
+               if (start2 < pe_end) {
+                       mda_size2 -= (pe_end - start2);
+                       start2 = pe_end;
+               }
+       }
+
+       /* If we already have a first mda, avoid overlap */
+       if (mda_size1) {
+               if (start2 < start1 + mda_size1) {
+                       mda_size2 -= (start1 + mda_size1 - start2);
+                       start2 = start1 + mda_size1;
+               }
+               /* No room for any PEs here now! */
+       }
+
+       if (mda_size2) {
+               if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start2,
+                            mda_size2, metadataignore)) return 0;
+               if (!dev_set(pv->dev, start2,
+                            (size_t) (mda_size1 >
+                                      wipe_size ? : mda_size1), 0)) {
+                       log_error("Failed to wipe new metadata area");
+                       return 0;
+               }
+       } else
+               return 0;
+
+       return 1;
+}
+
+/* Only for orphans */
+/* Set label_sector to -1 if rewriting existing label into same sector */
+/* If mdas is supplied it overwrites existing mdas e.g. used with pvcreate */
+static int _text_pv_write(const struct format_type *fmt, struct physical_volume *pv,
+                    struct dm_list *mdas, int64_t label_sector)
+{
+       struct label *label;
+       struct lvmcache_info *info;
+       struct mda_context *mdac;
+       struct metadata_area *mda;
+       char buf[MDA_HEADER_SIZE] __attribute__((aligned(8)));
+       struct mda_header *mdah = (struct mda_header *) buf;
+       uint64_t adjustment;
+       struct data_area_list *da;
+
+       /* FIXME Test mode don't update cache? */
+
+       if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
+                                 FMT_TEXT_ORPHAN_VG_NAME, NULL, 0)))
+               return_0;
+       label = info->label;
+
+       if (label_sector != -1)
+               label->sector = label_sector;
+
+       info->device_size = pv->size << SECTOR_SHIFT;
+       info->fmt = fmt;
+
+       /* If mdas supplied, use them regardless of existing ones, */
+       /* otherwise retain existing ones */
+       if (mdas) {
+               if (info->mdas.n)
+                       del_mdas(&info->mdas);
+               else
+                       dm_list_init(&info->mdas);
+               dm_list_iterate_items(mda, mdas) {
+                       mdac = mda->metadata_locn;
+                       log_debug("Creating metadata area on %s at sector %"
+                                 PRIu64 " size %" PRIu64 " sectors",
+                                 dev_name(mdac->area.dev),
+                                 mdac->area.start >> SECTOR_SHIFT,
+                                 mdac->area.size >> SECTOR_SHIFT);
+                       add_mda(fmt, NULL, &info->mdas, mdac->area.dev,
+                               mdac->area.start, mdac->area.size, mda_is_ignored(mda));
+               }
+               /* FIXME Temporary until mda creation supported by tools */
+       } else if (!info->mdas.n) {
+               dm_list_init(&info->mdas);
+       }
+
+       /*
+        * If no pe_start supplied but PV already exists,
+        * get existing value; use-cases include:
+        * - pvcreate on PV without prior pvremove
+        * - vgremove on VG with PV(s) that have pe_start=0 (hacked cfg)
+        */
+       if (info->das.n) {
+               if (!pv->pe_start)
+                       dm_list_iterate_items(da, &info->das)
+                               pv->pe_start = da->disk_locn.offset >> SECTOR_SHIFT;
+               del_das(&info->das);
+       } else
+               dm_list_init(&info->das);
+
+#if 0
+       /*
+        * FIXME: ideally a pre-existing pe_start seen in .pv_write
+        * would always be preserved BUT 'pvcreate on PV without prior pvremove'
+        * could easily cause the pe_start to overlap with the first mda!
+        */
+       if (pv->pe_start) {
+               log_very_verbose("%s: preserving pe_start=%lu",
+                                pv_dev_name(pv), pv->pe_start);
+               goto preserve_pe_start;
+       }
+#endif
+
+       /*
+        * If pe_start is still unset, set it to first aligned
+        * sector after any metadata areas that begin before pe_start.
+        */
+       if (!pv->pe_start) {
+               pv->pe_start = pv->pe_align;
+               if (pv->pe_align_offset)
+                       pv->pe_start += pv->pe_align_offset;
+       }
+       dm_list_iterate_items(mda, &info->mdas) {
+               mdac = (struct mda_context *) mda->metadata_locn;
+               if (pv->dev == mdac->area.dev &&
+                   ((mdac->area.start <= (pv->pe_start << SECTOR_SHIFT)) ||
+                   (mdac->area.start <= lvm_getpagesize() &&
+                    pv->pe_start < (lvm_getpagesize() >> SECTOR_SHIFT))) &&
+                   (mdac->area.start + mdac->area.size >
+                    (pv->pe_start << SECTOR_SHIFT))) {
+                       pv->pe_start = (mdac->area.start + mdac->area.size)
+                           >> SECTOR_SHIFT;
+                       /* Adjust pe_start to: (N * pe_align) + pe_align_offset */
+                       if (pv->pe_align) {
+                               adjustment =
+                               (pv->pe_start - pv->pe_align_offset) % pv->pe_align;
+                               if (adjustment)
+                                       pv->pe_start += (pv->pe_align - adjustment);
+
+                               log_very_verbose("%s: setting pe_start=%" PRIu64
+                                        " (orig_pe_start=%" PRIu64 ", "
+                                        "pe_align=%lu, pe_align_offset=%lu, "
+                                        "adjustment=%" PRIu64 ")",
+                                        pv_dev_name(pv), pv->pe_start,
+                                        (adjustment ?
+                                         pv->pe_start - (pv->pe_align - adjustment) :
+                                         pv->pe_start),
+                                        pv->pe_align, pv->pe_align_offset, adjustment);
+                       }
+               }
+       }
+       if (pv->pe_start >= pv->size) {
+               log_error("Data area is beyond end of device %s!",
+                         pv_dev_name(pv));
+               return 0;
+       }
+
+       /* FIXME: preserve_pe_start: */
+       if (!add_da
+           (NULL, &info->das, pv->pe_start << SECTOR_SHIFT, UINT64_C(0)))
+               return_0;
+
+       if (!dev_open(pv->dev))
+               return_0;
+
+       dm_list_iterate_items(mda, &info->mdas) {
+               mdac = mda->metadata_locn;
+               memset(&buf, 0, sizeof(buf));
+               mdah->size = mdac->area.size;
+               rlocn_set_ignored(mdah->raw_locns, mda_is_ignored(mda));
+               if (!_raw_write_mda_header(fmt, mdac->area.dev,
+                                          mdac->area.start, mdah)) {
+                       if (!dev_close(pv->dev))
+                               stack;
+                       return_0;
+               }
+       }
+
+       if (!label_write(pv->dev, label)) {
+               dev_close(pv->dev);
+               return_0;
+       }
+
+       if (!dev_close(pv->dev))
+               return_0;
+
+       return 1;
+}
+
+static int _add_raw(struct dm_list *raw_list, struct device_area *dev_area)
+{
+       struct raw_list *rl;
+
+       /* Already present? */
+       dm_list_iterate_items(rl, raw_list) {
+               /* FIXME Check size/overlap consistency too */
+               if (rl->dev_area.dev == dev_area->dev &&
+                   rl->dev_area.start == dev_area->start)
+                       return 1;
+       }
+
+       if (!(rl = dm_malloc(sizeof(struct raw_list)))) {
+               log_error("_add_raw allocation failed");
+               return 0;
+       }
+       memcpy(&rl->dev_area, dev_area, sizeof(*dev_area));
+       dm_list_add(raw_list, &rl->list);
+
+       return 1;
+}
+
+static int _get_pv_if_in_vg(struct lvmcache_info *info,
+                           struct physical_volume *pv)
+{
+       if (info->vginfo && info->vginfo->vgname &&
+           !is_orphan_vg(info->vginfo->vgname) &&
+           get_pv_from_vg_by_id(info->fmt, info->vginfo->vgname,
+                                info->vginfo->vgid, info->dev->pvid, pv))
+               return 1;
+
+       return 0;
+}
+
+static int _populate_pv_fields(struct lvmcache_info *info,
+                              struct physical_volume *pv,
+                              int scan_label_only)
+{
+       struct data_area_list *da;
+
+       /* Have we already cached vgname? */
+       if (!scan_label_only && _get_pv_if_in_vg(info, pv))
+               return 1;
+
+       /* Perform full scan (just the first time) and try again */
+       if (!scan_label_only && !memlock() && !full_scan_done()) {
+               lvmcache_label_scan(info->fmt->cmd, 2);
+
+               if (_get_pv_if_in_vg(info, pv))
+                       return 1;
+       }
+
+       /* Orphan */
+       pv->dev = info->dev;
+       pv->fmt = info->fmt;
+       pv->size = info->device_size >> SECTOR_SHIFT;
+       pv->vg_name = FMT_TEXT_ORPHAN_VG_NAME;
+       memcpy(&pv->id, &info->dev->pvid, sizeof(pv->id));
+
+       /* Currently only support exactly one data area */
+       if (dm_list_size(&info->das) != 1) {
+               log_error("Must be exactly one data area (found %d) on PV %s",
+                         dm_list_size(&info->das), dev_name(info->dev));
+               return 0;
+       }
+
+       dm_list_iterate_items(da, &info->das)
+               pv->pe_start = da->disk_locn.offset >> SECTOR_SHIFT;
+
+       return 1;
+}
+
+/*
+ * Copy constructor for a metadata_locn.
+ */
+static void *_metadata_locn_copy_raw(struct dm_pool *mem, void *metadata_locn)
+{
+       struct mda_context *mdac, *mdac_new;
+
+       mdac = (struct mda_context *) metadata_locn;
+       if (!(mdac_new = dm_pool_alloc(mem, sizeof(*mdac_new)))) {
+               log_error("mda_context allocation failed");
+               return NULL;
+       }
+       memcpy(mdac_new, mdac, sizeof(*mdac));
+
+       return mdac_new;
+}
+
+/*
+ * Return a string description of the metadata location.
+ */
+static const char *_metadata_locn_name_raw(void *metadata_locn)
+{
+       struct mda_context *mdac = (struct mda_context *) metadata_locn;
+
+       return dev_name(mdac->area.dev);
+}
+
+static uint64_t _metadata_locn_offset_raw(void *metadata_locn)
+{
+       struct mda_context *mdac = (struct mda_context *) metadata_locn;
+
+       return mdac->area.start;
+}
+
+static int _text_pv_read(const struct format_type *fmt, const char *pv_name,
+                   struct physical_volume *pv, struct dm_list *mdas,
+                   int scan_label_only)
+{
+       struct metadata_area *mda, *mda_new;
+       struct label *label;
+       struct device *dev;
+       struct lvmcache_info *info;
+
+       if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
+               return_0;
+
+       if (!(label_read(dev, &label, UINT64_C(0))))
+               return_0;
+       info = (struct lvmcache_info *) label->info;
+
+       if (!_populate_pv_fields(info, pv, scan_label_only))
+               return 0;
+
+       if (!mdas)
+               return 1;
+
+       /* Add copy of mdas to supplied list */
+       dm_list_iterate_items(mda, &info->mdas) {
+               mda_new = mda_copy(fmt->cmd->mem, mda);
+               if (!mda_new)
+                       return 0;
+               dm_list_add(mdas, &mda_new->list);
+       }
+
+       return 1;
+}
+
+static void _text_destroy_instance(struct format_instance *fid __attribute__((unused)))
+{
+}
+
+static void _free_dirs(struct dm_list *dir_list)
+{
+       struct dm_list *dl, *tmp;
+
+       dm_list_iterate_safe(dl, tmp, dir_list) {
+               dm_list_del(dl);
+               dm_free(dl);
+       }
+}
+
+static void _free_raws(struct dm_list *raw_list)
+{
+       struct dm_list *rl, *tmp;
+
+       dm_list_iterate_safe(rl, tmp, raw_list) {
+               dm_list_del(rl);
+               dm_free(rl);
+       }
+}
+
+static void _text_destroy(struct format_type *fmt)
+{
+       if (fmt->private) {
+               _free_dirs(&((struct mda_lists *) fmt->private)->dirs);
+               _free_raws(&((struct mda_lists *) fmt->private)->raws);
+               dm_free(fmt->private);
+       }
+
+       dm_free(fmt);
+}
+
+static struct metadata_area_ops _metadata_text_file_ops = {
+       .vg_read = _vg_read_file,
+       .vg_read_precommit = _vg_read_precommit_file,
+       .vg_write = _vg_write_file,
+       .vg_remove = _vg_remove_file,
+       .vg_commit = _vg_commit_file
+};
+
+static struct metadata_area_ops _metadata_text_file_backup_ops = {
+       .vg_read = _vg_read_file,
+       .vg_write = _vg_write_file,
+       .vg_remove = _vg_remove_file,
+       .vg_commit = _vg_commit_file_backup
+};
+
+static struct metadata_area_ops _metadata_text_raw_ops = {
+       .vg_read = _vg_read_raw,
+       .vg_read_precommit = _vg_read_precommit_raw,
+       .vg_write = _vg_write_raw,
+       .vg_remove = _vg_remove_raw,
+       .vg_precommit = _vg_precommit_raw,
+       .vg_commit = _vg_commit_raw,
+       .vg_revert = _vg_revert_raw,
+       .mda_metadata_locn_copy = _metadata_locn_copy_raw,
+       .mda_metadata_locn_name = _metadata_locn_name_raw,
+       .mda_metadata_locn_offset = _metadata_locn_offset_raw,
+       .mda_free_sectors = _mda_free_sectors_raw,
+       .mda_total_sectors = _mda_total_sectors_raw,
+       .mda_in_vg = _mda_in_vg_raw,
+       .pv_analyze_mda = _pv_analyze_mda_raw,
+       .mda_locns_match = _mda_locns_match_raw
+};
+
+/* pvmetadatasize in sectors */
+/*
+ * pe_start goal: FIXME -- reality of .pv_write complexity undermines this goal
+ * - In cases where a pre-existing pe_start is provided (pvcreate --restorefile
+ *   and vgconvert): pe_start must not be changed (so pv->pe_start = pe_start).
+ * - In cases where pe_start is 0: leave pv->pe_start as 0 and defer the
+ *   setting of pv->pe_start to .pv_write
+ */
+static int _text_pv_setup(const struct format_type *fmt,
+                         uint64_t pe_start, uint32_t extent_count,
+                         uint32_t extent_size, unsigned long data_alignment,
+                         unsigned long data_alignment_offset,
+                         int pvmetadatacopies, uint64_t pvmetadatasize,
+                         unsigned metadataignore, struct dm_list *mdas,
+                         struct physical_volume *pv, struct volume_group *vg)
+{
+       struct metadata_area *mda, *mda_new, *mda2;
+       struct mda_context *mdac, *mdac2;
+       struct dm_list *pvmdas;
+       struct lvmcache_info *info;
+       int found;
+       uint64_t pe_end = 0;
+       unsigned mda_count = 0;
+       uint64_t mda_size2 = 0;
+       uint64_t pe_count;
+
+       /* FIXME Cope with pvchange */
+       /* FIXME Merge code with _text_create_text_instance */
+
+       /* If new vg, add any further mdas on this PV to the fid's mda list */
+       if (vg) {
+               /* Iterate through all mdas on this PV */
+               if ((info = info_from_pvid(pv->dev->pvid, 0))) {
+                       pvmdas = &info->mdas;
+                       dm_list_iterate_items(mda, pvmdas) {
+                               mda_count++;
+                               mdac =
+                                   (struct mda_context *) mda->metadata_locn;
+
+                               /* FIXME Check it isn't already in use */
+
+                               /* Reduce usable device size */
+                               if (mda_count > 1)
+                                       mda_size2 = mdac->area.size >> SECTOR_SHIFT;
+
+                               /* Ensure it isn't already on list */
+                               found = 0;
+                               dm_list_iterate_items(mda2, mdas) {
+                                       if (mda2->ops !=
+                                           &_metadata_text_raw_ops) continue;
+                                       mdac2 =
+                                           (struct mda_context *)
+                                           mda2->metadata_locn;
+                                       if (!memcmp
+                                           (&mdac2->area, &mdac->area,
+                                            sizeof(mdac->area))) {
+                                               found = 1;
+                                               break;
+                                       }
+                               }
+                               if (found)
+                                       continue;
+
+                               mda_new = mda_copy(fmt->cmd->mem, mda);
+                               if (!mda_new)
+                                       return_0;
+                               dm_list_add(mdas, &mda_new->list);
+                               /* FIXME multiple dev_areas inside area */
+                       }
+               }
+
+               /* FIXME Cope with genuine pe_count 0 */
+
+               /* If missing, estimate pv->size from file-based metadata */
+               if (!pv->size && pv->pe_count)
+                       pv->size = pv->pe_count * (uint64_t) vg->extent_size +
+                                  pv->pe_start + mda_size2;
+
+               /* Recalculate number of extents that will fit */
+               if (!pv->pe_count) {
+                       pe_count = (pv->size - pv->pe_start - mda_size2) /
+                                  vg->extent_size;
+                       if (pe_count > UINT32_MAX) {
+                               log_error("PV %s too large for extent size %s.",
+                                         pv_dev_name(pv),
+                                         display_size(vg->cmd, (uint64_t) vg->extent_size));
+                               return 0;
+                       }
+                       pv->pe_count = (uint32_t) pe_count;
+               }
+
+               /* Unlike LVM1, we don't store this outside a VG */
+               /* FIXME Default from config file? vgextend cmdline flag? */
+               pv->status |= ALLOCATABLE_PV;
+       } else {
+               if (pe_start)
+                       pv->pe_start = pe_start;
+
+               if (!data_alignment)
+                       data_alignment = find_config_tree_int(pv->fmt->cmd,
+                                                     "devices/data_alignment",
+                                                     0) * 2;
+
+               if (set_pe_align(pv, data_alignment) != data_alignment &&
+                   data_alignment) {
+                       log_error("%s: invalid data alignment of "
+                                 "%lu sectors (requested %lu sectors)",
+                                 pv_dev_name(pv), pv->pe_align, data_alignment);
+                       return 0;
+               }
+
+               if (set_pe_align_offset(pv, data_alignment_offset) != data_alignment_offset &&
+                   data_alignment_offset) {
+                       log_error("%s: invalid data alignment offset of "
+                                 "%lu sectors (requested %lu sectors)",
+                                 pv_dev_name(pv), pv->pe_align_offset, data_alignment_offset);
+                       return 0;
+               }
+
+               if (pv->pe_align < pv->pe_align_offset) {
+                       log_error("%s: pe_align (%lu sectors) must not be less "
+                                 "than pe_align_offset (%lu sectors)",
+                                 pv_dev_name(pv), pv->pe_align, pv->pe_align_offset);
+                       return 0;
+               }
+
+               /*
+                * This initialization has a side-effect of allowing
+                * orphaned PVs to be created with the proper alignment.
+                * Setting pv->pe_start here circumvents .pv_write's
+                * "pvcreate on PV without prior pvremove" retreival of
+                * the PV's previous pe_start.
+                * - Without this you get actual != expected pe_start
+                *   failures in the testsuite.
+                */
+               if (!pe_start && pv->pe_start < pv->pe_align)
+                       pv->pe_start = pv->pe_align;
+
+               if (extent_count)
+                       pe_end = pe_start + extent_count * extent_size - 1;
+               if (!_mda_setup(fmt, pe_start, pe_end, pvmetadatacopies,
+                               pvmetadatasize, metadataignore,  mdas, pv, vg))
+                       return_0;
+       }
+
+       return 1;
+}
+
+/* NULL vgname means use only the supplied context e.g. an archive file */
+static struct format_instance *_text_create_text_instance(const struct format_type
+                                                    *fmt, const char *vgname,
+                                                    const char *vgid,
+                                                    void *context)
+{
+       struct format_instance *fid;
+       struct text_fid_context *fidtc;
+       struct metadata_area *mda;
+       struct mda_context *mdac;
+       struct dir_list *dl;
+       struct raw_list *rl;
+       struct dm_list *dir_list, *raw_list;
+       char path[PATH_MAX];
+       struct lvmcache_vginfo *vginfo;
+       struct lvmcache_info *info;
+
+       if (!(fid = dm_pool_alloc(fmt->cmd->mem, sizeof(*fid)))) {
+               log_error("Couldn't allocate format instance object.");
+               return NULL;
+       }
+
+       if (!(fidtc = (struct text_fid_context *)
+                       dm_pool_zalloc(fmt->cmd->mem,sizeof(*fidtc)))) {
+               log_error("Couldn't allocate text_fid_context.");
+               return NULL;
+       }
+
+       fidtc->raw_metadata_buf = NULL;
+       fid->private = (void *) fidtc;
+
+       fid->fmt = fmt;
+       dm_list_init(&fid->metadata_areas_in_use);
+       dm_list_init(&fid->metadata_areas_ignored);
+
+       if (!vgname) {
+               if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda))))
+                       return_NULL;
+               mda->ops = &_metadata_text_file_backup_ops;
+               mda->metadata_locn = context;
+               mda->status = 0;
+               fid_add_mda(fid, mda);
+       } else {
+               dir_list = &((struct mda_lists *) fmt->private)->dirs;
+
+               dm_list_iterate_items(dl, dir_list) {
+                       if (dm_snprintf(path, PATH_MAX, "%s/%s",
+                                        dl->dir, vgname) < 0) {
+                               log_error("Name too long %s/%s", dl->dir,
+                                         vgname);
+                               return NULL;
+                       }
+
+                       context = create_text_context(fmt->cmd, path, NULL);
+                       if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda))))
+                               return_NULL;
+                       mda->ops = &_metadata_text_file_ops;
+                       mda->metadata_locn = context;
+                       mda->status = 0;
+                       fid_add_mda(fid, mda);
+               }
+
+               raw_list = &((struct mda_lists *) fmt->private)->raws;
+
+               dm_list_iterate_items(rl, raw_list) {
+                       /* FIXME Cache this; rescan below if some missing */
+                       if (!_raw_holds_vgname(fid, &rl->dev_area, vgname))
+                               continue;
+
+                       if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda))))
+                               return_NULL;
+
+                       if (!(mdac = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mdac))))
+                               return_NULL;
+                       mda->metadata_locn = mdac;
+                       /* FIXME Allow multiple dev_areas inside area */
+                       memcpy(&mdac->area, &rl->dev_area, sizeof(mdac->area));
+                       mda->ops = &_metadata_text_raw_ops;
+                       mda->status = 0;
+                       /* FIXME MISTAKE? mda->metadata_locn = context; */
+                       fid_add_mda(fid, mda);
+               }
+
+               /* Scan PVs in VG for any further MDAs */
+               lvmcache_label_scan(fmt->cmd, 0);
+               if (!(vginfo = vginfo_from_vgname(vgname, vgid)))
+                       goto_out;
+               dm_list_iterate_items(info, &vginfo->infos) {
+                       if (!fid_add_mdas(fid, &info->mdas))
+                               return_NULL;
+               }
+               /* FIXME Check raw metadata area count - rescan if required */
+       }
+
+      out:
+       return fid;
+}
+
+void *create_text_context(struct cmd_context *cmd, const char *path,
+                         const char *desc)
+{
+       struct text_context *tc;
+       char *tmp;
+
+       if ((tmp = strstr(path, ".tmp")) && (tmp == path + strlen(path) - 4)) {
+               log_error("%s: Volume group filename may not end in .tmp",
+                         path);
+               return NULL;
+       }
+
+       if (!(tc = dm_pool_alloc(cmd->mem, sizeof(*tc))))
+               return_NULL;
+
+       if (!(tc->path_live = dm_pool_strdup(cmd->mem, path)))
+               goto_bad;
+
+       if (!(tc->path_edit = dm_pool_alloc(cmd->mem, strlen(path) + 5)))
+               goto_bad;
+
+       sprintf(tc->path_edit, "%s.tmp", path);
+
+       if (!desc)
+               desc = "";
+
+       if (!(tc->desc = dm_pool_strdup(cmd->mem, desc)))
+               goto_bad;
+
+       return (void *) tc;
+
+      bad:
+       dm_pool_free(cmd->mem, tc);
+
+       log_error("Couldn't allocate text format context object.");
+       return NULL;
+}
+
+static struct format_handler _text_handler = {
+       .scan = _text_scan,
+       .pv_read = _text_pv_read,
+       .pv_setup = _text_pv_setup,
+       .pv_write = _text_pv_write,
+       .vg_setup = _text_vg_setup,
+       .lv_setup = _text_lv_setup,
+       .create_instance = _text_create_text_instance,
+       .destroy_instance = _text_destroy_instance,
+       .destroy = _text_destroy
+};
+
+static int _add_dir(const char *dir, struct dm_list *dir_list)
+{
+       struct dir_list *dl;
+
+       if (dm_create_dir(dir)) {
+               if (!(dl = dm_malloc(sizeof(struct dm_list) + strlen(dir) + 1))) {
+                       log_error("_add_dir allocation failed");
+                       return 0;
+               }
+               log_very_verbose("Adding text format metadata dir: %s", dir);
+               strcpy(dl->dir, dir);
+               dm_list_add(dir_list, &dl->list);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int _get_config_disk_area(struct cmd_context *cmd,
+                                const struct config_node *cn, struct dm_list *raw_list)
+{
+       struct device_area dev_area;
+       const char *id_str;
+       struct id id;
+
+       if (!(cn = cn->child)) {
+               log_error("Empty metadata disk_area section of config file");
+               return 0;
+       }
+
+       if (!get_config_uint64(cn, "start_sector", &dev_area.start)) {
+               log_error("Missing start_sector in metadata disk_area section "
+                         "of config file");
+               return 0;
+       }
+       dev_area.start <<= SECTOR_SHIFT;
+
+       if (!get_config_uint64(cn, "size", &dev_area.size)) {
+               log_error("Missing size in metadata disk_area section "
+                         "of config file");
+               return 0;
+       }
+       dev_area.size <<= SECTOR_SHIFT;
+
+       if (!get_config_str(cn, "id", &id_str)) {
+               log_error("Missing uuid in metadata disk_area section "
+                         "of config file");
+               return 0;
+       }
+
+       if (!id_read_format(&id, id_str)) {
+               log_error("Invalid uuid in metadata disk_area section "
+                         "of config file: %s", id_str);
+               return 0;
+       }
+
+       if (!(dev_area.dev = device_from_pvid(cmd, &id, NULL))) {
+               char buffer[64] __attribute__((aligned(8)));
+
+               if (!id_write_format(&id, buffer, sizeof(buffer)))
+                       log_error("Couldn't find device.");
+               else
+                       log_error("Couldn't find device with uuid '%s'.",
+                                 buffer);
+
+               return 0;
+       }
+
+       return _add_raw(raw_list, &dev_area);
+}
+
+struct format_type *create_text_format(struct cmd_context *cmd)
+{
+       struct format_type *fmt;
+       const struct config_node *cn;
+       const struct config_value *cv;
+       struct mda_lists *mda_lists;
+
+       if (!(fmt = dm_malloc(sizeof(*fmt))))
+               return_NULL;
+
+       fmt->cmd = cmd;
+       fmt->ops = &_text_handler;
+       fmt->name = FMT_TEXT_NAME;
+       fmt->alias = FMT_TEXT_ALIAS;
+       fmt->orphan_vg_name = ORPHAN_VG_NAME(FMT_TEXT_NAME);
+       fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT |
+                       FMT_UNLIMITED_VOLS | FMT_RESIZE_PV |
+                       FMT_UNLIMITED_STRIPESIZE;
+
+       if (!(mda_lists = dm_malloc(sizeof(struct mda_lists)))) {
+               log_error("Failed to allocate dir_list");
+               dm_free(fmt);
+               return NULL;
+       }
+
+       dm_list_init(&mda_lists->dirs);
+       dm_list_init(&mda_lists->raws);
+       mda_lists->file_ops = &_metadata_text_file_ops;
+       mda_lists->raw_ops = &_metadata_text_raw_ops;
+       fmt->private = (void *) mda_lists;
+
+       if (!(fmt->labeller = text_labeller_create(fmt))) {
+               log_error("Couldn't create text label handler.");
+               dm_free(fmt);
+               return NULL;
+       }
+
+       if (!(label_register_handler(FMT_TEXT_NAME, fmt->labeller))) {
+               log_error("Couldn't register text label handler.");
+               dm_free(fmt);
+               return NULL;
+       }
+
+       if ((cn = find_config_tree_node(cmd, "metadata/dirs"))) {
+               for (cv = cn->v; cv; cv = cv->next) {
+                       if (cv->type != CFG_STRING) {
+                               log_error("Invalid string in config file: "
+                                         "metadata/dirs");
+                               goto err;
+                       }
+
+                       if (!_add_dir(cv->v.str, &mda_lists->dirs)) {
+                               log_error("Failed to add %s to text format "
+                                         "metadata directory list ", cv->v.str);
+                               goto err;
+                       }
+                       cmd->independent_metadata_areas = 1;
+               }
+       }
+
+       if ((cn = find_config_tree_node(cmd, "metadata/disk_areas"))) {
+               for (cn = cn->child; cn; cn = cn->sib) {
+                       if (!_get_config_disk_area(cmd, cn, &mda_lists->raws))
+                               goto err;
+                       cmd->independent_metadata_areas = 1;
+               }
+       }
+
+       log_very_verbose("Initialised format: %s", fmt->name);
+
+       return fmt;
+
+      err:
+       _free_dirs(&mda_lists->dirs);
+
+       dm_free(fmt);
+       return NULL;
+}
diff --git a/lib/format_text/format-text.h b/lib/format_text/format-text.h
new file mode 100644 (file)
index 0000000..79365ea
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FORMAT_TEXT_H
+#define _LVM_FORMAT_TEXT_H
+
+#include "lvm-types.h"
+#include "metadata.h"
+
+#define FMT_TEXT_NAME "lvm2"
+#define FMT_TEXT_ALIAS "text"
+#define FMT_TEXT_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_TEXT_NAME)
+
+/*
+ * Archives a vg config.  'retain_days' is the minimum number of
+ * days that an archive file must be held for.  'min_archives' is
+ * the minimum number of archives required to be kept for each
+ * volume group.
+ */
+int archive_vg(struct volume_group *vg,
+              const char *dir,
+              const char *desc, uint32_t retain_days, uint32_t min_archive);
+
+/*
+ * Displays a list of vg backups in a particular archive directory.
+ */
+int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname);
+int archive_list_file(struct cmd_context *cmd, const char *file);
+int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname);
+
+/*
+ * The text format can read and write a volume_group to a file.
+ */
+struct format_type *create_text_format(struct cmd_context *cmd);
+void *create_text_context(struct cmd_context *cmd, const char *path,
+                         const char *desc);
+
+struct labeller *text_labeller_create(const struct format_type *fmt);
+
+int pvhdr_read(struct device *dev, char *buf);
+
+int add_da(struct dm_pool *mem, struct dm_list *das,
+          uint64_t start, uint64_t size);
+void del_das(struct dm_list *das);
+
+int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *mdas,
+           struct device *dev, uint64_t start, uint64_t size, unsigned ignored);
+void del_mdas(struct dm_list *mdas);
+
+#endif
diff --git a/lib/format_text/import-export.h b/lib/format_text/import-export.h
new file mode 100644 (file)
index 0000000..3b56f08
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TEXT_IMPORT_EXPORT_H
+#define _LVM_TEXT_IMPORT_EXPORT_H
+
+#include "config.h"
+#include "lvm-types.h"
+#include "metadata.h"
+
+#include <stdio.h>
+
+/*
+ * Constants to identify files this code can parse.
+ */
+#define CONTENTS_FIELD "contents"
+#define CONTENTS_VALUE "Text Format Volume Group"
+
+#define FORMAT_VERSION_FIELD "version"
+#define FORMAT_VERSION_VALUE 1
+
+/*
+ * VGs, PVs and LVs all have status bitsets, we gather together
+ * common code for reading and writing them.
+ */
+enum {
+       COMPATIBLE_FLAG = 0x0,
+       VG_FLAGS,
+       PV_FLAGS,
+       LV_FLAGS,
+       STATUS_FLAG = 0x8,
+};
+
+struct text_vg_version_ops {
+       int (*check_version) (const struct config_tree * cf);
+       struct volume_group *(*read_vg) (struct format_instance * fid,
+                                        const struct config_tree *cf,
+                                        unsigned use_cached_pvs);
+       void (*read_desc) (struct dm_pool * mem, const struct config_tree *cf,
+                          time_t *when, char **desc);
+       const char *(*read_vgname) (const struct format_type *fmt,
+                                   const struct config_tree *cft,
+                                   struct id *vgid, uint64_t *vgstatus,
+                                   char **creation_host);
+};
+
+struct text_vg_version_ops *text_vg_vsn1_init(void);
+
+int print_flags(uint64_t status, int type, char *buffer, size_t size);
+int read_flags(uint64_t *status, int type, const struct config_value *cv);
+
+char *alloc_printed_tags(struct dm_list *tags);
+int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct config_value *cv);
+
+int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp);
+int text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf);
+struct volume_group *text_vg_import_file(struct format_instance *fid,
+                                        const char *file,
+                                        time_t *when, char **desc);
+struct volume_group *text_vg_import_fd(struct format_instance *fid,
+                                      const char *file,
+                                      struct device *dev,
+                                      off_t offset, uint32_t size,
+                                      off_t offset2, uint32_t size2,
+                                      checksum_fn_t checksum_fn,
+                                      uint32_t checksum,
+                                      time_t *when, char **desc);
+const char *text_vgname_import(const struct format_type *fmt,
+                              struct device *dev,
+                               off_t offset, uint32_t size,
+                               off_t offset2, uint32_t size2,
+                               checksum_fn_t checksum_fn, uint32_t checksum,
+                               struct id *vgid, uint64_t *vgstatus,
+                              char **creation_host);
+
+#endif
diff --git a/lib/format_text/import.c b/lib/format_text/import.c
new file mode 100644 (file)
index 0000000..60f465f
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvmcache.h"
+
+/* FIXME Use tidier inclusion method */
+static struct text_vg_version_ops *(_text_vsn_list[2]);
+
+static int _text_import_initialised = 0;
+
+static void _init_text_import(void)
+{
+       if (_text_import_initialised)
+               return;
+
+       _text_vsn_list[0] = text_vg_vsn1_init();
+       _text_vsn_list[1] = NULL;
+       _text_import_initialised = 1;
+}
+
+const char *text_vgname_import(const struct format_type *fmt,
+                              struct device *dev,
+                              off_t offset, uint32_t size,
+                              off_t offset2, uint32_t size2,
+                              checksum_fn_t checksum_fn, uint32_t checksum,
+                              struct id *vgid, uint64_t *vgstatus,
+                              char **creation_host)
+{
+       struct config_tree *cft;
+       struct text_vg_version_ops **vsn;
+       const char *vgname = NULL;
+
+       _init_text_import();
+
+       if (!(cft = create_config_tree(NULL, 0)))
+               return_NULL;
+
+       if ((!dev && !read_config_file(cft)) ||
+           (dev && !read_config_fd(cft, dev, offset, size,
+                                   offset2, size2, checksum_fn, checksum)))
+               goto_out;
+
+       /*
+        * Find a set of version functions that can read this file
+        */
+       for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
+               if (!(*vsn)->check_version(cft))
+                       continue;
+
+               if (!(vgname = (*vsn)->read_vgname(fmt, cft, vgid, vgstatus,
+                                                  creation_host)))
+                       goto_out;
+
+               break;
+       }
+
+      out:
+       destroy_config_tree(cft);
+       return vgname;
+}
+
+struct volume_group *text_vg_import_fd(struct format_instance *fid,
+                                      const char *file,
+                                      struct device *dev,
+                                      off_t offset, uint32_t size,
+                                      off_t offset2, uint32_t size2,
+                                      checksum_fn_t checksum_fn,
+                                      uint32_t checksum,
+                                      time_t *when, char **desc)
+{
+       struct volume_group *vg = NULL;
+       struct config_tree *cft;
+       struct text_vg_version_ops **vsn;
+
+       _init_text_import();
+
+       *desc = NULL;
+       *when = 0;
+
+       if (!(cft = create_config_tree(file, 0)))
+               return_NULL;
+
+       if ((!dev && !read_config_file(cft)) ||
+           (dev && !read_config_fd(cft, dev, offset, size,
+                                   offset2, size2, checksum_fn, checksum))) {
+               log_error("Couldn't read volume group metadata.");
+               goto out;
+       }
+
+       /*
+        * Find a set of version functions that can read this file
+        */
+       for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
+               if (!(*vsn)->check_version(cft))
+                       continue;
+
+               if (!(vg = (*vsn)->read_vg(fid, cft, 0)))
+                       goto_out;
+
+               (*vsn)->read_desc(vg->vgmem, cft, when, desc);
+               break;
+       }
+
+      out:
+       destroy_config_tree(cft);
+       return vg;
+}
+
+struct volume_group *text_vg_import_file(struct format_instance *fid,
+                                        const char *file,
+                                        time_t *when, char **desc)
+{
+       return text_vg_import_fd(fid, file, NULL, (off_t)0, 0, (off_t)0, 0, NULL, 0,
+                                when, desc);
+}
+
+struct volume_group *import_vg_from_buffer(const char *buf,
+                                           struct format_instance *fid)
+{
+       struct volume_group *vg = NULL;
+       struct config_tree *cft;
+       struct text_vg_version_ops **vsn;
+
+       _init_text_import();
+
+       if (!(cft = create_config_tree_from_string(fid->fmt->cmd, buf)))
+               return_NULL;
+
+       for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
+               if (!(*vsn)->check_version(cft))
+                       continue;
+               /*
+                * The only path to this point uses cached vgmetadata,
+                * so it can use cached PV state too.
+                */
+               if (!(vg = (*vsn)->read_vg(fid, cft, 1)))
+                       stack;
+               break;
+       }
+
+       destroy_config_tree(cft);
+       return vg;
+}
diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c
new file mode 100644 (file)
index 0000000..e5e83d4
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvmcache.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "segtype.h"
+#include "text_import.h"
+#include "defaults.h"
+
+typedef int (*section_fn) (struct format_instance * fid, struct dm_pool * mem,
+                          struct volume_group * vg, const struct config_node * pvn,
+                          const struct config_node * vgn,
+                          struct dm_hash_table * pv_hash,
+                          struct dm_hash_table * lv_hash,
+                          unsigned *scan_done_once,
+                          unsigned report_missing_devices);
+
+#define _read_int32(root, path, result) \
+       get_config_uint32(root, path, (uint32_t *) result)
+
+#define _read_uint32(root, path, result) \
+       get_config_uint32(root, path, result)
+
+#define _read_int64(root, path, result) \
+       get_config_uint64(root, path, result)
+
+/*
+ * Logs an attempt to read an invalid format file.
+ */
+static void _invalid_format(const char *str)
+{
+       log_error("Can't process text format file - %s.", str);
+}
+
+/*
+ * Checks that the config file contains vg metadata, and that it
+ * we recognise the version number,
+ */
+static int _check_version(const struct config_tree *cft)
+{
+       const struct config_node *cn;
+       const struct config_value *cv;
+
+       /*
+        * Check the contents field.
+        */
+       if (!(cn = find_config_node(cft->root, CONTENTS_FIELD))) {
+               _invalid_format("missing contents field");
+               return 0;
+       }
+
+       cv = cn->v;
+       if (!cv || cv->type != CFG_STRING || strcmp(cv->v.str, CONTENTS_VALUE)) {
+               _invalid_format("unrecognised contents field");
+               return 0;
+       }
+
+       /*
+        * Check the version number.
+        */
+       if (!(cn = find_config_node(cft->root, FORMAT_VERSION_FIELD))) {
+               _invalid_format("missing version number");
+               return 0;
+       }
+
+       cv = cn->v;
+       if (!cv || cv->type != CFG_INT || cv->v.i != FORMAT_VERSION_VALUE) {
+               _invalid_format("unrecognised version number");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _is_converting(struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+
+       if (lv->status & MIRRORED) {
+               seg = first_seg(lv);
+               /* Can't use is_temporary_mirror() because the metadata for
+                * seg_lv may not be read in and flags may not be set yet. */
+               if (seg_type(seg, 0) == AREA_LV &&
+                   strstr(seg_lv(seg, 0)->name, MIRROR_SYNC_LAYER))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static int _read_id(struct id *id, const struct config_node *cn, const char *path)
+{
+       const struct config_value *cv;
+
+       if (!(cn = find_config_node(cn, path))) {
+               log_error("Couldn't find uuid.");
+               return 0;
+       }
+
+       cv = cn->v;
+       if (!cv || !cv->v.str) {
+               log_error("uuid must be a string.");
+               return 0;
+       }
+
+       if (!id_read_format(id, cv->v.str)) {
+               log_error("Invalid uuid.");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _read_flag_config(const struct config_node *n, uint64_t *status, int type)
+{
+       const struct config_node *cn;
+       *status = 0;
+
+       if (!(cn = find_config_node(n, "status"))) {
+               log_error("Could not find status flags.");
+               return 0;
+       }
+
+       if (!(read_flags(status, type | STATUS_FLAG, cn->v))) {
+               log_error("Could not read status flags.");
+               return 0;
+       }
+
+       if ((cn = find_config_node(n, "flags"))) {
+               if (!(read_flags(status, type, cn->v))) {
+                       log_error("Could not read flags.");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _read_pv(struct format_instance *fid, struct dm_pool *mem,
+                   struct volume_group *vg, const struct config_node *pvn,
+                   const struct config_node *vgn __attribute__((unused)),
+                   struct dm_hash_table *pv_hash,
+                   struct dm_hash_table *lv_hash __attribute__((unused)),
+                   unsigned *scan_done_once,
+                   unsigned report_missing_devices)
+{
+       struct physical_volume *pv;
+       struct pv_list *pvl;
+       const struct config_node *cn;
+       uint64_t size;
+
+       if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
+           !(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv))))
+               return_0;
+
+       pv = pvl->pv;
+
+       /*
+        * Add the pv to the pv hash for quick lookup when we read
+        * the lv segments.
+        */
+       if (!dm_hash_insert(pv_hash, pvn->key, pv))
+               return_0;
+
+       if (!(pvn = pvn->child)) {
+               log_error("Empty pv section.");
+               return 0;
+       }
+
+       if (!_read_id(&pv->id, pvn, "id")) {
+               log_error("Couldn't read uuid for physical volume.");
+               return 0;
+       }
+
+       /*
+        * Convert the uuid into a device.
+        */
+       if (!(pv->dev = device_from_pvid(fid->fmt->cmd, &pv->id, scan_done_once))) {
+               char buffer[64] __attribute__((aligned(8)));
+
+               if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
+                       buffer[0] = '\0';
+               if (report_missing_devices)
+                       log_error_once("Couldn't find device with uuid %s.", buffer);
+               else
+                       log_very_verbose("Couldn't find device with uuid %s.", buffer);
+       }
+
+       if (!(pv->vg_name = dm_pool_strdup(mem, vg->name)))
+               return_0;
+
+       memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
+
+       if (!_read_flag_config(pvn, &pv->status, PV_FLAGS)) {
+               log_error("Couldn't read status flags for physical volume.");
+               return 0;
+       }
+
+       if (!pv->dev)
+               pv->status |= MISSING_PV;
+
+       /* Late addition */
+       _read_int64(pvn, "dev_size", &pv->size);
+
+       if (!_read_int64(pvn, "pe_start", &pv->pe_start)) {
+               log_error("Couldn't read extent size for physical volume.");
+               return 0;
+       }
+
+       if (!_read_int32(pvn, "pe_count", &pv->pe_count)) {
+               log_error("Couldn't find extent count (pe_count) for "
+                         "physical volume.");
+               return 0;
+       }
+
+       dm_list_init(&pv->tags);
+       dm_list_init(&pv->segments);
+
+       /* Optional tags */
+       if ((cn = find_config_node(pvn, "tags")) &&
+           !(read_tags(mem, &pv->tags, cn->v))) {
+               log_error("Couldn't read tags for physical volume %s in %s.",
+                         pv_dev_name(pv), vg->name);
+               return 0;
+       }
+
+       pv->pe_size = vg->extent_size;
+
+       pv->pe_alloc_count = 0;
+       pv->pe_align = 0;
+       pv->fmt = fid->fmt;
+
+       /* Fix up pv size if missing or impossibly large */
+       if ((!pv->size || pv->size > (1ULL << 62)) && pv->dev) {
+               if (!dev_get_size(pv->dev, &pv->size)) {
+                       log_error("%s: Couldn't get size.", pv_dev_name(pv));
+                       return 0;
+               }
+               log_verbose("Fixing up missing size (%s) "
+                           "for PV %s", display_size(fid->fmt->cmd, pv->size),
+                           pv_dev_name(pv));
+               if (vg) {
+                       size = pv->pe_count * (uint64_t) vg->extent_size +
+                              pv->pe_start;
+                       if (size > pv->size)
+                               log_warn("WARNING: Physical Volume %s is too "
+                                        "large for underlying device",
+                                        pv_dev_name(pv));
+               }
+       }
+
+       if (!alloc_pv_segment_whole_pv(mem, pv))
+               return_0;
+
+       vg->extent_count += pv->pe_count;
+       vg->free_count += pv->pe_count;
+       add_pvl_to_vgs(vg, pvl);
+
+       return 1;
+}
+
+static void _insert_segment(struct logical_volume *lv, struct lv_segment *seg)
+{
+       struct lv_segment *comp;
+
+       dm_list_iterate_items(comp, &lv->segments) {
+               if (comp->le > seg->le) {
+                       dm_list_add(&comp->list, &seg->list);
+                       return;
+               }
+       }
+
+       lv->le_count += seg->len;
+       dm_list_add(&lv->segments, &seg->list);
+}
+
+static int _read_segment(struct dm_pool *mem, struct volume_group *vg,
+                        struct logical_volume *lv, const struct config_node *sn,
+                        struct dm_hash_table *pv_hash)
+{
+       uint32_t area_count = 0u;
+       struct lv_segment *seg;
+       const struct config_node *cn, *sn_child = sn->child;
+       const struct config_value *cv;
+       uint32_t start_extent, extent_count;
+       struct segment_type *segtype;
+       const char *segtype_str;
+
+       if (!sn_child) {
+               log_error("Empty segment section.");
+               return 0;
+       }
+
+       if (!_read_int32(sn_child, "start_extent", &start_extent)) {
+               log_error("Couldn't read 'start_extent' for segment '%s' "
+                         "of logical volume %s.", sn->key, lv->name);
+               return 0;
+       }
+
+       if (!_read_int32(sn_child, "extent_count", &extent_count)) {
+               log_error("Couldn't read 'extent_count' for segment '%s' "
+                         "of logical volume %s.", sn->key, lv->name);
+               return 0;
+       }
+
+       segtype_str = "striped";
+
+       if ((cn = find_config_node(sn_child, "type"))) {
+               cv = cn->v;
+               if (!cv || !cv->v.str) {
+                       log_error("Segment type must be a string.");
+                       return 0;
+               }
+               segtype_str = cv->v.str;
+       }
+
+       if (!(segtype = get_segtype_from_string(vg->cmd, segtype_str)))
+               return_0;
+
+       if (segtype->ops->text_import_area_count &&
+           !segtype->ops->text_import_area_count(sn_child, &area_count))
+               return_0;
+
+       if (!(seg = alloc_lv_segment(mem, segtype, lv, start_extent,
+                                    extent_count, 0, 0, NULL, area_count,
+                                    extent_count, 0, 0, 0, NULL))) {
+               log_error("Segment allocation failed");
+               return 0;
+       }
+
+       if (seg->segtype->ops->text_import &&
+           !seg->segtype->ops->text_import(seg, sn_child, pv_hash))
+               return_0;
+
+       /* Optional tags */
+       if ((cn = find_config_node(sn_child, "tags")) &&
+           !(read_tags(mem, &seg->tags, cn->v))) {
+               log_error("Couldn't read tags for a segment of %s/%s.",
+                         vg->name, lv->name);
+               return 0;
+       }
+
+       /*
+        * Insert into correct part of segment list.
+        */
+       _insert_segment(lv, seg);
+
+       if (seg_is_mirrored(seg))
+               lv->status |= MIRRORED;
+
+       if (seg_is_virtual(seg))
+               lv->status |= VIRTUAL;
+
+       if (_is_converting(lv))
+               lv->status |= CONVERTING;
+
+       return 1;
+}
+
+int text_import_areas(struct lv_segment *seg, const struct config_node *sn,
+                     const struct config_node *cn, struct dm_hash_table *pv_hash,
+                     uint64_t status)
+{
+       unsigned int s;
+       const struct config_value *cv;
+       struct logical_volume *lv1;
+       struct physical_volume *pv;
+       const char *seg_name = config_parent_name(sn);
+
+       if (!seg->area_count) {
+               log_error("Zero areas not allowed for segment %s", seg_name);
+               return 0;
+       }
+
+       for (cv = cn->v, s = 0; cv && s < seg->area_count; s++, cv = cv->next) {
+
+               /* first we read the pv */
+               if (cv->type != CFG_STRING) {
+                       log_error("Bad volume name in areas array for segment %s.", seg_name);
+                       return 0;
+               }
+
+               if (!cv->next) {
+                       log_error("Missing offset in areas array for segment %s.", seg_name);
+                       return 0;
+               }
+
+               if (cv->next->type != CFG_INT) {
+                       log_error("Bad offset in areas array for segment %s.", seg_name);
+                       return 0;
+               }
+
+               /* FIXME Cope if LV not yet read in */
+               if ((pv = dm_hash_lookup(pv_hash, cv->v.str))) {
+                       if (!set_lv_segment_area_pv(seg, s, pv, (uint32_t) cv->next->v.i))
+                               return_0;
+               } else if ((lv1 = find_lv(seg->lv->vg, cv->v.str))) {
+                       if (!set_lv_segment_area_lv(seg, s, lv1,
+                                                   (uint32_t) cv->next->v.i,
+                                                   status))
+                               return_0;
+               } else {
+                       log_error("Couldn't find volume '%s' "
+                                 "for segment '%s'.",
+                                 cv->v.str ? : "NULL", seg_name);
+                       return 0;
+               }
+
+               cv = cv->next;
+       }
+
+       /*
+        * Check we read the correct number of stripes.
+        */
+       if (cv || (s < seg->area_count)) {
+               log_error("Incorrect number of areas in area array "
+                         "for segment '%s'.", seg_name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _read_segments(struct dm_pool *mem, struct volume_group *vg,
+                         struct logical_volume *lv, const struct config_node *lvn,
+                         struct dm_hash_table *pv_hash)
+{
+       const struct config_node *sn;
+       int count = 0, seg_count;
+
+       for (sn = lvn; sn; sn = sn->sib) {
+
+               /*
+                * All sub-sections are assumed to be segments.
+                */
+               if (!sn->v) {
+                       if (!_read_segment(mem, vg, lv, sn, pv_hash))
+                               return_0;
+
+                       count++;
+               }
+               /* FIXME Remove this restriction */
+               if ((lv->status & SNAPSHOT) && count > 1) {
+                       log_error("Only one segment permitted for snapshot");
+                       return 0;
+               }
+       }
+
+       if (!_read_int32(lvn, "segment_count", &seg_count)) {
+               log_error("Couldn't read segment count for logical volume %s.",
+                         lv->name);
+               return 0;
+       }
+
+       if (seg_count != count) {
+               log_error("segment_count and actual number of segments "
+                         "disagree for logical volume %s.", lv->name);
+               return 0;
+       }
+
+       /*
+        * Check there are no gaps or overlaps in the lv.
+        */
+       if (!check_lv_segments(lv, 0))
+               return_0;
+
+       /*
+        * Merge segments in case someones been editing things by hand.
+        */
+       if (!lv_merge_segments(lv))
+               return_0;
+
+       return 1;
+}
+
+static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
+                        struct dm_pool *mem,
+                        struct volume_group *vg, const struct config_node *lvn,
+                        const struct config_node *vgn __attribute__((unused)),
+                        struct dm_hash_table *pv_hash __attribute__((unused)),
+                        struct dm_hash_table *lv_hash,
+                        unsigned *scan_done_once __attribute__((unused)),
+                        unsigned report_missing_devices __attribute__((unused)))
+{
+       struct logical_volume *lv;
+       const struct config_node *cn;
+
+       if (!(lv = alloc_lv(mem)))
+               return_0;
+
+       if (!(lv->name = dm_pool_strdup(mem, lvn->key)))
+               return_0;
+
+       if (!(lvn = lvn->child)) {
+               log_error("Empty logical volume section.");
+               return 0;
+       }
+
+       if (!_read_flag_config(lvn, &lv->status, LV_FLAGS)) {
+               log_error("Couldn't read status flags for logical volume %s.",
+                         lv->name);
+               return 0;
+       }
+
+       lv->alloc = ALLOC_INHERIT;
+       if ((cn = find_config_node(lvn, "allocation_policy"))) {
+               const struct config_value *cv = cn->v;
+               if (!cv || !cv->v.str) {
+                       log_error("allocation_policy must be a string.");
+                       return 0;
+               }
+
+               lv->alloc = get_alloc_from_string(cv->v.str);
+               if (lv->alloc == ALLOC_INVALID) {
+                       log_warn("WARNING: Ignoring unrecognised allocation policy %s for LV %s", cv->v.str, lv->name);
+                       lv->alloc = ALLOC_INHERIT;
+               }
+       }
+
+       if (!_read_int32(lvn, "read_ahead", &lv->read_ahead))
+               /* If not present, choice of auto or none is configurable */
+               lv->read_ahead = vg->cmd->default_settings.read_ahead;
+       else {
+               switch (lv->read_ahead) {
+               case 0:
+                       lv->read_ahead = DM_READ_AHEAD_AUTO;
+                       break;
+               case (uint32_t) -1:
+                       lv->read_ahead = DM_READ_AHEAD_NONE;
+                       break;
+               default:
+                       ;
+               }
+       }
+
+       /* Optional tags */
+       if ((cn = find_config_node(lvn, "tags")) &&
+           !(read_tags(mem, &lv->tags, cn->v))) {
+               log_error("Couldn't read tags for logical volume %s/%s.",
+                         vg->name, lv->name);
+               return 0;
+       }
+
+       if (!dm_hash_insert(lv_hash, lv->name, lv))
+               return_0;
+
+       return link_lv_to_vg(vg, lv);
+}
+
+static int _read_lvsegs(struct format_instance *fid __attribute__((unused)),
+                       struct dm_pool *mem,
+                       struct volume_group *vg, const struct config_node *lvn,
+                       const struct config_node *vgn __attribute__((unused)),
+                       struct dm_hash_table *pv_hash,
+                       struct dm_hash_table *lv_hash,
+                       unsigned *scan_done_once __attribute__((unused)),
+                       unsigned report_missing_devices __attribute__((unused)))
+{
+       struct logical_volume *lv;
+
+       if (!(lv = dm_hash_lookup(lv_hash, lvn->key))) {
+               log_error("Lost logical volume reference %s", lvn->key);
+               return 0;
+       }
+
+       if (!(lvn = lvn->child)) {
+               log_error("Empty logical volume section.");
+               return 0;
+       }
+
+       /* FIXME: read full lvid */
+       if (!_read_id(&lv->lvid.id[1], lvn, "id")) {
+               log_error("Couldn't read uuid for logical volume %s.",
+                         lv->name);
+               return 0;
+       }
+
+       memcpy(&lv->lvid.id[0], &lv->vg->id, sizeof(lv->lvid.id[0]));
+
+       if (!_read_segments(mem, vg, lv, lvn, pv_hash))
+               return_0;
+
+       lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size;
+
+       lv->minor = -1;
+       if ((lv->status & FIXED_MINOR) &&
+           !_read_int32(lvn, "minor", &lv->minor)) {
+               log_error("Couldn't read minor number for logical "
+                         "volume %s.", lv->name);
+               return 0;
+       }
+
+       lv->major = -1;
+       if ((lv->status & FIXED_MINOR) &&
+           !_read_int32(lvn, "major", &lv->major)) {
+               log_error("Couldn't read major number for logical "
+                         "volume %s.", lv->name);
+       }
+
+       return 1;
+}
+
+static int _read_sections(struct format_instance *fid,
+                         const char *section, section_fn fn,
+                         struct dm_pool *mem,
+                         struct volume_group *vg, const struct config_node *vgn,
+                         struct dm_hash_table *pv_hash,
+                         struct dm_hash_table *lv_hash,
+                         int optional,
+                         unsigned *scan_done_once)
+{
+       const struct config_node *n;
+       /* Only report missing devices when doing a scan */
+       unsigned report_missing_devices = scan_done_once ? !*scan_done_once : 1;
+
+       if (!(n = find_config_node(vgn, section))) {
+               if (!optional) {
+                       log_error("Couldn't find section '%s'.", section);
+                       return 0;
+               }
+
+               return 1;
+       }
+
+       for (n = n->child; n; n = n->sib) {
+               if (!fn(fid, mem, vg, n, vgn, pv_hash, lv_hash,
+                       scan_done_once, report_missing_devices))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static struct volume_group *_read_vg(struct format_instance *fid,
+                                    const struct config_tree *cft,
+                                    unsigned use_cached_pvs)
+{
+       const struct config_node *vgn, *cn;
+       struct volume_group *vg;
+       struct dm_hash_table *pv_hash = NULL, *lv_hash = NULL;
+       struct dm_pool *mem = dm_pool_create("lvm2 vg_read", VG_MEMPOOL_CHUNK);
+       unsigned scan_done_once = use_cached_pvs;
+
+       if (!mem)
+               return_NULL;
+
+       /* skip any top-level values */
+       for (vgn = cft->root; (vgn && vgn->v); vgn = vgn->sib)
+               ;
+
+       if (!vgn) {
+               log_error("Couldn't find volume group in file.");
+               goto bad;
+       }
+
+       if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
+               goto_bad;
+
+       vg->vgmem = mem;
+       vg->cmd = fid->fmt->cmd;
+
+       /* FIXME Determine format type from file contents */
+       /* eg Set to instance of fmt1 here if reading a format1 backup? */
+       vg->fid = fid;
+
+       if (!(vg->name = dm_pool_strdup(mem, vgn->key)))
+               goto_bad;
+
+       if (!(vg->system_id = dm_pool_zalloc(mem, NAME_LEN)))
+               goto_bad;
+
+       vgn = vgn->child;
+
+       if ((cn = find_config_node(vgn, "system_id")) && cn->v) {
+               if (!cn->v->v.str) {
+                       log_error("system_id must be a string");
+                       goto bad;
+               }
+               strncpy(vg->system_id, cn->v->v.str, NAME_LEN);
+       }
+
+       if (!_read_id(&vg->id, vgn, "id")) {
+               log_error("Couldn't read uuid for volume group %s.", vg->name);
+               goto bad;
+       }
+
+       if (!_read_int32(vgn, "seqno", &vg->seqno)) {
+               log_error("Couldn't read 'seqno' for volume group %s.",
+                         vg->name);
+               goto bad;
+       }
+
+       if (!_read_flag_config(vgn, &vg->status, VG_FLAGS)) {
+               log_error("Error reading flags of volume group %s.",
+                         vg->name);
+               goto bad;
+       }
+
+       if (!_read_int32(vgn, "extent_size", &vg->extent_size)) {
+               log_error("Couldn't read extent size for volume group %s.",
+                         vg->name);
+               goto bad;
+       }
+
+       /*
+        * 'extent_count' and 'free_count' get filled in
+        * implicitly when reading in the pv's and lv's.
+        */
+
+       if (!_read_int32(vgn, "max_lv", &vg->max_lv)) {
+               log_error("Couldn't read 'max_lv' for volume group %s.",
+                         vg->name);
+               goto bad;
+       }
+
+       if (!_read_int32(vgn, "max_pv", &vg->max_pv)) {
+               log_error("Couldn't read 'max_pv' for volume group %s.",
+                         vg->name);
+               goto bad;
+       }
+
+       vg->alloc = ALLOC_NORMAL;
+       if ((cn = find_config_node(vgn, "allocation_policy"))) {
+               const struct config_value *cv = cn->v;
+               if (!cv || !cv->v.str) {
+                       log_error("allocation_policy must be a string.");
+                       goto bad;
+               }
+
+               vg->alloc = get_alloc_from_string(cv->v.str);
+               if (vg->alloc == ALLOC_INVALID) {
+                       log_warn("WARNING: Ignoring unrecognised allocation policy %s for VG %s", cv->v.str, vg->name);
+                       vg->alloc = ALLOC_NORMAL;
+               }
+       }
+
+       if (!_read_uint32(vgn, "metadata_copies", &vg->mda_copies)) {
+               vg->mda_copies = DEFAULT_VGMETADATACOPIES;
+       }
+
+       /*
+        * The pv hash memorises the pv section names -> pv
+        * structures.
+        */
+       if (!(pv_hash = dm_hash_create(32))) {
+               log_error("Couldn't create hash table.");
+               goto bad;
+       }
+
+       dm_list_init(&vg->pvs);
+       if (!_read_sections(fid, "physical_volumes", _read_pv, mem, vg,
+                           vgn, pv_hash, lv_hash, 0, &scan_done_once)) {
+               log_error("Couldn't find all physical volumes for volume "
+                         "group %s.", vg->name);
+               goto bad;
+       }
+
+       dm_list_init(&vg->lvs);
+       dm_list_init(&vg->tags);
+       dm_list_init(&vg->removed_pvs);
+
+       /* Optional tags */
+       if ((cn = find_config_node(vgn, "tags")) &&
+           !(read_tags(mem, &vg->tags, cn->v))) {
+               log_error("Couldn't read tags for volume group %s.", vg->name);
+               goto bad;
+       }
+
+       /*
+        * The lv hash memorises the lv section names -> lv
+        * structures.
+        */
+       if (!(lv_hash = dm_hash_create(32))) {
+               log_error("Couldn't create hash table.");
+               goto bad;
+       }
+
+       if (!_read_sections(fid, "logical_volumes", _read_lvnames, mem, vg,
+                           vgn, pv_hash, lv_hash, 1, NULL)) {
+               log_error("Couldn't read all logical volume names for volume "
+                         "group %s.", vg->name);
+               goto bad;
+       }
+
+       if (!_read_sections(fid, "logical_volumes", _read_lvsegs, mem, vg,
+                           vgn, pv_hash, lv_hash, 1, NULL)) {
+               log_error("Couldn't read all logical volumes for "
+                         "volume group %s.", vg->name);
+               goto bad;
+       }
+
+       if (!fixup_imported_mirrors(vg)) {
+               log_error("Failed to fixup mirror pointers after import for "
+                         "volume group %s.", vg->name);
+               goto bad;
+       }
+
+       dm_hash_destroy(pv_hash);
+       dm_hash_destroy(lv_hash);
+
+       /*
+        * Finished.
+        */
+       return vg;
+
+      bad:
+       if (pv_hash)
+               dm_hash_destroy(pv_hash);
+
+       if (lv_hash)
+               dm_hash_destroy(lv_hash);
+
+       dm_pool_destroy(mem);
+       return NULL;
+}
+
+static void _read_desc(struct dm_pool *mem,
+                      const struct config_tree *cft, time_t *when, char **desc)
+{
+       const char *d;
+       unsigned int u = 0u;
+       int old_suppress;
+
+       old_suppress = log_suppress(1);
+       d = find_config_str(cft->root, "description", "");
+       log_suppress(old_suppress);
+       *desc = dm_pool_strdup(mem, d);
+
+       get_config_uint32(cft->root, "creation_time", &u);
+       *when = u;
+}
+
+static const char *_read_vgname(const struct format_type *fmt,
+                               const struct config_tree *cft, struct id *vgid,
+                               uint64_t *vgstatus, char **creation_host)
+{
+       const struct config_node *vgn;
+       struct dm_pool *mem = fmt->cmd->mem;
+       char *vgname;
+       int old_suppress;
+
+       old_suppress = log_suppress(2);
+       *creation_host = dm_pool_strdup(mem,
+                                       find_config_str(cft->root,
+                                                       "creation_host", ""));
+       log_suppress(old_suppress);
+
+       /* skip any top-level values */
+       for (vgn = cft->root; (vgn && vgn->v); vgn = vgn->sib) ;
+
+       if (!vgn) {
+               log_error("Couldn't find volume group in file.");
+               return 0;
+       }
+
+       if (!(vgname = dm_pool_strdup(mem, vgn->key)))
+               return_0;
+
+       vgn = vgn->child;
+
+       if (!_read_id(vgid, vgn, "id")) {
+               log_error("Couldn't read uuid for volume group %s.", vgname);
+               return 0;
+       }
+
+       if (!_read_flag_config(vgn, vgstatus, VG_FLAGS)) {
+               log_error("Couldn't find status flags for volume group %s.",
+                         vgname);
+               return 0;
+       }
+
+       return vgname;
+}
+
+static struct text_vg_version_ops _vsn1_ops = {
+       .check_version = _check_version,
+       .read_vg = _read_vg,
+       .read_desc = _read_desc,
+       .read_vgname = _read_vgname,
+};
+
+struct text_vg_version_ops *text_vg_vsn1_init(void)
+{
+       return &_vsn1_ops;
+}
diff --git a/lib/format_text/layout.h b/lib/format_text/layout.h
new file mode 100644 (file)
index 0000000..a3141e0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TEXT_LAYOUT_H
+#define _LVM_TEXT_LAYOUT_H
+
+#include "config.h"
+#include "lvm-types.h"
+#include "metadata.h"
+#include "uuid.h"
+
+/* On disk */
+struct disk_locn {
+       uint64_t offset;        /* Offset in bytes to start sector */
+       uint64_t size;          /* Bytes */
+} __attribute__ ((packed));
+
+/* Data areas (holding PEs) */
+struct data_area_list {
+       struct dm_list list;
+       struct disk_locn disk_locn;
+};
+
+/* Fields with the suffix _xl should be xlate'd wherever they appear */
+/* On disk */
+struct pv_header {
+       int8_t pv_uuid[ID_LEN];
+
+       /* This size can be overridden if PV belongs to a VG */
+       uint64_t device_size_xl;        /* Bytes */
+
+       /* NULL-terminated list of data areas followed by */
+       /* NULL-terminated list of metadata area headers */
+       struct disk_locn disk_areas_xl[0];      /* Two lists */
+} __attribute__ ((packed));
+
+/*
+ * Ignore this raw location.  This allows us to
+ * ignored metadata areas easily, and thus balance
+ * metadata across VGs with many PVs.
+ */
+#define RAW_LOCN_IGNORED 0x00000001
+
+/* On disk */
+struct raw_locn {
+       uint64_t offset;        /* Offset in bytes to start sector */
+       uint64_t size;          /* Bytes */
+       uint32_t checksum;
+       uint32_t flags;
+} __attribute__ ((packed));
+
+int rlocn_is_ignored(const struct raw_locn *rlocn);
+void rlocn_set_ignored(struct raw_locn *rlocn, unsigned mda_ignored);
+
+/* On disk */
+/* Structure size limited to one sector */
+struct mda_header {
+       uint32_t checksum_xl;   /* Checksum of rest of mda_header */
+       int8_t magic[16];       /* To aid scans for metadata */
+       uint32_t version;
+       uint64_t start;         /* Absolute start byte of mda_header */
+       uint64_t size;          /* Size of metadata area */
+
+       struct raw_locn raw_locns[0];   /* NULL-terminated list */
+} __attribute__ ((packed));
+
+struct mda_header *raw_read_mda_header(const struct format_type *fmt,
+                                      struct device_area *dev_area);
+
+struct mda_lists {
+       struct dm_list dirs;
+       struct dm_list raws;
+       struct metadata_area_ops *file_ops;
+       struct metadata_area_ops *raw_ops;
+};
+
+struct mda_context {
+       struct device_area area;
+       uint64_t free_sectors;
+       struct raw_locn rlocn;  /* Store inbetween write and commit */
+};
+
+/* FIXME Convert this at runtime */
+#define FMTT_MAGIC "\040\114\126\115\062\040\170\133\065\101\045\162\060\116\052\076"
+#define FMTT_VERSION 1
+#define MDA_HEADER_SIZE 512
+#define LVM2_LABEL "LVM2 001"
+#define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize())
+
+
+const char *vgname_from_mda(const struct format_type *fmt,
+                           struct mda_header *mdah,
+                           struct device_area *dev_area, struct id *vgid,
+                           uint64_t *vgstatus, char **creation_host,
+                           uint64_t *mda_free_sectors);
+
+#endif
diff --git a/lib/format_text/tags.c b/lib/format_text/tags.c
new file mode 100644 (file)
index 0000000..76d42db
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "str_list.h"
+#include "lvm-string.h"
+
+char *alloc_printed_tags(struct dm_list *tags)
+{
+       struct str_list *sl;
+       int first = 1;
+       size_t size = 0;
+       char *buffer, *buf;
+
+       dm_list_iterate_items(sl, tags)
+               /* '"' + tag + '"' + ',' + ' ' */
+               size += strlen(sl->str) + 4;
+       /* '[' + ']' + '\0' */
+       size += 3;
+
+       if (!(buffer = buf = dm_malloc(size))) {
+               log_error("Could not allocate memory for tag list buffer.");
+               return NULL;
+       }
+
+       if (!emit_to_buffer(&buf, &size, "["))
+               goto_bad;
+
+       dm_list_iterate_items(sl, tags) {
+               if (!first) {
+                       if (!emit_to_buffer(&buf, &size, ", "))
+                               goto_bad;
+               } else
+                       first = 0;
+
+               if (!emit_to_buffer(&buf, &size, "\"%s\"", sl->str))
+                       goto_bad;
+       }
+
+       if (!emit_to_buffer(&buf, &size, "]"))
+               goto_bad;
+
+       return buffer;
+
+bad:
+       dm_free(buffer);
+       return_NULL;
+}
+
+int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct config_value *cv)
+{
+       if (cv->type == CFG_EMPTY_ARRAY)
+               return 1;
+
+       while (cv) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Found a tag that is not a string");
+                       return 0;
+               }
+
+               if (!str_list_add(mem, tags, dm_pool_strdup(mem, cv->v.str)))
+                       return_0;
+
+               cv = cv->next;
+       }
+
+       return 1;
+}
diff --git a/lib/format_text/text_export.h b/lib/format_text/text_export.h
new file mode 100644 (file)
index 0000000..39a6916
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TEXT_EXPORT_H
+#define _LVM_TEXT_EXPORT_H
+
+#define outsize(args...) do {if (!out_size(args)) return_0;} while (0)
+#define outhint(args...) do {if (!out_hint(args)) return_0;} while (0)
+#define outfc(args...) do {if (!out_text_with_comment(args)) return_0;} while (0)
+#define outf(args...) do {if (!out_text(args)) return_0;} while (0)
+#define outnl(f) do {if (!out_newline(f)) return_0;} while (0)
+
+struct formatter;
+struct lv_segment;
+struct config_node;
+
+int out_size(struct formatter *f, uint64_t size, const char *fmt, ...)
+    __attribute__ ((format(printf, 3, 4)));
+
+int out_hint(struct formatter *f, const char *fmt, ...)
+    __attribute__ ((format(printf, 2, 3)));
+
+int out_text(struct formatter *f, const char *fmt, ...)
+    __attribute__ ((format(printf, 2, 3)));
+
+int out_config_node(struct formatter *f, const struct config_node *cn);
+
+int out_areas(struct formatter *f, const struct lv_segment *seg,
+             const char *type);
+
+int out_text_with_comment(struct formatter *f, const char* comment, const char *fmt, ...)
+    __attribute__ ((format(printf, 3, 4)));
+
+void out_inc_indent(struct formatter *f);
+void out_dec_indent(struct formatter *f);
+int out_newline(struct formatter *f);
+
+#endif
diff --git a/lib/format_text/text_import.h b/lib/format_text/text_import.h
new file mode 100644 (file)
index 0000000..99d222f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TEXT_IMPORT_H
+#define _LVM_TEXT_IMPORT_H
+
+struct lv_segment;
+struct config_node;
+
+int text_import_areas(struct lv_segment *seg, const struct config_node *sn,
+                     const struct config_node *cn, struct dm_hash_table *pv_hash,
+                     uint64_t status);
+
+#endif
diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c
new file mode 100644 (file)
index 0000000..e459cde
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "format-text.h"
+#include "layout.h"
+#include "label.h"
+#include "xlate.h"
+#include "lvmcache.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int _text_can_handle(struct labeller *l __attribute__((unused)),
+                           void *buf,
+                           uint64_t sector __attribute__((unused)))
+{
+       struct label_header *lh = (struct label_header *) buf;
+
+       if (!strncmp((char *)lh->type, LVM2_LABEL, sizeof(lh->type)))
+               return 1;
+
+       return 0;
+}
+
+static int _text_write(struct label *label, void *buf)
+{
+       struct label_header *lh = (struct label_header *) buf;
+       struct pv_header *pvhdr;
+       struct lvmcache_info *info;
+       struct disk_locn *pvh_dlocn_xl;
+       struct metadata_area *mda;
+       struct mda_context *mdac;
+       struct data_area_list *da;
+       char buffer[64] __attribute__((aligned(8)));
+       int da1, mda1, mda2;
+
+       /* FIXME Move to where label is created */
+       strncpy(label->type, LVM2_LABEL, sizeof(label->type));
+
+       strncpy((char *)lh->type, label->type, sizeof(label->type));
+
+       pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl));
+       info = (struct lvmcache_info *) label->info;
+       pvhdr->device_size_xl = xlate64(info->device_size);
+       memcpy(pvhdr->pv_uuid, &info->dev->pvid, sizeof(struct id));
+       if (!id_write_format((const struct id *)pvhdr->pv_uuid, buffer,
+                            sizeof(buffer))) {
+               stack;
+               buffer[0] = '\0';
+       }
+
+       pvh_dlocn_xl = &pvhdr->disk_areas_xl[0];
+
+       /* List of data areas (holding PEs) */
+       dm_list_iterate_items(da, &info->das) {
+               pvh_dlocn_xl->offset = xlate64(da->disk_locn.offset);
+               pvh_dlocn_xl->size = xlate64(da->disk_locn.size);
+               pvh_dlocn_xl++;
+       }
+
+       /* NULL-termination */
+       pvh_dlocn_xl->offset = xlate64(UINT64_C(0));
+       pvh_dlocn_xl->size = xlate64(UINT64_C(0));
+       pvh_dlocn_xl++;
+
+       /* List of metadata area header locations */
+       dm_list_iterate_items(mda, &info->mdas) {
+               mdac = (struct mda_context *) mda->metadata_locn;
+
+               if (mdac->area.dev != info->dev)
+                       continue;
+
+               pvh_dlocn_xl->offset = xlate64(mdac->area.start);
+               pvh_dlocn_xl->size = xlate64(mdac->area.size);
+               pvh_dlocn_xl++;
+       }
+
+       /* NULL-termination */
+       pvh_dlocn_xl->offset = xlate64(UINT64_C(0));
+       pvh_dlocn_xl->size = xlate64(UINT64_C(0));
+
+       /* Create debug message with da and mda locations */
+       if (xlate64(pvhdr->disk_areas_xl[0].offset) ||
+           xlate64(pvhdr->disk_areas_xl[0].size))
+               da1 = 0;
+       else
+               da1 = -1;
+
+       mda1 = da1 + 2;
+       mda2 = mda1 + 1;
+       
+       if (!xlate64(pvhdr->disk_areas_xl[mda1].offset) &&
+           !xlate64(pvhdr->disk_areas_xl[mda1].size))
+               mda1 = mda2 = 0;
+       else if (!xlate64(pvhdr->disk_areas_xl[mda2].offset) &&
+                !xlate64(pvhdr->disk_areas_xl[mda2].size))
+               mda2 = 0;
+
+       log_debug("%s: Preparing PV label header %s size %" PRIu64 " with"
+                 "%s%.*" PRIu64 "%s%.*" PRIu64 "%s"
+                 "%s%.*" PRIu64 "%s%.*" PRIu64 "%s"
+                 "%s%.*" PRIu64 "%s%.*" PRIu64 "%s",
+                 dev_name(info->dev), buffer, info->device_size, 
+                 (da1 > -1) ? " da1 (" : "",
+                 (da1 > -1) ? 1 : 0,
+                 (da1 > -1) ? xlate64(pvhdr->disk_areas_xl[da1].offset) >> SECTOR_SHIFT : 0,
+                 (da1 > -1) ? "s, " : "",
+                 (da1 > -1) ? 1 : 0,
+                 (da1 > -1) ? xlate64(pvhdr->disk_areas_xl[da1].size) >> SECTOR_SHIFT : 0,
+                 (da1 > -1) ? "s)" : "",
+                 mda1 ? " mda1 (" : "",
+                 mda1 ? 1 : 0,
+                 mda1 ? xlate64(pvhdr->disk_areas_xl[mda1].offset) >> SECTOR_SHIFT : 0,
+                 mda1 ? "s, " : "",
+                 mda1 ? 1 : 0,
+                 mda1 ? xlate64(pvhdr->disk_areas_xl[mda1].size) >> SECTOR_SHIFT : 0,
+                 mda1 ? "s)" : "",
+                 mda2 ? " mda2 (" : "",
+                 mda2 ? 1 : 0,
+                 mda2 ? xlate64(pvhdr->disk_areas_xl[mda2].offset) >> SECTOR_SHIFT : 0,
+                 mda2 ? "s, " : "",
+                 mda2 ? 1 : 0,
+                 mda2 ? xlate64(pvhdr->disk_areas_xl[mda2].size) >> SECTOR_SHIFT : 0,
+                 mda2 ? "s)" : "");
+
+       if (da1 < 0) {
+               log_error(INTERNAL_ERROR "%s label header currently requires "
+                         "a data area.", dev_name(info->dev));
+               return 0;
+       }
+
+       return 1;
+}
+
+int add_da(struct dm_pool *mem, struct dm_list *das,
+          uint64_t start, uint64_t size)
+{
+       struct data_area_list *dal;
+
+       if (!mem) {
+               if (!(dal = dm_malloc(sizeof(*dal)))) {
+                       log_error("struct data_area_list allocation failed");
+                       return 0;
+               }
+       } else {
+               if (!(dal = dm_pool_alloc(mem, sizeof(*dal)))) {
+                       log_error("struct data_area_list allocation failed");
+                       return 0;
+               }
+       }
+
+       dal->disk_locn.offset = start;
+       dal->disk_locn.size = size;
+
+       dm_list_add(das, &dal->list);
+
+       return 1;
+}
+
+void del_das(struct dm_list *das)
+{
+       struct dm_list *dah, *tmp;
+       struct data_area_list *da;
+
+       dm_list_iterate_safe(dah, tmp, das) {
+               da = dm_list_item(dah, struct data_area_list);
+               dm_list_del(&da->list);
+               dm_free(da);
+       }
+}
+
+/* FIXME: refactor this function with other mda constructor code */
+int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *mdas,
+           struct device *dev, uint64_t start, uint64_t size, unsigned ignored)
+{
+/* FIXME List size restricted by pv_header SECTOR_SIZE */
+       struct metadata_area *mdal;
+       struct mda_lists *mda_lists = (struct mda_lists *) fmt->private;
+       struct mda_context *mdac;
+
+       if (!mem) {
+               if (!(mdal = dm_malloc(sizeof(struct metadata_area)))) {
+                       log_error("struct mda_list allocation failed");
+                       return 0;
+               }
+
+               if (!(mdac = dm_malloc(sizeof(struct mda_context)))) {
+                       log_error("struct mda_context allocation failed");
+                       dm_free(mdal);
+                       return 0;
+               }
+       } else {
+               if (!(mdal = dm_pool_alloc(mem, sizeof(struct metadata_area)))) {
+                       log_error("struct mda_list allocation failed");
+                       return 0;
+               }
+
+               if (!(mdac = dm_pool_alloc(mem, sizeof(struct mda_context)))) {
+                       log_error("struct mda_context allocation failed");
+                       return 0;
+               }
+       }
+
+       mdal->ops = mda_lists->raw_ops;
+       mdal->metadata_locn = mdac;
+       mdal->status = 0;
+
+       mdac->area.dev = dev;
+       mdac->area.start = start;
+       mdac->area.size = size;
+       mdac->free_sectors = UINT64_C(0);
+       memset(&mdac->rlocn, 0, sizeof(mdac->rlocn));
+       mda_set_ignored(mdal, ignored);
+
+       dm_list_add(mdas, &mdal->list);
+       return 1;
+}
+
+void del_mdas(struct dm_list *mdas)
+{
+       struct dm_list *mdah, *tmp;
+       struct metadata_area *mda;
+
+       dm_list_iterate_safe(mdah, tmp, mdas) {
+               mda = dm_list_item(mdah, struct metadata_area);
+               dm_free(mda->metadata_locn);
+               dm_list_del(&mda->list);
+               dm_free(mda);
+       }
+}
+
+static int _text_initialise_label(struct labeller *l __attribute__((unused)),
+                                 struct label *label)
+{
+       strncpy(label->type, LVM2_LABEL, sizeof(label->type));
+
+       return 1;
+}
+
+static int _text_read(struct labeller *l, struct device *dev, void *buf,
+                struct label **label)
+{
+       struct label_header *lh = (struct label_header *) buf;
+       struct pv_header *pvhdr;
+       struct lvmcache_info *info;
+       struct disk_locn *dlocn_xl;
+       uint64_t offset;
+       struct metadata_area *mda;
+       struct id vgid;
+       struct mda_context *mdac;
+       const char *vgname;
+       uint64_t vgstatus;
+       char *creation_host;
+       struct mda_header *mdah;
+
+       pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl));
+
+       if (!(info = lvmcache_add(l, (char *)pvhdr->pv_uuid, dev,
+                                 FMT_TEXT_ORPHAN_VG_NAME,
+                                 FMT_TEXT_ORPHAN_VG_NAME, 0)))
+               return_0;
+       *label = info->label;
+
+       info->device_size = xlate64(pvhdr->device_size_xl);
+
+       if (info->das.n)
+               del_das(&info->das);
+       dm_list_init(&info->das);
+
+       if (info->mdas.n)
+               del_mdas(&info->mdas);
+       dm_list_init(&info->mdas);
+
+       /* Data areas holding the PEs */
+       dlocn_xl = pvhdr->disk_areas_xl;
+       while ((offset = xlate64(dlocn_xl->offset))) {
+               add_da(NULL, &info->das, offset,
+                      xlate64(dlocn_xl->size));
+               dlocn_xl++;
+       }
+
+       /* Metadata area headers */
+       dlocn_xl++;
+       while ((offset = xlate64(dlocn_xl->offset))) {
+               add_mda(info->fmt, NULL, &info->mdas, dev, offset,
+                       xlate64(dlocn_xl->size), 0);
+               dlocn_xl++;
+       }
+
+       dm_list_iterate_items(mda, &info->mdas) {
+               mdac = (struct mda_context *) mda->metadata_locn;
+               if (!dev_open(mdac->area.dev)) {
+                       mda_set_ignored(mda, 1);
+                       stack;
+                       continue;
+               }
+               if (!(mdah = raw_read_mda_header(info->fmt, &mdac->area))) {
+                       stack;
+                       goto close_dev;
+               }
+               mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
+
+               if (mda_is_ignored(mda)) {
+                       log_debug("Ignoring mda on device %s at offset %"PRIu64,
+                                 dev_name(mdac->area.dev),
+                                 mdac->area.start);
+                       if (!dev_close(mdac->area.dev))
+                               stack;
+                       continue;
+               }
+
+               if ((vgname = vgname_from_mda(info->fmt, mdah,
+                                             &mdac->area,
+                                             &vgid, &vgstatus, &creation_host,
+                                             &mdac->free_sectors)) &&
+                   !lvmcache_update_vgname_and_id(info, vgname,
+                                                  (char *) &vgid, vgstatus,
+                                                  creation_host)) {
+                       if (!dev_close(mdac->area.dev))
+                                       stack;
+                       return_0;
+               }
+       close_dev:
+               if (!dev_close(mdac->area.dev))
+                       stack;
+       }
+
+       info->status &= ~CACHE_INVALID;
+
+       return 1;
+}
+
+static void _text_destroy_label(struct labeller *l __attribute__((unused)),
+                               struct label *label)
+{
+       struct lvmcache_info *info = (struct lvmcache_info *) label->info;
+
+       if (info->mdas.n)
+               del_mdas(&info->mdas);
+       if (info->das.n)
+               del_das(&info->das);
+}
+
+static void _fmt_text_destroy(struct labeller *l)
+{
+       dm_free(l);
+}
+
+struct label_ops _text_ops = {
+       .can_handle = _text_can_handle,
+       .write = _text_write,
+       .read = _text_read,
+       .verify = _text_can_handle,
+       .initialise_label = _text_initialise_label,
+       .destroy_label = _text_destroy_label,
+       .destroy = _fmt_text_destroy,
+};
+
+struct labeller *text_labeller_create(const struct format_type *fmt)
+{
+       struct labeller *l;
+
+       if (!(l = dm_malloc(sizeof(*l)))) {
+               log_error("Couldn't allocate labeller object.");
+               return NULL;
+       }
+
+       l->ops = &_text_ops;
+       l->private = (const void *) fmt;
+
+       return l;
+}
diff --git a/lib/freeseg/freeseg.c b/lib/freeseg/freeseg.c
new file mode 100644 (file)
index 0000000..8e73be0
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "str_list.h"
+#include "metadata.h"
+
+static const char *_freeseg_name(const struct lv_segment *seg)
+{
+       return seg->segtype->name;
+}
+
+static void _freeseg_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _freeseg_ops = {
+       .name = _freeseg_name,
+       .destroy = _freeseg_destroy,
+};
+
+struct segment_type *init_free_segtype(struct cmd_context *cmd)
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_freeseg_ops;
+       segtype->name = "free";
+       segtype->private = NULL;
+       segtype->flags = SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/lib/label/label.c b/lib/label/label.c
new file mode 100644 (file)
index 0000000..c622812
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "crc.h"
+#include "xlate.h"
+#include "lvmcache.h"
+#include "metadata.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* FIXME Allow for larger labels?  Restricted to single sector currently */
+
+/*
+ * Internal labeller struct.
+ */
+struct labeller_i {
+       struct dm_list list;
+
+       struct labeller *l;
+       char name[0];
+};
+
+static struct dm_list _labellers;
+
+static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
+{
+       struct labeller_i *li;
+       size_t len;
+
+       len = sizeof(*li) + strlen(name) + 1;
+
+       if (!(li = dm_malloc(len))) {
+               log_error("Couldn't allocate memory for labeller list object.");
+               return NULL;
+       }
+
+       li->l = l;
+       strcpy(li->name, name);
+
+       return li;
+}
+
+static void _free_li(struct labeller_i *li)
+{
+       dm_free(li);
+}
+
+int label_init(void)
+{
+       dm_list_init(&_labellers);
+       return 1;
+}
+
+void label_exit(void)
+{
+       struct dm_list *c, *n;
+       struct labeller_i *li;
+
+       for (c = _labellers.n; c && c != &_labellers; c = n) {
+               n = c->n;
+               li = dm_list_item(c, struct labeller_i);
+               li->l->ops->destroy(li->l);
+               _free_li(li);
+       }
+
+       dm_list_init(&_labellers);
+}
+
+int label_register_handler(const char *name, struct labeller *handler)
+{
+       struct labeller_i *li;
+
+       if (!(li = _alloc_li(name, handler)))
+               return_0;
+
+       dm_list_add(&_labellers, &li->list);
+       return 1;
+}
+
+struct labeller *label_get_handler(const char *name)
+{
+       struct labeller_i *li;
+
+       dm_list_iterate_items(li, &_labellers)
+               if (!strcmp(li->name, name))
+                       return li->l;
+
+       return NULL;
+}
+
+static struct labeller *_find_labeller(struct device *dev, char *buf,
+                                      uint64_t *label_sector,
+                                      uint64_t scan_sector)
+{
+       struct labeller_i *li;
+       struct labeller *r = NULL;
+       struct label_header *lh;
+       struct lvmcache_info *info;
+       uint64_t sector;
+       int found = 0;
+       char readbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
+
+       if (!dev_read(dev, scan_sector << SECTOR_SHIFT,
+                     LABEL_SCAN_SIZE, readbuf)) {
+               log_debug("%s: Failed to read label area", dev_name(dev));
+               goto out;
+       }
+
+       /* Scan a few sectors for a valid label */
+       for (sector = 0; sector < LABEL_SCAN_SECTORS;
+            sector += LABEL_SIZE >> SECTOR_SHIFT) {
+               lh = (struct label_header *) (readbuf +
+                                             (sector << SECTOR_SHIFT));
+
+               if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
+                       if (found) {
+                               log_error("Ignoring additional label on %s at "
+                                         "sector %" PRIu64, dev_name(dev),
+                                         sector + scan_sector);
+                       }
+                       if (xlate64(lh->sector_xl) != sector + scan_sector) {
+                               log_info("%s: Label for sector %" PRIu64
+                                        " found at sector %" PRIu64
+                                        " - ignoring", dev_name(dev),
+                                        (uint64_t)xlate64(lh->sector_xl),
+                                        sector + scan_sector);
+                               continue;
+                       }
+                       if (calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
+                                    ((uint8_t *) &lh->offset_xl - (uint8_t *) lh)) !=
+                           xlate32(lh->crc_xl)) {
+                               log_info("Label checksum incorrect on %s - "
+                                        "ignoring", dev_name(dev));
+                               continue;
+                       }
+                       if (found)
+                               continue;
+               }
+
+               dm_list_iterate_items(li, &_labellers) {
+                       if (li->l->ops->can_handle(li->l, (char *) lh,
+                                                  sector + scan_sector)) {
+                               log_very_verbose("%s: %s label detected",
+                                                dev_name(dev), li->name);
+                               if (found) {
+                                       log_error("Ignoring additional label "
+                                                 "on %s at sector %" PRIu64,
+                                                 dev_name(dev),
+                                                 sector + scan_sector);
+                                       continue;
+                               }
+                               r = li->l;
+                               memcpy(buf, lh, LABEL_SIZE);
+                               if (label_sector)
+                                       *label_sector = sector + scan_sector;
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+
+      out:
+       if (!found) {
+               if ((info = info_from_pvid(dev->pvid, 0)))
+                       lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
+                                                     info->fmt->orphan_vg_name,
+                                                     0, NULL);
+               log_very_verbose("%s: No label detected", dev_name(dev));
+       }
+
+       return r;
+}
+
+/* FIXME Also wipe associated metadata area headers? */
+int label_remove(struct device *dev)
+{
+       char buf[LABEL_SIZE] __attribute__((aligned(8)));
+       char readbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
+       int r = 1;
+       uint64_t sector;
+       int wipe;
+       struct labeller_i *li;
+       struct label_header *lh;
+
+       memset(buf, 0, LABEL_SIZE);
+
+       log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev));
+
+       if (!dev_open(dev))
+               return_0;
+
+       /*
+        * We flush the device just in case someone is stupid
+        * enough to be trying to import an open pv into lvm.
+        */
+       dev_flush(dev);
+
+       if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) {
+               log_debug("%s: Failed to read label area", dev_name(dev));
+               goto out;
+       }
+
+       /* Scan first few sectors for anything looking like a label */
+       for (sector = 0; sector < LABEL_SCAN_SECTORS;
+            sector += LABEL_SIZE >> SECTOR_SHIFT) {
+               lh = (struct label_header *) (readbuf +
+                                             (sector << SECTOR_SHIFT));
+
+               wipe = 0;
+
+               if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
+                       if (xlate64(lh->sector_xl) == sector)
+                               wipe = 1;
+               } else {
+                       dm_list_iterate_items(li, &_labellers) {
+                               if (li->l->ops->can_handle(li->l, (char *) lh,
+                                                          sector)) {
+                                       wipe = 1;
+                                       break;
+                               }
+                       }
+               }
+
+               if (wipe) {
+                       log_info("%s: Wiping label at sector %" PRIu64,
+                                dev_name(dev), sector);
+                       if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
+                                      buf)) {
+                               log_error("Failed to remove label from %s at "
+                                         "sector %" PRIu64, dev_name(dev),
+                                         sector);
+                               r = 0;
+                       }
+               }
+       }
+
+      out:
+       if (!dev_close(dev))
+               stack;
+
+       return r;
+}
+
+int label_read(struct device *dev, struct label **result,
+               uint64_t scan_sector)
+{
+       char buf[LABEL_SIZE] __attribute__((aligned(8)));
+       struct labeller *l;
+       uint64_t sector;
+       struct lvmcache_info *info;
+       int r = 0;
+
+       if ((info = info_from_pvid(dev->pvid, 1))) {
+               log_debug("Using cached label for %s", dev_name(dev));
+               *result = info->label;
+               return 1;
+       }
+
+       if (!dev_open(dev)) {
+               stack;
+
+               if ((info = info_from_pvid(dev->pvid, 0)))
+                       lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
+                                                     info->fmt->orphan_vg_name,
+                                                     0, NULL);
+
+               return r;
+       }
+
+       if (!(l = _find_labeller(dev, buf, &sector, scan_sector)))
+               goto out;
+
+       if ((r = (l->ops->read)(l, dev, buf, result)) && result && *result)
+               (*result)->sector = sector;
+
+      out:
+       if (!dev_close(dev))
+               stack;
+
+       return r;
+}
+
+/* Caller may need to use label_get_handler to create label struct! */
+int label_write(struct device *dev, struct label *label)
+{
+       char buf[LABEL_SIZE] __attribute__((aligned(8)));
+       struct label_header *lh = (struct label_header *) buf;
+       int r = 1;
+
+       if (!label->labeller->ops->write) {
+               log_error("Label handler does not support label writes");
+               return 0;
+       }
+
+       if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) {
+               log_error("Label sector %" PRIu64 " beyond range (%ld)",
+                         label->sector, LABEL_SCAN_SECTORS);
+               return 0;
+       }
+
+       memset(buf, 0, LABEL_SIZE);
+
+       strncpy((char *)lh->id, LABEL_ID, sizeof(lh->id));
+       lh->sector_xl = xlate64(label->sector);
+       lh->offset_xl = xlate32(sizeof(*lh));
+
+       if (!(label->labeller->ops->write)(label, buf))
+               return_0;
+
+       lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
+                                     ((uint8_t *) &lh->offset_xl - (uint8_t *) lh)));
+
+       if (!dev_open(dev))
+               return_0;
+
+       log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
+                PRIu32 ".", dev_name(dev), label->sector,
+                xlate32(lh->offset_xl));
+       if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
+               log_debug("Failed to write label to %s", dev_name(dev));
+               r = 0;
+       }
+
+       if (!dev_close(dev))
+               stack;
+
+       return r;
+}
+
+/* Unused */
+int label_verify(struct device *dev)
+{
+       struct labeller *l;
+       char buf[LABEL_SIZE] __attribute__((aligned(8)));
+       uint64_t sector;
+       struct lvmcache_info *info;
+       int r = 0;
+
+       if (!dev_open(dev)) {
+               if ((info = info_from_pvid(dev->pvid, 0)))
+                       lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
+                                                     info->fmt->orphan_vg_name,
+                                                     0, NULL);
+
+               return_0;
+       }
+
+       if (!(l = _find_labeller(dev, buf, &sector, UINT64_C(0))))
+               goto out;
+
+       r = l->ops->verify ? l->ops->verify(l, buf, sector) : 1;
+
+      out:
+       if (!dev_close(dev))
+               stack;
+
+       return r;
+}
+
+void label_destroy(struct label *label)
+{
+       label->labeller->ops->destroy_label(label->labeller, label);
+       dm_free(label);
+}
+
+struct label *label_create(struct labeller *labeller)
+{
+       struct label *label;
+
+       if (!(label = dm_zalloc(sizeof(*label)))) {
+               log_error("label allocaction failed");
+               return NULL;
+       }
+
+       label->labeller = labeller;
+
+       labeller->ops->initialise_label(labeller, label);
+
+       return label;
+}
diff --git a/lib/label/label.h b/lib/label/label.h
new file mode 100644 (file)
index 0000000..3e07f06
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_LABEL_H
+#define _LVM_LABEL_H
+
+#include "uuid.h"
+#include "device.h"
+
+#define LABEL_ID "LABELONE"
+#define LABEL_SIZE SECTOR_SIZE /* Think very carefully before changing this */
+#define LABEL_SCAN_SECTORS 4L
+#define LABEL_SCAN_SIZE (LABEL_SCAN_SECTORS << SECTOR_SHIFT)
+
+struct labeller;
+
+/* On disk - 32 bytes */
+struct label_header {
+       int8_t id[8];           /* LABELONE */
+       uint64_t sector_xl;     /* Sector number of this label */
+       uint32_t crc_xl;        /* From next field to end of sector */
+       uint32_t offset_xl;     /* Offset from start of struct to contents */
+       int8_t type[8];         /* LVM2 001 */
+} __attribute__ ((packed));
+
+/* In core */
+struct label {
+       char type[8];
+       uint64_t sector;
+       struct labeller *labeller;
+       void *info;
+};
+
+struct labeller;
+
+struct label_ops {
+       /*
+        * Is the device labelled with this format ?
+        */
+       int (*can_handle) (struct labeller * l, void *buf, uint64_t sector);
+
+       /*
+        * Write a label to a volume.
+        */
+       int (*write) (struct label * label, void *buf);
+
+       /*
+        * Read a label from a volume.
+        */
+       int (*read) (struct labeller * l, struct device * dev,
+                    void *buf, struct label ** label);
+
+       /*
+        * Additional consistency checks for the paranoid.
+        */
+       int (*verify) (struct labeller * l, void *buf, uint64_t sector);
+
+       /*
+        * Populate label_type etc.
+        */
+       int (*initialise_label) (struct labeller * l, struct label * label);
+
+       /*
+        * Destroy a previously read label.
+        */
+       void (*destroy_label) (struct labeller * l, struct label * label);
+
+       /*
+        * Destructor.
+        */
+       void (*destroy) (struct labeller * l);
+};
+
+struct labeller {
+       struct label_ops *ops;
+       const void *private;
+};
+
+int label_init(void);
+void label_exit(void);
+
+int label_register_handler(const char *name, struct labeller *handler);
+
+struct labeller *label_get_handler(const char *name);
+
+int label_remove(struct device *dev);
+int label_read(struct device *dev, struct label **result,
+               uint64_t scan_sector);
+int label_write(struct device *dev, struct label *label);
+int label_verify(struct device *dev);
+struct label *label_create(struct labeller *labeller);
+void label_destroy(struct label *label);
+
+#endif
diff --git a/lib/locking/.exported_symbols b/lib/locking/.exported_symbols
new file mode 100644 (file)
index 0000000..0a2cae7
--- /dev/null
@@ -0,0 +1,5 @@
+locking_init
+locking_end
+lock_resource
+query_resource
+reset_locking
diff --git a/lib/locking/Makefile.in b/lib/locking/Makefile.in
new file mode 100644 (file)
index 0000000..1aae878
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = cluster_locking.c
+
+LIB_SHARED = liblvm2clusterlock.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install install_cluster: install_lvm2_plugin
diff --git a/lib/locking/cluster_locking.c b/lib/locking/cluster_locking.c
new file mode 100644 (file)
index 0000000..eea9974
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Locking functions for LVM.
+ * The main purpose of this part of the library is to serialise LVM
+ * management operations across a cluster.
+ */
+
+#include "lib.h"
+#include "clvm.h"
+#include "lvm-string.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "toolcontext.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#ifndef CLUSTER_LOCKING_INTERNAL
+int lock_resource(struct cmd_context *cmd, const char *resource, uint32_t flags);
+int query_resource(const char *resource, int *mode);
+void locking_end(void);
+int locking_init(int type, struct config_tree *cf, uint32_t *flags);
+#endif
+
+typedef struct lvm_response {
+       char node[255];
+       char *response;
+       int status;
+       int len;
+} lvm_response_t;
+
+/*
+ * This gets stuck at the start of memory we allocate so we
+ * can sanity-check it at deallocation time
+ */
+#define LVM_SIGNATURE 0x434C564D
+
+/*
+ * NOTE: the LVMD uses the socket FD as the client ID, this means
+ * that any client that calls fork() will inherit the context of
+ * it's parent.
+ */
+static int _clvmd_sock = -1;
+
+/* FIXME Install SIGPIPE handler? */
+
+/* Open connection to the Cluster Manager daemon */
+static int _open_local_sock(void)
+{
+       int local_socket;
+       struct sockaddr_un sockaddr;
+
+       /* Open local socket */
+       if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+               log_error("Local socket creation failed: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&sockaddr, 0, sizeof(sockaddr));
+       memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+
+       sockaddr.sun_family = AF_UNIX;
+
+       if (connect(local_socket,(struct sockaddr *) &sockaddr,
+                   sizeof(sockaddr))) {
+               int saved_errno = errno;
+
+               log_error("connect() failed on local socket: %s",
+                         strerror(errno));
+               if (close(local_socket))
+                       stack;
+
+               errno = saved_errno;
+               return -1;
+       }
+
+       return local_socket;
+}
+
+/* Send a request and return the status */
+static int _send_request(char *inbuf, int inlen, char **retbuf)
+{
+       char outbuf[PIPE_BUF] __attribute__((aligned(8)));
+       struct clvm_header *outheader = (struct clvm_header *) outbuf;
+       int len;
+       int off;
+       int buflen;
+       int err;
+
+       /* Send it to CLVMD */
+ rewrite:
+       if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
+               if (err == -1 && errno == EINTR)
+                       goto rewrite;
+               log_error("Error writing data to clvmd: %s", strerror(errno));
+               return 0;
+       }
+
+       /* Get the response */
+ reread:
+       if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
+               if (errno == EINTR)
+                       goto reread;
+               log_error("Error reading data from clvmd: %s", strerror(errno));
+               return 0;
+       }
+
+       if (len == 0) {
+               log_error("EOF reading CLVMD");
+               errno = ENOTCONN;
+               return 0;
+       }
+
+       /* Allocate buffer */
+       buflen = len + outheader->arglen;
+       *retbuf = dm_malloc(buflen);
+       if (!*retbuf) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* Copy the header */
+       memcpy(*retbuf, outbuf, len);
+       outheader = (struct clvm_header *) *retbuf;
+
+       /* Read the returned values */
+       off = 1;                /* we've already read the first byte */
+       while (off <= outheader->arglen && len > 0) {
+               len = read(_clvmd_sock, outheader->args + off,
+                          buflen - off - offsetof(struct clvm_header, args));
+               if (len > 0)
+                       off += len;
+       }
+
+       /* Was it an error ? */
+       if (outheader->status != 0) {
+               errno = outheader->status;
+
+               /* Only return an error here if there are no node-specific
+                  errors present in the message that might have more detail */
+               if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
+                       log_error("cluster request failed: %s", strerror(errno));
+                       return 0;
+               }
+
+       }
+
+       return 1;
+}
+
+/* Build the structure header and parse-out wildcard node names */
+/* FIXME: Cleanup implicit casts of clvmd_cmd (int, char, uint8_t, etc). */
+static void _build_header(struct clvm_header *head, int clvmd_cmd, const char *node,
+                         int len)
+{
+       head->cmd = clvmd_cmd;
+       head->status = 0;
+       head->flags = 0;
+       head->clientid = 0;
+       head->arglen = len;
+
+       if (node) {
+               /*
+                * Allow a couple of special node names:
+                * "*" for all nodes,
+                * "." for the local node only
+                */
+               if (strcmp(node, "*") == 0) {
+                       head->node[0] = '\0';
+               } else if (strcmp(node, ".") == 0) {
+                       head->node[0] = '\0';
+                       head->flags = CLVMD_FLAG_LOCAL;
+               } else
+                       strcpy(head->node, node);
+       } else
+               head->node[0] = '\0';
+}
+
+/*
+ * Send a message to a(or all) node(s) in the cluster and wait for replies
+ */
+static int _cluster_request(char clvmd_cmd, const char *node, void *data, int len,
+                          lvm_response_t ** response, int *num)
+{
+       char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1] __attribute__((aligned(8)));
+       char *inptr;
+       char *retbuf = NULL;
+       int status;
+       int i;
+       int num_responses = 0;
+       struct clvm_header *head = (struct clvm_header *) outbuf;
+       lvm_response_t *rarray;
+
+       *num = 0;
+
+       if (_clvmd_sock == -1)
+               _clvmd_sock = _open_local_sock();
+
+       if (_clvmd_sock == -1)
+               return 0;
+
+       _build_header(head, clvmd_cmd, node, len);
+       memcpy(head->node + strlen(head->node) + 1, data, len);
+
+       status = _send_request(outbuf, sizeof(struct clvm_header) +
+                             strlen(head->node) + len, &retbuf);
+       if (!status)
+               goto out;
+
+       /* Count the number of responses we got */
+       head = (struct clvm_header *) retbuf;
+       inptr = head->args;
+       while (inptr[0]) {
+               num_responses++;
+               inptr += strlen(inptr) + 1;
+               inptr += sizeof(int);
+               inptr += strlen(inptr) + 1;
+       }
+
+       /*
+        * Allocate response array.
+        * With an extra pair of INTs on the front to sanity
+        * check the pointer when we are given it back to free
+        */
+       *response = dm_malloc(sizeof(lvm_response_t) * num_responses);
+       if (!*response) {
+               errno = ENOMEM;
+               status = 0;
+               goto out;
+       }
+
+       rarray = *response;
+
+       /* Unpack the response into an lvm_response_t array */
+       inptr = head->args;
+       i = 0;
+       while (inptr[0]) {
+               strcpy(rarray[i].node, inptr);
+               inptr += strlen(inptr) + 1;
+
+               memcpy(&rarray[i].status, inptr, sizeof(int));
+               inptr += sizeof(int);
+
+               rarray[i].response = dm_malloc(strlen(inptr) + 1);
+               if (rarray[i].response == NULL) {
+                       /* Free up everything else and return error */
+                       int j;
+                       for (j = 0; j < i; j++)
+                               dm_free(rarray[i].response);
+                       free(*response);
+                       errno = ENOMEM;
+                       status = -1;
+                       goto out;
+               }
+
+               strcpy(rarray[i].response, inptr);
+               rarray[i].len = strlen(inptr);
+               inptr += strlen(inptr) + 1;
+               i++;
+       }
+       *num = num_responses;
+       *response = rarray;
+
+      out:
+       if (retbuf)
+               dm_free(retbuf);
+
+       return status;
+}
+
+/* Free reply array */
+static int _cluster_free_request(lvm_response_t * response, int num)
+{
+       int i;
+
+       for (i = 0; i < num; i++) {
+               dm_free(response[i].response);
+       }
+
+       dm_free(response);
+
+       return 1;
+}
+
+static int _lock_for_cluster(struct cmd_context *cmd, unsigned char clvmd_cmd,
+                            uint32_t flags, const char *name)
+{
+       int status;
+       int i;
+       char *args;
+       const char *node = "";
+       int len;
+       int dmeventd_mode;
+       int saved_errno;
+       lvm_response_t *response = NULL;
+       int num_responses;
+
+       assert(name);
+
+       len = strlen(name) + 3;
+       args = alloca(len);
+       strcpy(args + 2, name);
+
+       /* Mask off lock flags */
+       args[0] = flags & (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_NONBLOCK | LCK_HOLD); 
+       args[1] = flags & (LCK_LOCAL | LCK_CLUSTER_VG);
+
+       if (flags & LCK_ORIGIN_ONLY)
+               args[1] |= LCK_ORIGIN_ONLY_MODE;
+
+       if (mirror_in_sync())
+               args[1] |= LCK_MIRROR_NOSYNC_MODE;
+
+       /*
+        * Must handle tri-state return from dmeventd_monitor_mode.
+        * But DMEVENTD_MONITOR_IGNORE is not propagated across the cluster.
+        */
+       dmeventd_mode = dmeventd_monitor_mode();
+       if (dmeventd_mode != DMEVENTD_MONITOR_IGNORE && dmeventd_mode)
+               args[1] |= LCK_DMEVENTD_MONITOR_MODE;
+
+       if (cmd->partial_activation)
+               args[1] |= LCK_PARTIAL_MODE;
+
+       /*
+        * VG locks are just that: locks, and have no side effects
+        * so we only need to do them on the local node because all
+        * locks are cluster-wide.
+        * Also, if the lock is exclusive it makes no sense to try to
+        * acquire it on all nodes, so just do that on the local node too.
+        * One exception, is that P_ locks /do/ get distributed across
+        * the cluster because they might have side-effects.
+        */
+       if (strncmp(name, "P_", 2) &&
+           (clvmd_cmd == CLVMD_CMD_LOCK_VG ||
+            (flags & LCK_TYPE_MASK) == LCK_EXCL ||
+            (flags & LCK_LOCAL) ||
+            !(flags & LCK_CLUSTER_VG)))
+               node = ".";
+
+       status = _cluster_request(clvmd_cmd, node, args, len,
+                                 &response, &num_responses);
+
+       /* If any nodes were down then display them and return an error */
+       for (i = 0; i < num_responses; i++) {
+               if (response[i].status == EHOSTDOWN) {
+                       log_error("clvmd not running on node %s",
+                                 response[i].node);
+                       status = 0;
+                       errno = response[i].status;
+               } else if (response[i].status) {
+                       log_error("Error locking on node %s: %s",
+                                 response[i].node,
+                                 response[i].response[0] ?
+                                       response[i].response :
+                                       strerror(response[i].status));
+                       status = 0;
+                       errno = response[i].status;
+               }
+       }
+
+       saved_errno = errno;
+       _cluster_free_request(response, num_responses);
+       errno = saved_errno;
+
+       return status;
+}
+
+/* API entry point for LVM */
+#ifdef CLUSTER_LOCKING_INTERNAL
+static int _lock_resource(struct cmd_context *cmd, const char *resource,
+                         uint32_t flags)
+#else
+int lock_resource(struct cmd_context *cmd, const char *resource, uint32_t flags)
+#endif
+{
+       char lockname[PATH_MAX];
+       int clvmd_cmd = 0;
+       const char *lock_scope;
+       const char *lock_type = "";
+
+       assert(strlen(resource) < sizeof(lockname));
+       assert(resource);
+
+       switch (flags & LCK_SCOPE_MASK) {
+       case LCK_VG:
+               if (flags == LCK_VG_BACKUP) {
+                       log_very_verbose("Requesting backup of VG metadata for %s",
+                                        resource);
+                       return _lock_for_cluster(cmd, CLVMD_CMD_VG_BACKUP,
+                                                LCK_CLUSTER_VG, resource);
+               }
+
+               /* If the VG name is empty then lock the unused PVs */
+               if (is_orphan_vg(resource) || is_global_vg(resource) || (flags & LCK_CACHE))
+                       dm_snprintf(lockname, sizeof(lockname), "P_%s",
+                                   resource);
+               else
+                       dm_snprintf(lockname, sizeof(lockname), "V_%s",
+                                   resource);
+
+               lock_scope = "VG";
+               clvmd_cmd = CLVMD_CMD_LOCK_VG;
+               /*
+                * Old clvmd does not expect LCK_HOLD which was already processed
+                * in lock_vol, mask it for compatibility reasons.
+                */
+               if (flags != LCK_VG_COMMIT && flags != LCK_VG_REVERT)
+                       flags &= ~LCK_HOLD;
+
+               break;
+
+       case LCK_LV:
+               clvmd_cmd = CLVMD_CMD_LOCK_LV;
+               strcpy(lockname, resource);
+               lock_scope = "LV";
+               flags &= ~LCK_HOLD;     /* Mask off HOLD flag */
+               break;
+
+       default:
+               log_error("Unrecognised lock scope: %d",
+                         flags & LCK_SCOPE_MASK);
+               return 0;
+       }
+
+       switch(flags & LCK_TYPE_MASK) {
+       case LCK_UNLOCK:
+               lock_type = "UN";
+               break;
+       case LCK_NULL:
+               lock_type = "NL";
+               break;
+       case LCK_READ:
+               lock_type = "CR";
+               break;
+       case LCK_PREAD:
+               lock_type = "PR";
+               break;
+       case LCK_WRITE:
+               lock_type = "PW";
+               break;
+       case LCK_EXCL:
+               lock_type = "EX";
+               break;
+       default:
+               log_error("Unrecognised lock type: %u",
+                         flags & LCK_TYPE_MASK);
+               return 0;
+       }
+
+       log_very_verbose("Locking %s %s %s (%s%s%s%s%s%s%s) (0x%x)", lock_scope, lockname,
+                        lock_type, lock_scope,
+                        flags & LCK_NONBLOCK ? "|NONBLOCK" : "",
+                        flags & LCK_HOLD ? "|HOLD" : "",
+                        flags & LCK_LOCAL ? "|LOCAL" : "",
+                        flags & LCK_CLUSTER_VG ? "|CLUSTER" : "",
+                        flags & LCK_CACHE ? "|CACHE" : "",
+                        flags & LCK_ORIGIN_ONLY ? "|ORIGIN_ONLY" : "",
+                        flags);
+
+       /* Send a message to the cluster manager */
+       return _lock_for_cluster(cmd, clvmd_cmd, flags, lockname);
+}
+
+static int decode_lock_type(const char *response)
+{
+       if (!response)
+               return LCK_NULL;
+       else if (strcmp(response, "EX"))
+               return LCK_EXCL;
+       else if (strcmp(response, "CR"))
+               return LCK_READ;
+       else if (strcmp(response, "PR"))
+               return LCK_PREAD;
+
+       stack;
+       return 0;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+static int _query_resource(const char *resource, int *mode)
+#else
+int query_resource(const char *resource, int *mode)
+#endif
+{
+       int i, status, len, num_responses, saved_errno;
+       const char *node = "";
+       char *args;
+       lvm_response_t *response = NULL;
+
+       saved_errno = errno;
+       len = strlen(resource) + 3;
+       args = alloca(len);
+       strcpy(args + 2, resource);
+
+       args[0] = 0;
+       args[1] = LCK_CLUSTER_VG;
+
+       status = _cluster_request(CLVMD_CMD_LOCK_QUERY, node, args, len,
+                                 &response, &num_responses);
+       *mode = LCK_NULL;
+       for (i = 0; i < num_responses; i++) {
+               if (response[i].status == EHOSTDOWN)
+                       continue;
+
+               if (!response[i].response[0])
+                       continue;
+
+               /*
+                * All nodes should use CR, or exactly one node
+                * should held EX. (PR is obsolete)
+                * If two nodes node reports different locks,
+                * something is broken - just return more important mode.
+                */
+               if (decode_lock_type(response[i].response) > *mode)
+                       *mode = decode_lock_type(response[i].response);
+
+               log_debug("Lock held for %s, node %s : %s", resource,
+                         response[i].node, response[i].response);
+       }
+
+       _cluster_free_request(response, num_responses);
+       errno = saved_errno;
+
+       return status;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+static void _locking_end(void)
+#else
+void locking_end(void)
+#endif
+{
+       if (_clvmd_sock != -1 && close(_clvmd_sock))
+               stack;
+
+       _clvmd_sock = -1;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+static void _reset_locking(void)
+#else
+void reset_locking(void)
+#endif
+{
+       if (close(_clvmd_sock))
+               stack;
+
+       _clvmd_sock = _open_local_sock();
+       if (_clvmd_sock == -1)
+               stack;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+int init_cluster_locking(struct locking_type *locking, struct cmd_context *cmd)
+{
+       locking->lock_resource = _lock_resource;
+       locking->query_resource = _query_resource;
+       locking->fin_locking = _locking_end;
+       locking->reset_locking = _reset_locking;
+       locking->flags = LCK_PRE_MEMLOCK | LCK_CLUSTERED;
+
+       _clvmd_sock = _open_local_sock();
+       if (_clvmd_sock == -1)
+               return 0;
+
+       return 1;
+}
+#else
+int locking_init(int type, struct config_tree *cf, uint32_t *flags)
+{
+       _clvmd_sock = _open_local_sock();
+       if (_clvmd_sock == -1)
+               return 0;
+
+       /* Ask LVM to lock memory before calling us */
+       *flags |= LCK_PRE_MEMLOCK;
+       *flags |= LCK_CLUSTERED;
+
+       return 1;
+}
+#endif
diff --git a/lib/locking/external_locking.c b/lib/locking/external_locking.c
new file mode 100644 (file)
index 0000000..949d065
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "locking_types.h"
+#include "defaults.h"
+#include "sharedlib.h"
+#include "toolcontext.h"
+
+static void *_locking_lib = NULL;
+static void (*_reset_fn) (void) = NULL;
+static void (*_end_fn) (void) = NULL;
+static int (*_lock_fn) (struct cmd_context * cmd, const char *resource,
+                       uint32_t flags) = NULL;
+static int (*_init_fn) (int type, struct config_tree * cft,
+                       uint32_t *flags) = NULL;
+static int (*_lock_query_fn) (const char *resource, int *mode) = NULL;
+
+static int _lock_resource(struct cmd_context *cmd, const char *resource,
+                         uint32_t flags)
+{
+       if (_lock_fn)
+               return _lock_fn(cmd, resource, flags);
+       else
+               return 0;
+}
+
+static void _fin_external_locking(void)
+{
+       if (_end_fn)
+               _end_fn();
+
+       dlclose(_locking_lib);
+
+       _locking_lib = NULL;
+       _init_fn = NULL;
+       _end_fn = NULL;
+       _lock_fn = NULL;
+       _reset_fn = NULL;
+}
+
+static void _reset_external_locking(void)
+{
+       if (_reset_fn)
+               _reset_fn();
+}
+
+int init_external_locking(struct locking_type *locking, struct cmd_context *cmd)
+{
+       const char *libname;
+
+       if (_locking_lib) {
+               log_error("External locking already initialised");
+               return 1;
+       }
+
+       locking->lock_resource = _lock_resource;
+       locking->fin_locking = _fin_external_locking;
+       locking->reset_locking = _reset_external_locking;
+       locking->flags = 0;
+
+       libname = find_config_tree_str(cmd, "global/locking_library",
+                                      DEFAULT_LOCKING_LIB);
+
+       if (!(_locking_lib = load_shared_library(cmd, libname, "locking", 1)))
+               return_0;
+
+       /* Get the functions we need */
+       if (!(_init_fn = dlsym(_locking_lib, "locking_init")) ||
+           !(_lock_fn = dlsym(_locking_lib, "lock_resource")) ||
+           !(_reset_fn = dlsym(_locking_lib, "reset_locking")) ||
+           !(_end_fn = dlsym(_locking_lib, "locking_end"))) {
+               log_error("Shared library %s does not contain locking "
+                         "functions", libname);
+               dlclose(_locking_lib);
+               _locking_lib = NULL;
+               return 0;
+       }
+
+       if (!(_lock_query_fn = dlsym(_locking_lib, "query_resource")))
+               log_warn("WARNING: %s: _query_resource() missing: "
+                        "Using inferior activation method.", libname);
+
+       log_verbose("Loaded external locking library %s", libname);
+       return _init_fn(2, cmd->cft, &locking->flags);
+}
diff --git a/lib/locking/file_locking.c b/lib/locking/file_locking.c
new file mode 100644 (file)
index 0000000..9137a30
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "activate.h"
+#include "config.h"
+#include "defaults.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "lvmcache.h"
+
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <signal.h>
+
+struct lock_list {
+       struct dm_list list;
+       int lf;
+       char *res;
+};
+
+static struct dm_list _lock_list;
+static char _lock_dir[NAME_LEN];
+static int _prioritise_write_locks;
+
+static sig_t _oldhandler;
+static sigset_t _fullsigset, _intsigset;
+static volatile sig_atomic_t _handler_installed;
+
+static void _undo_flock(const char *file, int fd)
+{
+       struct stat buf1, buf2;
+
+       log_debug("_undo_flock %s", file);
+       if (!flock(fd, LOCK_NB | LOCK_EX) &&
+           !stat(file, &buf1) &&
+           !fstat(fd, &buf2) &&
+           is_same_inode(buf1, buf2))
+               if (unlink(file))
+                       log_sys_error("unlink", file);
+
+       if (close(fd) < 0)
+               log_sys_error("close", file);
+}
+
+static int _release_lock(const char *file, int unlock)
+{
+       struct lock_list *ll;
+       struct dm_list *llh, *llt;
+
+       dm_list_iterate_safe(llh, llt, &_lock_list) {
+               ll = dm_list_item(llh, struct lock_list);
+
+               if (!file || !strcmp(ll->res, file)) {
+                       dm_list_del(llh);
+                       if (unlock) {
+                               log_very_verbose("Unlocking %s", ll->res);
+                               if (flock(ll->lf, LOCK_NB | LOCK_UN))
+                                       log_sys_error("flock", ll->res);
+                       }
+
+                       _undo_flock(ll->res, ll->lf);
+
+                       dm_free(ll->res);
+                       dm_free(llh);
+
+                       if (file)
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+static void _fin_file_locking(void)
+{
+       _release_lock(NULL, 1);
+}
+
+static void _reset_file_locking(void)
+{
+       _release_lock(NULL, 0);
+}
+
+static void _remove_ctrl_c_handler(void)
+{
+       siginterrupt(SIGINT, 0);
+       if (!_handler_installed)
+               return;
+
+       _handler_installed = 0;
+
+       sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
+       if (signal(SIGINT, _oldhandler) == SIG_ERR)
+               log_sys_error("signal", "_remove_ctrl_c_handler");
+}
+
+static void _trap_ctrl_c(int sig __attribute__((unused)))
+{
+       _remove_ctrl_c_handler();
+       log_error("CTRL-c detected: giving up waiting for lock");
+}
+
+static void _install_ctrl_c_handler(void)
+{
+       _handler_installed = 1;
+
+       if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
+               _handler_installed = 0;
+               return;
+       }
+
+       sigprocmask(SIG_SETMASK, &_intsigset, NULL);
+       siginterrupt(SIGINT, 1);
+}
+
+static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock)
+{
+       int r = 1;
+       int old_errno;
+       struct stat buf1, buf2;
+
+       log_debug("_do_flock %s %c%c",
+                 file, operation == LOCK_EX ? 'W' : 'R', nonblock ? ' ' : 'B');
+       do {
+               if ((*fd > -1) && close(*fd))
+                       log_sys_error("close", file);
+
+               if ((*fd = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
+                       log_sys_error("open", file);
+                       return 0;
+               }
+
+               if (nonblock)
+                       operation |= LOCK_NB;
+               else
+                       _install_ctrl_c_handler();
+
+               r = flock(*fd, operation);
+               old_errno = errno;
+               if (!nonblock)
+                       _remove_ctrl_c_handler();
+
+               if (r) {
+                       errno = old_errno;
+                       log_sys_error("flock", file);
+                       close(*fd);
+                       return 0;
+               }
+
+               if (!stat(file, &buf1) && !fstat(*fd, &buf2) &&
+                   is_same_inode(buf1, buf2))
+                       return 1;
+       } while (!nonblock);
+
+       return_0;
+}
+
+#define AUX_LOCK_SUFFIX ":aux"
+
+static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
+{
+       int r, fd_aux = -1;
+       char *file_aux = alloca(strlen(file) + sizeof(AUX_LOCK_SUFFIX));
+
+       strcpy(file_aux, file);
+       strcat(file_aux, AUX_LOCK_SUFFIX);
+
+       if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
+               if (operation == LOCK_EX) {
+                       r = _do_flock(file, fd, operation, nonblock);
+                       _undo_flock(file_aux, fd_aux);
+               } else {
+                       _undo_flock(file_aux, fd_aux);
+                       r = _do_flock(file, fd, operation, nonblock);
+               }
+       }
+
+       return r;
+}
+
+static int _lock_file(const char *file, uint32_t flags)
+{
+       int operation;
+       uint32_t nonblock = flags & LCK_NONBLOCK;
+       int r;
+
+       struct lock_list *ll;
+       char state;
+
+       switch (flags & LCK_TYPE_MASK) {
+       case LCK_READ:
+               operation = LOCK_SH;
+               state = 'R';
+               break;
+       case LCK_WRITE:
+               operation = LOCK_EX;
+               state = 'W';
+               break;
+       case LCK_UNLOCK:
+               return _release_lock(file, 1);
+       default:
+               log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
+               return 0;
+       }
+
+       if (!(ll = dm_malloc(sizeof(struct lock_list))))
+               return_0;
+
+       if (!(ll->res = dm_strdup(file))) {
+               dm_free(ll);
+               return_0;
+       }
+
+       ll->lf = -1;
+
+       log_very_verbose("Locking %s %c%c", ll->res, state,
+                        nonblock ? ' ' : 'B');
+
+       (void) dm_prepare_selinux_context(file, S_IFREG);
+       if (_prioritise_write_locks)
+               r = _do_write_priority_flock(file, &ll->lf, operation, nonblock);
+       else 
+               r = _do_flock(file, &ll->lf, operation, nonblock);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       if (r)
+               dm_list_add(&_lock_list, &ll->list);
+       else {
+               dm_free(ll->res);
+               dm_free(ll);
+               stack;
+       }
+
+       return r;
+}
+
+static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
+                              uint32_t flags)
+{
+       char lockfile[PATH_MAX];
+       unsigned origin_only = (flags & LCK_ORIGIN_ONLY) ? 1 : 0;
+
+       switch (flags & LCK_SCOPE_MASK) {
+       case LCK_VG:
+               /* Skip cache refresh for VG_GLOBAL - the caller handles it */
+               if (strcmp(resource, VG_GLOBAL))
+                       lvmcache_drop_metadata(resource, 0);
+
+               /* LCK_CACHE does not require a real lock */
+               if (flags & LCK_CACHE)
+                       break;
+
+               if (is_orphan_vg(resource) || is_global_vg(resource))
+                       dm_snprintf(lockfile, sizeof(lockfile),
+                                    "%s/P_%s", _lock_dir, resource + 1);
+               else
+                       dm_snprintf(lockfile, sizeof(lockfile),
+                                    "%s/V_%s", _lock_dir, resource);
+
+               if (!_lock_file(lockfile, flags))
+                       return_0;
+               break;
+       case LCK_LV:
+               switch (flags & LCK_TYPE_MASK) {
+               case LCK_UNLOCK:
+                       log_very_verbose("Unlocking LV %s%s", resource, origin_only ? " without snapshots" : "");
+                       if (!lv_resume_if_active(cmd, resource, origin_only))
+                               return 0;
+                       break;
+               case LCK_NULL:
+                       log_very_verbose("Locking LV %s (NL)", resource);
+                       if (!lv_deactivate(cmd, resource))
+                               return 0;
+                       break;
+               case LCK_READ:
+                       log_very_verbose("Locking LV %s (R)", resource);
+                       if (!lv_activate_with_filter(cmd, resource, 0))
+                               return 0;
+                       break;
+               case LCK_PREAD:
+                       log_very_verbose("Locking LV %s (PR) - ignored", resource);
+                       break;
+               case LCK_WRITE:
+                       log_very_verbose("Locking LV %s (W)%s", resource, origin_only ? " without snapshots" : "");
+                       if (!lv_suspend_if_active(cmd, resource, origin_only))
+                               return 0;
+                       break;
+               case LCK_EXCL:
+                       log_very_verbose("Locking LV %s (EX)", resource);
+                       if (!lv_activate_with_filter(cmd, resource, 1))
+                               return 0;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               log_error("Unrecognised lock scope: %d",
+                         flags & LCK_SCOPE_MASK);
+               return 0;
+       }
+
+       return 1;
+}
+
+int init_file_locking(struct locking_type *locking, struct cmd_context *cmd)
+{
+       locking->lock_resource = _file_lock_resource;
+       locking->reset_locking = _reset_file_locking;
+       locking->fin_locking = _fin_file_locking;
+       locking->flags = 0;
+       int r;
+
+       /* Get lockfile directory from config file */
+       strncpy(_lock_dir, find_config_tree_str(cmd, "global/locking_dir",
+                                               DEFAULT_LOCK_DIR),
+               sizeof(_lock_dir));
+
+       _prioritise_write_locks =
+           find_config_tree_bool(cmd, "global/prioritise_write_locks",
+                                 DEFAULT_PRIORITISE_WRITE_LOCKS);
+
+       (void) dm_prepare_selinux_context(_lock_dir, S_IFDIR);
+       r = dm_create_dir(_lock_dir);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       if (!r)
+               return 0;
+
+       /* Trap a read-only file system */
+       if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
+               return 0;
+
+       dm_list_init(&_lock_list);
+
+       if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
+               log_sys_error("sigfillset", "init_file_locking");
+               return 0;
+       }
+
+       if (sigdelset(&_intsigset, SIGINT)) {
+               log_sys_error("sigdelset", "init_file_locking");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/lib/locking/locking.c b/lib/locking/locking.c
new file mode 100644 (file)
index 0000000..60eaab3
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "toolcontext.h"
+#include "memlock.h"
+#include "defaults.h"
+#include "lvmcache.h"
+
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <unistd.h>
+
+static struct locking_type _locking;
+static sigset_t _oldset;
+
+static int _vg_lock_count = 0;         /* Number of locks held */
+static int _vg_write_lock_held = 0;    /* VG write lock held? */
+static int _signals_blocked = 0;
+static int _blocking_supported = 0;
+
+static volatile sig_atomic_t _sigint_caught = 0;
+static volatile sig_atomic_t _handler_installed;
+static struct sigaction _oldhandler;
+static int _oldmasked;
+
+typedef enum {
+        LV_NOOP,
+        LV_SUSPEND,
+        LV_RESUME
+} lv_operation_t;
+
+static void _catch_sigint(int unused __attribute__((unused)))
+{
+       _sigint_caught = 1;
+}
+
+int sigint_caught(void) {
+       return _sigint_caught;
+}
+
+void sigint_clear(void)
+{
+       _sigint_caught = 0;
+}
+
+/*
+ * Temporarily allow keyboard interrupts to be intercepted and noted;
+ * saves interrupt handler state for sigint_restore().  Users should
+ * use the sigint_caught() predicate to check whether interrupt was
+ * requested and act appropriately.  Interrupt flags are never
+ * cleared automatically by this code, but the tools clear the flag
+ * before running each command in lvm_run_command().  All other places
+ * where the flag needs to be cleared need to call sigint_clear().
+ */
+
+void sigint_allow(void)
+{
+       struct sigaction handler;
+       sigset_t sigs;
+
+       /*
+        * Do not overwrite the backed-up handler data -
+        * just increase nesting count.
+        */
+       if (_handler_installed) {
+               _handler_installed++;
+               return;
+       }
+
+       /* Grab old sigaction for SIGINT: shall not fail. */
+       sigaction(SIGINT, NULL, &handler);
+       handler.sa_flags &= ~SA_RESTART; /* Clear restart flag */
+       handler.sa_handler = _catch_sigint;
+
+       _handler_installed = 1;
+
+       /* Override the signal handler: shall not fail. */
+       sigaction(SIGINT, &handler, &_oldhandler);
+
+       /* Unmask SIGINT.  Remember to mask it again on restore. */
+       sigprocmask(0, NULL, &sigs);
+       if ((_oldmasked = sigismember(&sigs, SIGINT))) {
+               sigdelset(&sigs, SIGINT);
+               sigprocmask(SIG_SETMASK, &sigs, NULL);
+       }
+}
+
+void sigint_restore(void)
+{
+       if (!_handler_installed)
+               return;
+
+       if (_handler_installed > 1) {
+               _handler_installed--;
+               return;
+       }
+
+       /* Nesting count went down to 0. */
+       _handler_installed = 0;
+
+       if (_oldmasked) {
+               sigset_t sigs;
+               sigprocmask(0, NULL, &sigs);
+               sigaddset(&sigs, SIGINT);
+               sigprocmask(SIG_SETMASK, &sigs, NULL);
+       }
+
+       sigaction(SIGINT, &_oldhandler, NULL);
+}
+
+static void _block_signals(uint32_t flags __attribute__((unused)))
+{
+       sigset_t set;
+
+       if (_signals_blocked)
+               return;
+
+       if (sigfillset(&set)) {
+               log_sys_error("sigfillset", "_block_signals");
+               return;
+       }
+
+       if (sigprocmask(SIG_SETMASK, &set, &_oldset)) {
+               log_sys_error("sigprocmask", "_block_signals");
+               return;
+       }
+
+       _signals_blocked = 1;
+}
+
+static void _unblock_signals(void)
+{
+       /* Don't unblock signals while any locks are held */
+       if (!_signals_blocked || _vg_lock_count)
+               return;
+
+       if (sigprocmask(SIG_SETMASK, &_oldset, NULL)) {
+               log_sys_error("sigprocmask", "_block_signals");
+               return;
+       }
+
+       _signals_blocked = 0;
+}
+
+static void _lock_memory(struct cmd_context *cmd, lv_operation_t lv_op)
+{
+       if (!(_locking.flags & LCK_PRE_MEMLOCK))
+               return;
+
+       if (lv_op == LV_SUSPEND)
+               memlock_inc(cmd);
+}
+
+static void _unlock_memory(struct cmd_context *cmd, lv_operation_t lv_op)
+{
+       if (!(_locking.flags & LCK_PRE_MEMLOCK))
+               return;
+
+       if (lv_op == LV_RESUME)
+               memlock_dec(cmd);
+}
+
+void reset_locking(void)
+{
+       int was_locked = _vg_lock_count;
+
+       _vg_lock_count = 0;
+       _vg_write_lock_held = 0;
+
+       if (_locking.reset_locking)
+               _locking.reset_locking();
+
+       if (was_locked)
+               _unblock_signals();
+}
+
+static void _update_vg_lock_count(const char *resource, uint32_t flags)
+{
+       /* Ignore locks not associated with updating VG metadata */
+       if ((flags & LCK_SCOPE_MASK) != LCK_VG ||
+           (flags & LCK_CACHE) ||
+           !strcmp(resource, VG_GLOBAL))
+               return;
+
+       if ((flags & LCK_TYPE_MASK) == LCK_UNLOCK)
+               _vg_lock_count--;
+       else
+               _vg_lock_count++;
+
+       /* We don't bother to reset this until all VG locks are dropped */
+       if ((flags & LCK_TYPE_MASK) == LCK_WRITE)
+               _vg_write_lock_held = 1;
+       else if (!_vg_lock_count)
+               _vg_write_lock_held = 0;
+}
+
+/*
+ * Select a locking type
+ * type: locking type; if < 0, then read config tree value
+ */
+int init_locking(int type, struct cmd_context *cmd, int suppress_messages)
+{
+       if (ignorelockingfailure() && getenv("LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES"))
+               suppress_messages = 1;
+
+       if (type < 0)
+               type = find_config_tree_int(cmd, "global/locking_type", 1);
+
+       _blocking_supported = find_config_tree_int(cmd,
+           "global/wait_for_locks", DEFAULT_WAIT_FOR_LOCKS);
+
+       switch (type) {
+       case 0:
+               init_no_locking(&_locking, cmd);
+               log_warn("WARNING: Locking disabled. Be careful! "
+                         "This could corrupt your metadata.");
+               return 1;
+
+       case 1:
+               log_very_verbose("%sFile-based locking selected.",
+                                _blocking_supported ? "" : "Non-blocking ");
+
+               if (!init_file_locking(&_locking, cmd)) {
+                       log_error_suppress(suppress_messages,
+                                          "File-based locking initialisation failed.");
+                       break;
+               }
+               return 1;
+
+#ifdef HAVE_LIBDL
+       case 2:
+               if (!is_static()) {
+                       log_very_verbose("External locking selected.");
+                       if (init_external_locking(&_locking, cmd))
+                               return 1;
+               }
+               if (!find_config_tree_int(cmd, "locking/fallback_to_clustered_locking",
+                           find_config_tree_int(cmd, "global/fallback_to_clustered_locking",
+                                                DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING))) {
+                       log_error("External locking initialisation failed.");
+                       break;
+               }
+#endif
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+               log_very_verbose("Falling back to internal clustered locking.");
+               /* Fall through */
+
+       case 3:
+               log_very_verbose("Cluster locking selected.");
+               if (!init_cluster_locking(&_locking, cmd)) {
+                       log_error_suppress(suppress_messages,
+                                          "Internal cluster locking initialisation failed.");
+                       break;
+               }
+               return 1;
+#endif
+
+       case 4:
+               log_verbose("Read-only locking selected. "
+                           "Only read operations permitted.");
+               if (!init_readonly_locking(&_locking, cmd))
+                       break;
+               return 1;
+
+       default:
+               log_error("Unknown locking type requested.");
+               return 0;
+       }
+
+       if ((type == 2 || type == 3) &&
+           find_config_tree_int(cmd, "locking/fallback_to_local_locking",
+                   find_config_tree_int(cmd, "global/fallback_to_local_locking",
+                                        DEFAULT_FALLBACK_TO_LOCAL_LOCKING))) {
+               log_warn_suppress(suppress_messages, "WARNING: Falling back to local file-based locking.");
+               log_warn_suppress(suppress_messages,
+                                 "Volume Groups with the clustered attribute will "
+                                 "be inaccessible.");
+               if (init_file_locking(&_locking, cmd))
+                       return 1;
+               else
+                       log_error_suppress(suppress_messages,
+                                          "File-based locking initialisation failed.");
+       }
+
+       if (!ignorelockingfailure())
+               return 0;
+
+       log_verbose("Locking disabled - only read operations permitted.");
+       init_readonly_locking(&_locking, cmd);
+
+       return 1;
+}
+
+void fin_locking(void)
+{
+       _locking.fin_locking();
+}
+
+/*
+ * Does the LVM1 driver know of this VG name?
+ */
+int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname)
+{
+       struct stat info;
+       char path[PATH_MAX];
+
+       /* We'll allow operations on orphans */
+       if (is_orphan_vg(vgname) || is_global_vg(vgname))
+               return 1;
+
+       /* LVM1 is only present in 2.4 kernels. */
+       if (strncmp(cmd->kernel_vsn, "2.4.", 4))
+               return 1;
+
+       if (dm_snprintf(path, sizeof(path), "%s/lvm/VGs/%s", cmd->proc_dir,
+                        vgname) < 0) {
+               log_error("LVM1 proc VG pathname too long for %s", vgname);
+               return 0;
+       }
+
+       if (stat(path, &info) == 0) {
+               log_error("%s exists: Is the original LVM driver using "
+                         "this volume group?", path);
+               return 0;
+       } else if (errno != ENOENT && errno != ENOTDIR) {
+               log_sys_error("stat", path);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * VG locking is by VG name.
+ * FIXME This should become VG uuid.
+ */
+static int _lock_vol(struct cmd_context *cmd, const char *resource,
+                    uint32_t flags, lv_operation_t lv_op)
+{
+       int ret = 0;
+
+       _block_signals(flags);
+       _lock_memory(cmd, lv_op);
+
+       assert(resource);
+
+       if (!*resource) {
+               log_error(INTERNAL_ERROR "Use of P_orphans is deprecated.");
+               return 0;
+       }
+
+       if ((is_orphan_vg(resource) || is_global_vg(resource)) && (flags & LCK_CACHE)) {
+               log_error(INTERNAL_ERROR "P_%s referenced", resource);
+               return 0;
+       }
+
+       if (cmd->metadata_read_only &&
+           ((flags & LCK_TYPE_MASK) == LCK_WRITE) &&
+           strcmp(resource, VG_GLOBAL)) {
+               log_error("Operation prohibited while global/metadata_read_only is set.");
+               return 0;
+       }
+
+       if ((ret = _locking.lock_resource(cmd, resource, flags))) {
+               if ((flags & LCK_SCOPE_MASK) == LCK_VG &&
+                   !(flags & LCK_CACHE)) {
+                       if ((flags & LCK_TYPE_MASK) == LCK_UNLOCK)
+                               lvmcache_unlock_vgname(resource);
+                       else
+                               lvmcache_lock_vgname(resource, (flags & LCK_TYPE_MASK)
+                                                               == LCK_READ);
+                       dev_reset_error_count(cmd);
+               }
+
+               _update_vg_lock_count(resource, flags);
+       } else
+               stack;
+
+       _unlock_memory(cmd, lv_op);
+       _unblock_signals();
+
+       return ret;
+}
+
+int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags)
+{
+       char resource[258] __attribute__((aligned(8)));
+       lv_operation_t lv_op;
+
+       switch (flags & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) {
+               case LCK_LV_SUSPEND:
+                               lv_op = LV_SUSPEND;
+                               break;
+               case LCK_LV_RESUME:
+                               lv_op = LV_RESUME;
+                               break;
+               default:        lv_op = LV_NOOP;
+       }
+
+
+       if (flags == LCK_NONE) {
+               log_debug(INTERNAL_ERROR "%s: LCK_NONE lock requested", vol);
+               return 1;
+       }
+
+       switch (flags & LCK_SCOPE_MASK) {
+       case LCK_VG:
+               if (!_blocking_supported)
+                       flags |= LCK_NONBLOCK;
+
+               /* Global VG_ORPHANS lock covers all orphan formats. */
+               if (is_orphan_vg(vol))
+                       vol = VG_ORPHANS;
+               /* VG locks alphabetical, ORPHAN lock last */
+               if (((flags & LCK_TYPE_MASK) != LCK_UNLOCK) &&
+                        !(flags & LCK_CACHE) &&
+                        !lvmcache_verify_lock_order(vol))
+                       return 0;
+
+               /* Lock VG to change on-disk metadata. */
+               /* If LVM1 driver knows about the VG, it can't be accessed. */
+               if (!check_lvm1_vg_inactive(cmd, vol))
+                       return 0;
+               break;
+       case LCK_LV:
+               /* All LV locks are non-blocking. */
+               flags |= LCK_NONBLOCK;
+               break;
+       default:
+               log_error("Unrecognised lock scope: %d",
+                         flags & LCK_SCOPE_MASK);
+               return 0;
+       }
+
+       strncpy(resource, vol, sizeof(resource));
+
+       if (!_lock_vol(cmd, resource, flags, lv_op))
+               return 0;
+
+       /*
+        * If a real lock was acquired (i.e. not LCK_CACHE),
+        * perform an immediate unlock unless LCK_HOLD was requested.
+        */
+       if (!(flags & LCK_CACHE) && !(flags & LCK_HOLD) &&
+           ((flags & LCK_TYPE_MASK) != LCK_UNLOCK)) {
+               if (!_lock_vol(cmd, resource,
+                              (flags & ~LCK_TYPE_MASK) | LCK_UNLOCK, lv_op))
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* Unlock list of LVs */
+int resume_lvs(struct cmd_context *cmd, struct dm_list *lvs)
+{
+       struct lv_list *lvl;
+       int r = 1;
+
+       dm_list_iterate_items(lvl, lvs)
+               if (!resume_lv(cmd, lvl->lv)) {
+                       r = 0;
+                       stack;
+               }
+
+       return r;
+}
+
+/* Lock a list of LVs */
+int suspend_lvs(struct cmd_context *cmd, struct dm_list *lvs)
+{
+       struct dm_list *lvh;
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, lvs) {
+               if (!suspend_lv(cmd, lvl->lv)) {
+                       log_error("Failed to suspend %s", lvl->lv->name);
+                       dm_list_uniterate(lvh, lvs, &lvl->list) {
+                               lvl = dm_list_item(lvh, struct lv_list);
+                               if (!resume_lv(cmd, lvl->lv))
+                                       stack;
+                       }
+
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/* Lock a list of LVs */
+int activate_lvs(struct cmd_context *cmd, struct dm_list *lvs, unsigned exclusive)
+{
+       struct dm_list *lvh;
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, lvs) {
+               if (!exclusive) {
+                       if (!activate_lv(cmd, lvl->lv)) {
+                               log_error("Failed to activate %s", lvl->lv->name);
+                               return 0;
+                       }
+               } else if (!activate_lv_excl(cmd, lvl->lv)) {
+                       log_error("Failed to activate %s", lvl->lv->name);
+                       dm_list_uniterate(lvh, lvs, &lvl->list) {
+                               lvl = dm_list_item(lvh, struct lv_list);
+                               if (!activate_lv(cmd, lvl->lv))
+                                       stack;
+                       }
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+int vg_write_lock_held(void)
+{
+       return _vg_write_lock_held;
+}
+
+int locking_is_clustered(void)
+{
+       return (_locking.flags & LCK_CLUSTERED) ? 1 : 0;
+}
+
+int remote_lock_held(const char *vol)
+{
+       int mode = LCK_NULL;
+
+       if (!locking_is_clustered())
+               return 0;
+
+       if (!_locking.query_resource)
+               return -1;
+
+       /*
+        * If an error occured, expect that volume is active
+        */
+       if (!_locking.query_resource(vol, &mode)) {
+               stack;
+               return 1;
+       }
+
+       return mode == LCK_NULL ? 0 : 1;
+}
diff --git a/lib/locking/locking.h b/lib/locking/locking.h
new file mode 100644 (file)
index 0000000..8893500
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_LOCKING_H
+#define _LVM_LOCKING_H
+
+#include "uuid.h"
+#include "config.h"
+
+int init_locking(int type, struct cmd_context *cmd, int suppress_messages);
+void fin_locking(void);
+void reset_locking(void);
+int vg_write_lock_held(void);
+int locking_is_clustered(void);
+
+int remote_lock_held(const char *vol);
+
+/*
+ * LCK_VG:
+ *   Lock/unlock on-disk volume group data.
+ *   Use VG_ORPHANS to lock all orphan PVs.
+ *   Use VG_GLOBAL as a global lock and to wipe the internal cache.
+ *   char *vol holds volume group name.
+ *   Set LCK_CACHE flag when manipulating 'vol' metadata in the internal cache.
+ *   (Like commit, revert or invalidate metadata.)
+ *   If more than one lock needs to be held simultaneously, they must be
+ *   acquired in alphabetical order of 'vol' (to avoid deadlocks), with
+ *   VG_ORPHANS last.
+ *
+ * LCK_LV:
+ *   Lock/unlock an individual logical volume
+ *   char *vol holds lvid
+ */
+int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags);
+
+/*
+ * Internal locking representation.
+ *   LCK_VG: Uses prefix V_ unless the vol begins with # (i.e. #global or #orphans)
+ *           or the LCK_CACHE flag is set when it uses the prefix P_.
+ * If LCK_CACHE is set, we do not take out a real lock.
+ * NB In clustered situations, LCK_CACHE is not propagated directly to remote nodes.
+ * (It can be deduced from lock name.)
+ */
+
+/*
+ * Does the LVM1 driver have this VG active?
+ */
+int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
+
+/*
+ * Lock type - these numbers are the same as VMS and the IBM DLM
+ */
+#define LCK_TYPE_MASK  0x00000007U
+
+#define LCK_NULL       0x00000000U     /* LCK$_NLMODE */
+#define LCK_READ       0x00000001U     /* LCK$_CRMODE */
+                                       /* LCK$_CWMODE */
+#define LCK_PREAD       0x00000003U    /* LCK$_PRMODE */
+#define LCK_WRITE      0x00000004U     /* LCK$_PWMODE */
+#define LCK_EXCL       0x00000005U     /* LCK$_EXMODE */
+#define LCK_UNLOCK      0x00000006U    /* This is ours */
+
+/*
+ * Lock flags - these numbers are the same as DLM
+ */
+#define LCKF_NOQUEUE   0x00000001U     /* LKF$_NOQUEUE */
+#define LCKF_CONVERT   0x00000004U     /* LKF$_CONVERT */
+
+/*
+ * Lock scope
+ */
+#define LCK_SCOPE_MASK 0x00000008U
+#define LCK_VG         0x00000000U
+#define LCK_LV         0x00000008U
+
+/*
+ * Lock bits
+ */
+#define LCK_NONBLOCK   0x00000010U     /* Don't block waiting for lock? */
+#define LCK_HOLD       0x00000020U     /* Hold lock when lock_vol returns? */
+#define LCK_LOCAL      0x00000040U     /* Don't propagate to other nodes */
+#define LCK_CLUSTER_VG 0x00000080U     /* VG is clustered */
+#define LCK_CACHE      0x00000100U     /* Operation on cache only using P_ lock */
+#define LCK_ORIGIN_ONLY        0x00000200U     /* Operation should bypass any snapshots */
+
+/*
+ * Additional lock bits for cluster communication via args[1]
+ */
+#define LCK_PARTIAL_MODE               0x01    /* Partial activation? */
+#define LCK_MIRROR_NOSYNC_MODE         0x02    /* Mirrors don't require sync */
+#define LCK_DMEVENTD_MONITOR_MODE      0x04    /* Register with dmeventd */
+#define LCK_CONVERT                    0x08    /* Convert existing lock */
+#define LCK_ORIGIN_ONLY_MODE           0x20    /* Same as above */
+
+/*
+ * Special cases of VG locks.
+ */
+#define VG_ORPHANS     "#orphans"
+#define VG_GLOBAL      "#global"
+
+/*
+ * Common combinations
+ */
+#define LCK_NONE               (LCK_VG | LCK_NULL)
+
+#define LCK_VG_READ            (LCK_VG | LCK_READ | LCK_HOLD)
+#define LCK_VG_WRITE           (LCK_VG | LCK_WRITE | LCK_HOLD)
+#define LCK_VG_UNLOCK          (LCK_VG | LCK_UNLOCK)
+#define LCK_VG_DROP_CACHE      (LCK_VG | LCK_WRITE | LCK_CACHE)
+
+/* FIXME: LCK_HOLD abused here */
+#define LCK_VG_COMMIT          (LCK_VG | LCK_WRITE | LCK_CACHE | LCK_HOLD)
+#define LCK_VG_REVERT          (LCK_VG | LCK_READ  | LCK_CACHE | LCK_HOLD)
+
+#define LCK_VG_BACKUP          (LCK_VG | LCK_CACHE)
+
+#define LCK_LV_EXCLUSIVE       (LCK_LV | LCK_EXCL)
+#define LCK_LV_SUSPEND         (LCK_LV | LCK_WRITE)
+#define LCK_LV_RESUME          (LCK_LV | LCK_UNLOCK)
+#define LCK_LV_ACTIVATE                (LCK_LV | LCK_READ)
+#define LCK_LV_DEACTIVATE      (LCK_LV | LCK_NULL)
+
+#define LCK_MASK (LCK_TYPE_MASK | LCK_SCOPE_MASK)
+
+#define LCK_LV_CLUSTERED(lv)   \
+       (vg_is_clustered((lv)->vg) ? LCK_CLUSTER_VG : 0)
+
+#define lock_lv_vol(cmd, lv, flags)    \
+       (find_replicator_vgs((lv)) ? \
+               lock_vol(cmd, (lv)->lvid.s, flags | LCK_LV_CLUSTERED(lv)) : \
+               0)
+
+#define unlock_vg(cmd, vol)    lock_vol(cmd, vol, LCK_VG_UNLOCK)
+#define unlock_and_free_vg(cmd, vg, vol) \
+       do { \
+               unlock_vg(cmd, vol); \
+               free_vg(vg); \
+       } while (0)
+
+#define resume_lv(cmd, lv)     lock_lv_vol(cmd, lv, LCK_LV_RESUME)
+#define resume_lv_origin(cmd, lv)      lock_lv_vol(cmd, lv, LCK_LV_RESUME | LCK_ORIGIN_ONLY)
+#define suspend_lv(cmd, lv)    lock_lv_vol(cmd, lv, LCK_LV_SUSPEND | LCK_HOLD)
+#define suspend_lv_origin(cmd, lv)     lock_lv_vol(cmd, lv, LCK_LV_SUSPEND | LCK_HOLD | LCK_ORIGIN_ONLY)
+#define deactivate_lv(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_DEACTIVATE)
+#define activate_lv(cmd, lv)   lock_lv_vol(cmd, lv, LCK_LV_ACTIVATE | LCK_HOLD)
+#define activate_lv_excl(cmd, lv)      \
+                               lock_lv_vol(cmd, lv, LCK_LV_EXCLUSIVE | LCK_HOLD)
+#define activate_lv_local(cmd, lv)     \
+       lock_lv_vol(cmd, lv, LCK_LV_ACTIVATE | LCK_HOLD | LCK_LOCAL)
+#define deactivate_lv_local(cmd, lv)   \
+       lock_lv_vol(cmd, lv, LCK_LV_DEACTIVATE | LCK_LOCAL)
+#define drop_cached_metadata(vg)       \
+       lock_vol((vg)->cmd, (vg)->name, LCK_VG_DROP_CACHE)
+#define remote_commit_cached_metadata(vg)      \
+       lock_vol((vg)->cmd, (vg)->name, LCK_VG_COMMIT)
+#define remote_revert_cached_metadata(vg)      \
+       lock_vol((vg)->cmd, (vg)->name, LCK_VG_REVERT)
+#define remote_backup_metadata(vg)     \
+       lock_vol((vg)->cmd, (vg)->name, LCK_VG_BACKUP)
+
+/* Process list of LVs */
+int suspend_lvs(struct cmd_context *cmd, struct dm_list *lvs);
+int resume_lvs(struct cmd_context *cmd, struct dm_list *lvs);
+int activate_lvs(struct cmd_context *cmd, struct dm_list *lvs, unsigned exclusive);
+
+/* Interrupt handling */
+void sigint_clear(void);
+void sigint_allow(void);
+void sigint_restore(void);
+int sigint_caught(void);
+
+#endif
diff --git a/lib/locking/locking_types.h b/lib/locking/locking_types.h
new file mode 100644 (file)
index 0000000..c3ca16b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "metadata.h"
+#include "config.h"
+
+typedef int (*lock_resource_fn) (struct cmd_context * cmd, const char *resource,
+                                uint32_t flags);
+typedef int (*query_resource_fn) (const char *resource, int *mode);
+
+typedef void (*fin_lock_fn) (void);
+typedef void (*reset_lock_fn) (void);
+
+#define LCK_PRE_MEMLOCK        0x00000001      /* Is memlock() needed before calls? */
+#define LCK_CLUSTERED  0x00000002
+
+struct locking_type {
+       uint32_t flags;
+       lock_resource_fn lock_resource;
+       query_resource_fn query_resource;
+
+       reset_lock_fn reset_locking;
+       fin_lock_fn fin_locking;
+};
+
+/*
+ * Locking types
+ */
+int init_no_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_readonly_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_file_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_external_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_cluster_locking(struct locking_type *locking, struct cmd_context *cmd);
diff --git a/lib/locking/no_locking.c b/lib/locking/no_locking.c
new file mode 100644 (file)
index 0000000..8c4110f
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "lvm-string.h"
+#include "activate.h"
+
+#include <signal.h>
+
+/*
+ * No locking
+ */
+
+static void _no_fin_locking(void)
+{
+}
+
+static void _no_reset_locking(void)
+{
+}
+
+static int _no_lock_resource(struct cmd_context *cmd, const char *resource,
+                            uint32_t flags)
+{
+       switch (flags & LCK_SCOPE_MASK) {
+       case LCK_VG:
+               break;
+       case LCK_LV:
+               switch (flags & LCK_TYPE_MASK) {
+               case LCK_NULL:
+                       return lv_deactivate(cmd, resource);
+               case LCK_UNLOCK:
+                       return lv_resume_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1: 0);
+               case LCK_READ:
+                       return lv_activate_with_filter(cmd, resource, 0);
+               case LCK_WRITE:
+                       return lv_suspend_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1 : 0);
+               case LCK_EXCL:
+                       return lv_activate_with_filter(cmd, resource, 1);
+               default:
+                       break;
+               }
+               break;
+       default:
+               log_error("Unrecognised lock scope: %d",
+                         flags & LCK_SCOPE_MASK);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _readonly_lock_resource(struct cmd_context *cmd,
+                                  const char *resource,
+                                  uint32_t flags)
+{
+       if ((flags & LCK_TYPE_MASK) == LCK_WRITE &&
+           (flags & LCK_SCOPE_MASK) == LCK_VG &&
+           !(flags & LCK_CACHE) &&
+           strcmp(resource, VG_GLOBAL)) {
+               log_error("Write locks are prohibited with read-only locking.");
+               return 0;
+       }
+
+       return _no_lock_resource(cmd, resource, flags);
+}
+
+int init_no_locking(struct locking_type *locking, struct cmd_context *cmd __attribute__((unused)))
+{
+       locking->lock_resource = _no_lock_resource;
+       locking->reset_locking = _no_reset_locking;
+       locking->fin_locking = _no_fin_locking;
+       locking->flags = LCK_CLUSTERED;
+
+       return 1;
+}
+
+int init_readonly_locking(struct locking_type *locking, struct cmd_context *cmd __attribute__((unused)))
+{
+       locking->lock_resource = _readonly_lock_resource;
+       locking->reset_locking = _no_reset_locking;
+       locking->fin_locking = _no_fin_locking;
+       locking->flags = 0;
+
+       return 1;
+}
diff --git a/lib/log/log.c b/lib/log/log.c
new file mode 100644 (file)
index 0000000..86b4988
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "memlock.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "defaults.h"
+#include "config.h"
+
+#include <stdarg.h>
+#include <syslog.h>
+
+static FILE *_log_file;
+static struct device _log_dev;
+static struct str_list _log_dev_alias;
+
+static int _syslog = 0;
+static int _log_to_file = 0;
+static int _log_direct = 0;
+static int _log_while_suspended = 0;
+static int _indent = 1;
+static int _log_suppress = 0;
+static char _msg_prefix[30] = "  ";
+static int _already_logging = 0;
+static int _abort_on_internal_errors = 0;
+
+static lvm2_log_fn_t _lvm2_log_fn = NULL;
+
+static int _lvm_errno = 0;
+static int _store_errmsg = 0;
+static char *_lvm_errmsg = NULL;
+
+void init_log_fn(lvm2_log_fn_t log_fn)
+{
+       if (log_fn)
+               _lvm2_log_fn = log_fn;
+       else
+               _lvm2_log_fn = NULL;
+}
+
+void init_log_file(const char *log_file, int append)
+{
+       const char *open_mode = append ? "a" : "w";
+
+       if (!(_log_file = fopen(log_file, open_mode))) {
+               log_sys_error("fopen", log_file);
+               return;
+       }
+
+       _log_to_file = 1;
+}
+
+void init_log_direct(const char *log_file, int append)
+{
+       int open_flags = append ? 0 : O_TRUNC;
+
+       dev_create_file(log_file, &_log_dev, &_log_dev_alias, 1);
+       if (!dev_open_flags(&_log_dev, O_RDWR | O_CREAT | open_flags, 1, 0))
+               return;
+
+       _log_direct = 1;
+}
+
+void init_log_while_suspended(int log_while_suspended)
+{
+       _log_while_suspended = log_while_suspended;
+}
+
+void init_syslog(int facility)
+{
+       openlog("lvm", LOG_PID, facility);
+       _syslog = 1;
+}
+
+int log_suppress(int suppress)
+{
+       int old_suppress = _log_suppress;
+
+       _log_suppress = suppress;
+
+       return old_suppress;
+}
+
+void release_log_memory(void)
+{
+       if (!_log_direct)
+               return;
+
+       dm_free((char *) _log_dev_alias.str);
+       _log_dev_alias.str = "activate_log file";
+}
+
+void fin_log(void)
+{
+       if (_log_direct) {
+               dev_close(&_log_dev);
+               _log_direct = 0;
+       }
+
+       if (_log_to_file) {
+               if (dm_fclose(_log_file)) {
+                       if (errno)
+                             fprintf(stderr, "failed to write log file: %s\n",
+                                     strerror(errno));
+                       else
+                             fprintf(stderr, "failed to write log file\n");
+
+               }
+               _log_to_file = 0;
+       }
+}
+
+void fin_syslog()
+{
+       if (_syslog)
+               closelog();
+       _syslog = 0;
+}
+
+void init_msg_prefix(const char *prefix)
+{
+       strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
+       _msg_prefix[sizeof(_msg_prefix) - 1] = '\0';
+}
+
+void init_indent(int indent)
+{
+       _indent = indent;
+}
+
+void init_abort_on_internal_errors(int fatal)
+{
+       _abort_on_internal_errors = fatal;
+}
+
+void reset_lvm_errno(int store_errmsg)
+{
+       _lvm_errno = 0;
+
+       if (_lvm_errmsg) {
+               dm_free(_lvm_errmsg);
+               _lvm_errmsg = NULL;
+       }
+
+       _store_errmsg = store_errmsg;
+}
+
+int stored_errno(void)
+{
+       return _lvm_errno;
+}
+
+const char *stored_errmsg(void)
+{
+       return _lvm_errmsg ? : "";
+}
+
+static struct dm_hash_table *_duplicated = NULL;
+
+void reset_log_duplicated(void) {
+       if (_duplicated)
+               dm_hash_destroy(_duplicated);
+       _duplicated = NULL;
+}
+
+void print_log(int level, const char *file, int line, int dm_errno,
+              const char *format, ...)
+{
+       va_list ap;
+       char buf[1024], buf2[4096], locn[4096];
+       int bufused, n;
+       const char *message;
+       const char *trformat;           /* Translated format string */
+       char *newbuf;
+       int use_stderr = level & _LOG_STDERR;
+       int log_once = level & _LOG_ONCE;
+       int fatal_internal_error = 0;
+
+       level &= ~(_LOG_STDERR|_LOG_ONCE);
+
+       if (_abort_on_internal_errors &&
+           !strncmp(format, INTERNAL_ERROR,
+                    strlen(INTERNAL_ERROR))) {
+               fatal_internal_error = 1;
+               /* Internal errors triggering abort cannot be suppressed. */
+               _log_suppress = 0;
+               level = _LOG_FATAL;
+       }
+
+       if (_log_suppress == 2)
+               return;
+
+       if (level <= _LOG_ERR)
+               init_error_message_produced(1);
+
+       trformat = _(format);
+
+       if (dm_errno && !_lvm_errno)
+               _lvm_errno = dm_errno;
+
+       if (_lvm2_log_fn ||
+           (_store_errmsg && (level <= _LOG_ERR)) ||
+           log_once) {
+               va_start(ap, format);
+               n = vsnprintf(buf2, sizeof(buf2) - 1, trformat, ap);
+               va_end(ap);
+
+               if (n < 0) {
+                       fprintf(stderr, _("vsnprintf failed: skipping external "
+                                       "logging function"));
+                       goto log_it;
+               }
+
+               buf2[sizeof(buf2) - 1] = '\0';
+               message = &buf2[0];
+       }
+
+       if (_store_errmsg && (level <= _LOG_ERR)) {
+               if (!_lvm_errmsg)
+                       _lvm_errmsg = dm_strdup(message);
+               else if ((newbuf = dm_realloc(_lvm_errmsg,
+                                             strlen(_lvm_errmsg) +
+                                             strlen(message) + 2))) {
+                       _lvm_errmsg = strcat(newbuf, "\n");
+                       _lvm_errmsg = strcat(newbuf, message);
+               }
+       }
+
+       if (log_once) {
+               if (!_duplicated)
+                       _duplicated = dm_hash_create(128);
+               if (_duplicated) {
+                       if (dm_hash_lookup(_duplicated, message))
+                               level = _LOG_NOTICE;
+                       dm_hash_insert(_duplicated, message, (void*)1);
+               }
+       }
+
+       if (_lvm2_log_fn) {
+               _lvm2_log_fn(level, file, line, 0, message);
+               if (fatal_internal_error)
+                       abort();
+               return;
+       }
+
+      log_it:
+       if (!_log_suppress) {
+               if (verbose_level() > _LOG_DEBUG)
+                       dm_snprintf(locn, sizeof(locn), "#%s:%d ",
+                                    file, line);
+               else
+                       locn[0] = '\0';
+
+               va_start(ap, format);
+               switch (level) {
+               case _LOG_DEBUG:
+                       if (!strcmp("<backtrace>", format) &&
+                           verbose_level() <= _LOG_DEBUG)
+                               break;
+                       if (verbose_level() >= _LOG_DEBUG) {
+                               fprintf(stderr, "%s%s%s", locn, log_command_name(),
+                                       _msg_prefix);
+                               if (_indent)
+                                       fprintf(stderr, "      ");
+                               vfprintf(stderr, trformat, ap);
+                               fputc('\n', stderr);
+                       }
+                       break;
+
+               case _LOG_INFO:
+                       if (verbose_level() >= _LOG_INFO) {
+                               fprintf(stderr, "%s%s%s", locn, log_command_name(),
+                                       _msg_prefix);
+                               if (_indent)
+                                       fprintf(stderr, "    ");
+                               vfprintf(stderr, trformat, ap);
+                               fputc('\n', stderr);
+                       }
+                       break;
+               case _LOG_NOTICE:
+                       if (verbose_level() >= _LOG_NOTICE) {
+                               fprintf(stderr, "%s%s%s", locn, log_command_name(),
+                                       _msg_prefix);
+                               if (_indent)
+                                       fprintf(stderr, "  ");
+                               vfprintf(stderr, trformat, ap);
+                               fputc('\n', stderr);
+                       }
+                       break;
+               case _LOG_WARN:
+                       if (verbose_level() >= _LOG_WARN) {
+                               fprintf(use_stderr ? stderr : stdout, "%s%s",
+                                       log_command_name(), _msg_prefix);
+                               vfprintf(use_stderr ? stderr : stdout, trformat, ap);
+                               fputc('\n', use_stderr ? stderr : stdout);
+                       }
+                       break;
+               case _LOG_ERR:
+                       if (verbose_level() >= _LOG_ERR) {
+                               fprintf(stderr, "%s%s%s", locn, log_command_name(),
+                                       _msg_prefix);
+                               vfprintf(stderr, trformat, ap);
+                               fputc('\n', stderr);
+                       }
+                       break;
+               case _LOG_FATAL:
+               default:
+                       if (verbose_level() >= _LOG_FATAL) {
+                               fprintf(stderr, "%s%s%s", locn, log_command_name(),
+                                       _msg_prefix);
+                               vfprintf(stderr, trformat, ap);
+                               fputc('\n', stderr);
+                       }
+                       break;
+               }
+               va_end(ap);
+       }
+
+       if (fatal_internal_error)
+               abort();
+
+       if (level > debug_level())
+               return;
+
+       if (_log_to_file && (_log_while_suspended || !memlock())) {
+               fprintf(_log_file, "%s:%d %s%s", file, line, log_command_name(),
+                       _msg_prefix);
+
+               va_start(ap, format);
+               vfprintf(_log_file, trformat, ap);
+               va_end(ap);
+
+               fprintf(_log_file, "\n");
+               fflush(_log_file);
+       }
+
+       if (_syslog && (_log_while_suspended || !memlock())) {
+               va_start(ap, format);
+               vsyslog(level, trformat, ap);
+               va_end(ap);
+       }
+
+       /* FIXME This code is unfinished - pre-extend & condense. */
+       if (!_already_logging && _log_direct && memlock()) {
+               _already_logging = 1;
+               memset(&buf, ' ', sizeof(buf));
+               bufused = 0;
+               if ((n = dm_snprintf(buf, sizeof(buf) - 1,
+                                     "%s:%d %s%s", file, line, log_command_name(),
+                                     _msg_prefix)) == -1)
+                       goto done;
+
+               bufused += n;
+
+               va_start(ap, format);
+               n = vsnprintf(buf + bufused - 1, sizeof(buf) - bufused - 1,
+                             trformat, ap);
+               va_end(ap);
+               bufused += n;
+
+             done:
+               buf[bufused - 1] = '\n';
+               buf[bufused] = '\n';
+               buf[sizeof(buf) - 1] = '\n';
+               /* FIXME real size bufused */
+               dev_append(&_log_dev, sizeof(buf), buf);
+               _already_logging = 0;
+       }
+}
diff --git a/lib/log/log.h b/lib/log/log.h
new file mode 100644 (file)
index 0000000..10f34be
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_LOG_H
+#define _LVM_LOG_H
+
+/*
+ * printf()-style macros to use for messages:
+ *
+ *   log_error   - always print to stderr.
+ *   log_print   - always print to stdout.  Use this instead of printf.
+ *   log_verbose - print to stdout if verbose is set (-v)
+ *   log_very_verbose - print to stdout if verbose is set twice (-vv)
+ *   log_debug   - print to stdout if verbose is set three times (-vvv)
+ *
+ * In addition, messages will be logged to file or syslog if they
+ * are more serious than the log level specified with the log/debug_level
+ * parameter in the configuration file.  These messages get the file
+ * and line number prepended.  'stack' (without arguments) can be used 
+ * to log this information at debug level.
+ *
+ * log_sys_error and log_sys_very_verbose are for errors from system calls
+ * e.g. log_sys_error("stat", filename);
+ *      /dev/fd/7: stat failed: No such file or directory
+ *
+ */
+
+#include <stdio.h>             /* FILE */
+#include <string.h>            /* strerror() */
+#include <errno.h>
+
+#define EUNCLASSIFIED -1       /* Generic error code */
+
+#define _LOG_STDERR 128 /* force things to go to stderr, even if loglevel
+                          would make them go to stdout */
+#define _LOG_ONCE 256 /* downgrade to NOTICE if this has been already logged */
+#define _LOG_DEBUG 7
+#define _LOG_INFO 6
+#define _LOG_NOTICE 5
+#define _LOG_WARN 4
+#define _LOG_ERR 3
+#define _LOG_FATAL 2
+#define INTERNAL_ERROR "Internal error: "
+
+#define log_debug(x...) LOG_LINE(_LOG_DEBUG, x)
+#define log_info(x...) LOG_LINE(_LOG_INFO, x)
+#define log_notice(x...) LOG_LINE(_LOG_NOTICE, x)
+#define log_warn(x...) LOG_LINE(_LOG_WARN | _LOG_STDERR, x)
+#define log_warn_suppress(s, x...) LOG_LINE(s ? _LOG_NOTICE : _LOG_WARN | _LOG_STDERR, x)
+#define log_err(x...) LOG_LINE_WITH_ERRNO(_LOG_ERR, EUNCLASSIFIED, x)
+#define log_err_suppress(s, x...) LOG_LINE_WITH_ERRNO(s ? _LOG_NOTICE : _LOG_ERR, EUNCLASSIFIED, x)
+#define log_err_once(x...) LOG_LINE_WITH_ERRNO(_LOG_ERR | _LOG_ONCE, EUNCLASSIFIED, x)
+#define log_fatal(x...) LOG_LINE_WITH_ERRNO(_LOG_FATAL, EUNCLASSIFIED, x)
+
+#define stack log_debug("<backtrace>") /* Backtrace on error */
+#define log_very_verbose(args...) log_info(args)
+#define log_verbose(args...) log_notice(args)
+#define log_print(args...) LOG_LINE(_LOG_WARN, args)
+#define log_error(args...) log_err(args)
+#define log_error_suppress(s, args...) log_err_suppress(s, args)
+#define log_error_once(args...) log_err_once(args)
+#define log_errno(args...) LOG_LINE_WITH_ERRNO(_LOG_ERR, args)
+
+/* System call equivalents */
+#define log_sys_error(x, y) \
+               log_err("%s: %s failed: %s", y, x, strerror(errno))
+#define log_sys_very_verbose(x, y) \
+               log_info("%s: %s failed: %s", y, x, strerror(errno))
+#define log_sys_debug(x, y) \
+               log_debug("%s: %s failed: %s", y, x, strerror(errno))
+
+#define return_0       do { stack; return 0; } while (0)
+#define return_NULL    do { stack; return NULL; } while (0)
+#define goto_out       do { stack; goto out; } while (0)
+#define goto_bad       do { stack; goto bad; } while (0)
+
+#endif
diff --git a/lib/log/lvm-logging.h b/lib/log/lvm-logging.h
new file mode 100644 (file)
index 0000000..1c0a580
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_LOGGING_H
+#define _LVM_LOGGING_H
+
+void print_log(int level, const char *file, int line, int dm_errno,
+              const char *format, ...)
+    __attribute__ ((format(printf, 5, 6)));
+
+#define LOG_LINE(l, x...) \
+    print_log(l, __FILE__, __LINE__ , 0, ## x)
+
+#define LOG_LINE_WITH_ERRNO(l, e, x...) \
+    print_log(l, __FILE__, __LINE__ , e, ## x)
+
+#include "log.h"
+
+typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
+                              int dm_errno, const char *message);
+
+void init_log_fn(lvm2_log_fn_t log_fn);
+
+void init_indent(int indent);
+void init_msg_prefix(const char *prefix);
+
+void init_log_file(const char *log_file, int append);
+void init_log_direct(const char *log_file, int append);
+void init_log_while_suspended(int log_while_suspended);
+void init_abort_on_internal_errors(int fatal);
+
+void fin_log(void);
+void release_log_memory(void);
+void reset_log_duplicated(void);
+
+void init_syslog(int facility);
+void fin_syslog(void);
+
+int error_message_produced(void);
+void reset_lvm_errno(int store_errmsg);
+int stored_errno(void);
+const char *stored_errmsg(void);
+
+/* Suppress messages to stdout/stderr (1) or everywhere (2) */
+/* Returns previous setting */
+int log_suppress(int suppress);
+
+/* Suppress messages to syslog */
+void syslog_suppress(int suppress);
+
+#endif
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
new file mode 100644 (file)
index 0000000..4e7d9dc
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "display.h"
+#include "activate.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "str_list.h"
+
+char *lvseg_tags_dup(const struct lv_segment *seg)
+{
+       return tags_format_and_copy(seg->lv->vg->vgmem, &seg->tags);
+}
+
+char *lvseg_segtype_dup(const struct lv_segment *seg)
+{
+       if (seg->area_count == 1) {
+               return (char *)"linear";
+       }
+
+       return dm_pool_strdup(seg->lv->vg->vgmem, seg->segtype->ops->name(seg));
+}
+
+uint64_t lvseg_chunksize(const struct lv_segment *seg)
+{
+       uint64_t size;
+
+       if (lv_is_cow(seg->lv))
+               size = (uint64_t) find_cow(seg->lv)->chunk_size;
+       else
+               size = UINT64_C(0);
+       return size;
+}
+
+uint64_t lvseg_start(const struct lv_segment *seg)
+{
+       return (uint64_t) seg->le * seg->lv->vg->extent_size;
+}
+
+uint64_t lvseg_size(const struct lv_segment *seg)
+{
+       return (uint64_t) seg->len * seg->lv->vg->extent_size;
+}
+
+uint32_t lv_kernel_read_ahead(const struct logical_volume *lv)
+{
+       struct lvinfo info;
+
+       if (!lv_info(lv->vg->cmd, lv, 0, &info, 0, 1) || !info.exists)
+               return UINT32_MAX;
+       return info.read_ahead;
+}
+
+char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       if (lv_is_cow(lv))
+               return lv_name_dup(mem, origin_from_cow(lv));
+       return NULL;
+}
+
+char *lv_name_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       return dm_pool_strdup(mem, lv->name);
+}
+
+char *lv_modules_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       struct dm_list *modules;
+
+       if (!(modules = str_list_create(mem))) {
+               log_error("modules str_list allocation failed");
+               return NULL;
+       }
+
+       if (!list_lv_modules(mem, lv, modules))
+               return_NULL;
+       return tags_format_and_copy(mem, modules);
+}
+
+char *lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (!seg_is_mirrored(seg) || !seg->log_lv)
+                       continue;
+               return dm_pool_strdup(mem, seg->log_lv->name);
+       }
+       return NULL;
+}
+
+int lv_kernel_minor(const struct logical_volume *lv)
+{
+       struct lvinfo info;
+
+       if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists)
+               return info.minor;
+       return -1;
+}
+
+int lv_kernel_major(const struct logical_volume *lv)
+{
+       struct lvinfo info;
+       if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists)
+               return info.major;
+       return -1;
+}
+
+char *lv_convert_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+
+       if (lv->status & (CONVERTING|MIRRORED)) {
+               seg = first_seg(lv);
+
+               /* Temporary mirror is always area_num == 0 */
+               if (seg_type(seg, 0) == AREA_LV &&
+                   is_temporary_mirror_layer(seg_lv(seg, 0)))
+                       return dm_pool_strdup(mem, seg_lv(seg, 0)->name);
+       }
+       return NULL;
+}
+
+char *lv_move_pv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (seg->status & PVMOVE)
+                       return dm_pool_strdup(mem, dev_name(seg_dev(seg, 0)));
+       }
+       return NULL;
+}
+
+uint64_t lv_origin_size(const struct logical_volume *lv)
+{
+       if (lv_is_cow(lv))
+               return (uint64_t) find_cow(lv)->len * lv->vg->extent_size;
+       if (lv_is_origin(lv))
+               return lv->size;
+       return 0;
+}
+
+char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       char *repstr;
+       size_t len;
+
+       len = strlen(lv->vg->cmd->dev_dir) + strlen(lv->vg->name) +
+               strlen(lv->name) + 2;
+
+       if (!(repstr = dm_pool_zalloc(mem, len))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, len, "%s%s/%s",
+                       lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) {
+               log_error("lvpath snprintf failed");
+               return 0;
+       }
+       return repstr;
+}
+
+char *lv_uuid_dup(const struct logical_volume *lv)
+{
+       return id_format_and_copy(lv->vg->vgmem, &lv->lvid.id[1]);
+}
+
+char *lv_tags_dup(const struct logical_volume *lv)
+{
+       return tags_format_and_copy(lv->vg->vgmem, &lv->tags);
+}
+
+uint64_t lv_size(const struct logical_volume *lv)
+{
+       return lv->size;
+}
+
+static int _lv_mimage_in_sync(const struct logical_volume *lv)
+{
+       percent_t percent;
+       struct lv_segment *mirror_seg = find_mirror_seg(first_seg(lv));
+
+       if (!(lv->status & MIRROR_IMAGE) || !mirror_seg)
+               return_0;
+
+       if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent,
+                              NULL))
+               return_0;
+
+       return (percent == PERCENT_100) ? 1 : 0;
+}
+
+char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+       percent_t snap_percent;
+       struct lvinfo info;
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(mem, 7))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       /* Blank if this is a "free space" LV. */
+       if (!*lv->name)
+               goto out;
+
+       if (lv->status & PVMOVE)
+               repstr[0] = 'p';
+       else if (lv->status & CONVERTING)
+               repstr[0] = 'c';
+       else if (lv->status & VIRTUAL)
+               repstr[0] = 'v';
+       /* Origin takes precedence over Mirror */
+       else if (lv_is_origin(lv)) {
+               repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
+       }
+       else if (lv->status & MIRRORED) {
+               repstr[0] = (lv->status & MIRROR_NOTSYNCED) ? 'M' : 'm';
+       }else if (lv->status & MIRROR_IMAGE)
+               repstr[0] = (_lv_mimage_in_sync(lv)) ? 'i' : 'I';
+       else if (lv->status & MIRROR_LOG)
+               repstr[0] = 'l';
+       else if (lv_is_cow(lv)) {
+               repstr[0] = (lv_is_merging_cow(lv)) ? 'S' : 's';
+       } else
+               repstr[0] = '-';
+
+       if (lv->status & PVMOVE)
+               repstr[1] = '-';
+       else if (lv->status & LVM_WRITE)
+               repstr[1] = 'w';
+       else if (lv->status & LVM_READ)
+               repstr[1] = 'r';
+       else
+               repstr[1] = '-';
+
+       repstr[2] = alloc_policy_char(lv->alloc);
+
+       if (lv->status & LOCKED)
+               repstr[2] = toupper(repstr[2]);
+
+       repstr[3] = (lv->status & FIXED_MINOR) ? 'm' : '-';
+
+       if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) && info.exists) {
+               if (info.suspended)
+                       repstr[4] = 's';        /* Suspended */
+               else if (info.live_table)
+                       repstr[4] = 'a';        /* Active */
+               else if (info.inactive_table)
+                       repstr[4] = 'i';        /* Inactive with table */
+               else
+                       repstr[4] = 'd';        /* Inactive without table */
+
+               /* Snapshot dropped? */
+               if (info.live_table && lv_is_cow(lv) &&
+                   (!lv_snapshot_percent(lv, &snap_percent) ||
+                    snap_percent == PERCENT_INVALID)) {
+                       repstr[0] = toupper(repstr[0]);
+                       if (info.suspended)
+                               repstr[4] = 'S'; /* Susp Inv snapshot */
+                       else
+                               repstr[4] = 'I'; /* Invalid snapshot */
+               }
+
+               repstr[5] = (info.open_count) ? 'o' : '-';
+       } else {
+               repstr[4] = '-';
+               repstr[5] = '-';
+       }
+out:
+       return repstr;
+}
diff --git a/lib/metadata/lv.h b/lib/metadata/lv.h
new file mode 100644 (file)
index 0000000..bff36d2
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_LV_H
+#define _LVM_LV_H
+
+union lvid;
+struct volume_group;
+struct dm_list;
+struct lv_segment;
+struct replicator_device;
+
+struct logical_volume {
+       union lvid lvid;
+       char *name;
+
+       struct volume_group *vg;
+
+       uint64_t status;
+       alloc_policy_t alloc;
+       uint32_t read_ahead;
+       int32_t major;
+       int32_t minor;
+
+       uint64_t size;          /* Sectors */
+       uint32_t le_count;
+
+       uint32_t origin_count;
+       struct dm_list snapshot_segs;
+       struct lv_segment *snapshot;
+
+       struct replicator_device *rdevice;/* For replicator-devs, rimages, slogs - reference to rdevice */
+       struct dm_list rsites;  /* For replicators - all sites */
+
+       struct dm_list segments;
+       struct dm_list tags;
+       struct dm_list segs_using_this_lv;
+};
+
+uint64_t lv_size(const struct logical_volume *lv);
+char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_uuid_dup(const struct logical_volume *lv);
+char *lv_tags_dup(const struct logical_volume *lv);
+char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv);
+uint64_t lv_origin_size(const struct logical_volume *lv);
+char *lv_move_pv_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_convert_lv_dup(struct dm_pool *mem, const struct logical_volume *lv);
+int lv_kernel_major(const struct logical_volume *lv);
+int lv_kernel_minor(const struct logical_volume *lv);
+char *lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_modules_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_name_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv);
+uint32_t lv_kernel_read_ahead(const struct logical_volume *lv);
+uint64_t lvseg_start(const struct lv_segment *seg);
+uint64_t lvseg_size(const struct lv_segment *seg);
+uint64_t lvseg_chunksize(const struct lv_segment *seg);
+char *lvseg_segtype_dup(const struct lv_segment *seg);
+char *lvseg_tags_dup(const struct lv_segment *seg);
+
+#endif /* _LVM_LV_H */
diff --git a/lib/metadata/lv_alloc.h b/lib/metadata/lv_alloc.h
new file mode 100644 (file)
index 0000000..785a957
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_LV_ALLOC_H
+
+struct lv_segment *alloc_lv_segment(struct dm_pool *mem,
+                                   const struct segment_type *segtype,
+                                   struct logical_volume *lv,
+                                   uint32_t le, uint32_t len,
+                                   uint64_t status,
+                                   uint32_t stripe_size,
+                                   struct logical_volume *log_lv,
+                                   uint32_t area_count,
+                                   uint32_t area_len,
+                                   uint32_t chunk_size,
+                                   uint32_t region_size,
+                                   uint32_t extents_copied,
+                                   struct lv_segment *pvmove_source_seg);
+
+struct lv_segment *alloc_snapshot_seg(struct logical_volume *lv,
+                                     uint64_t status, uint32_t old_le_count);
+
+int set_lv_segment_area_pv(struct lv_segment *seg, uint32_t area_num,
+                          struct physical_volume *pv, uint32_t pe);
+int set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
+                          struct logical_volume *lv, uint32_t le,
+                          uint64_t status);
+int move_lv_segment_area(struct lv_segment *seg_to, uint32_t area_to,
+                        struct lv_segment *seg_from, uint32_t area_from);
+void release_lv_segment_area(struct lv_segment *seg, uint32_t s,
+                            uint32_t area_reduction);
+
+struct alloc_handle;
+struct alloc_handle *allocate_extents(struct volume_group *vg,
+                                     struct logical_volume *lv,
+                                      const struct segment_type *segtype,
+                                      uint32_t stripes,
+                                      uint32_t mirrors, uint32_t log_count,
+                                     uint32_t log_region_size, uint32_t extents,
+                                      struct dm_list *allocatable_pvs,
+                                     alloc_policy_t alloc,
+                                     struct dm_list *parallel_areas);
+
+int lv_add_segment(struct alloc_handle *ah,
+                  uint32_t first_area, uint32_t num_areas,
+                  struct logical_volume *lv,
+                   const struct segment_type *segtype,
+                   uint32_t stripe_size,
+                   uint64_t status,
+                  uint32_t region_size);
+
+int lv_add_mirror_areas(struct alloc_handle *ah,
+                       struct logical_volume *lv, uint32_t le,
+                       uint32_t region_size);
+int lv_add_mirror_lvs(struct logical_volume *lv,
+                     struct logical_volume **sub_lvs,
+                     uint32_t num_extra_areas,
+                     uint64_t status, uint32_t region_size);
+
+int lv_add_log_segment(struct alloc_handle *ah, uint32_t first_area,
+                      struct logical_volume *log_lv, uint64_t status);
+int lv_add_virtual_segment(struct logical_volume *lv, uint64_t status,
+                           uint32_t extents, const struct segment_type *segtype);
+
+void alloc_destroy(struct alloc_handle *ah);
+
+struct dm_list *build_parallel_areas_from_lv(struct cmd_context *cmd,
+                                            struct logical_volume *lv,
+                                            unsigned use_pvmove_parent_lv);
+
+#endif
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
new file mode 100644 (file)
index 0000000..4a984a5
--- /dev/null
@@ -0,0 +1,3427 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "locking.h"
+#include "pv_map.h"
+#include "lvm-string.h"
+#include "toolcontext.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "display.h"
+#include "segtype.h"
+#include "archiver.h"
+#include "activate.h"
+#include "str_list.h"
+
+struct lv_names {
+       const char *old;
+       const char *new;
+};
+
+int add_seg_to_segs_using_this_lv(struct logical_volume *lv,
+                                 struct lv_segment *seg)
+{
+       struct seg_list *sl;
+
+       dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+               if (sl->seg == seg) {
+                       sl->count++;
+                       return 1;
+               }
+       }
+
+       log_very_verbose("Adding %s:%" PRIu32 " as an user of %s",
+                        seg->lv->name, seg->le, lv->name);
+
+       if (!(sl = dm_pool_zalloc(lv->vg->vgmem, sizeof(*sl)))) {
+               log_error("Failed to allocate segment list");
+               return 0;
+       }
+
+       sl->count = 1;
+       sl->seg = seg;
+       dm_list_add(&lv->segs_using_this_lv, &sl->list);
+
+       return 1;
+}
+
+int remove_seg_from_segs_using_this_lv(struct logical_volume *lv,
+                                      struct lv_segment *seg)
+{
+       struct seg_list *sl;
+
+       dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+               if (sl->seg != seg)
+                       continue;
+               if (sl->count > 1)
+                       sl->count--;
+               else {
+                       log_very_verbose("%s:%" PRIu32 " is no longer a user "
+                                        "of %s", seg->lv->name, seg->le,
+                                        lv->name);
+                       dm_list_del(&sl->list);
+               }
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * This is a function specialized for the common case where there is
+ * only one segment which uses the LV.
+ * e.g. the LV is a layer inserted by insert_layer_for_lv().
+ *
+ * In general, walk through lv->segs_using_this_lv.
+ */
+struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv)
+{
+       struct seg_list *sl;
+
+       if (dm_list_size(&lv->segs_using_this_lv) != 1) {
+               log_error("%s is expected to have only one segment using it, "
+                         "while it has %d", lv->name,
+                         dm_list_size(&lv->segs_using_this_lv));
+               return NULL;
+       }
+
+       sl = dm_list_item(dm_list_first(&lv->segs_using_this_lv), struct seg_list);
+
+       if (sl->count != 1) {
+               log_error("%s is expected to have only one segment using it, "
+                         "while %s:%" PRIu32 " uses it %d times",
+                         lv->name, sl->seg->lv->name, sl->seg->le, sl->count);
+               return NULL;
+       }
+
+       return sl->seg;
+}
+
+/*
+ * PVs used by a segment of an LV
+ */
+struct seg_pvs {
+       struct dm_list list;
+
+       struct dm_list pvs;     /* struct pv_list */
+
+       uint32_t le;
+       uint32_t len;
+};
+
+static struct seg_pvs *_find_seg_pvs_by_le(struct dm_list *list, uint32_t le)
+{
+       struct seg_pvs *spvs;
+
+       dm_list_iterate_items(spvs, list)
+               if (le >= spvs->le && le < spvs->le + spvs->len)
+                       return spvs;
+
+       return NULL;
+}
+
+/*
+ * Find first unused LV number.
+ */
+uint32_t find_free_lvnum(struct logical_volume *lv)
+{
+       int lvnum_used[MAX_RESTRICTED_LVS + 1];
+       uint32_t i = 0;
+       struct lv_list *lvl;
+       int lvnum;
+
+       memset(&lvnum_used, 0, sizeof(lvnum_used));
+
+       dm_list_iterate_items(lvl, &lv->vg->lvs) {
+               lvnum = lvnum_from_lvid(&lvl->lv->lvid);
+               if (lvnum <= MAX_RESTRICTED_LVS)
+                       lvnum_used[lvnum] = 1;
+       }
+
+       while (lvnum_used[i])
+               i++;
+
+       /* FIXME What if none are free? */
+
+       return i;
+}
+
+/*
+ * All lv_segments get created here.
+ */
+struct lv_segment *alloc_lv_segment(struct dm_pool *mem,
+                                   const struct segment_type *segtype,
+                                   struct logical_volume *lv,
+                                   uint32_t le, uint32_t len,
+                                   uint64_t status,
+                                   uint32_t stripe_size,
+                                   struct logical_volume *log_lv,
+                                   uint32_t area_count,
+                                   uint32_t area_len,
+                                   uint32_t chunk_size,
+                                   uint32_t region_size,
+                                   uint32_t extents_copied,
+                                   struct lv_segment *pvmove_source_seg)
+{
+       struct lv_segment *seg;
+       uint32_t areas_sz = area_count * sizeof(*seg->areas);
+
+       if (!(seg = dm_pool_zalloc(mem, sizeof(*seg))))
+               return_NULL;
+
+       if (!(seg->areas = dm_pool_zalloc(mem, areas_sz))) {
+               dm_pool_free(mem, seg);
+               return_NULL;
+       }
+
+       if (!segtype) {
+               log_error("alloc_lv_segment: Missing segtype.");
+               return NULL;
+       }
+
+       seg->segtype = segtype;
+       seg->lv = lv;
+       seg->le = le;
+       seg->len = len;
+       seg->status = status;
+       seg->stripe_size = stripe_size;
+       seg->area_count = area_count;
+       seg->area_len = area_len;
+       seg->chunk_size = chunk_size;
+       seg->region_size = region_size;
+       seg->extents_copied = extents_copied;
+       seg->log_lv = log_lv;
+       seg->pvmove_source_seg = pvmove_source_seg;
+       dm_list_init(&seg->tags);
+
+       if (log_lv && !attach_mirror_log(seg, log_lv))
+               return_NULL;
+
+       return seg;
+}
+
+struct lv_segment *alloc_snapshot_seg(struct logical_volume *lv,
+                                     uint64_t status, uint32_t old_le_count)
+{
+       struct lv_segment *seg;
+       const struct segment_type *segtype;
+
+       segtype = get_segtype_from_string(lv->vg->cmd, "snapshot");
+       if (!segtype) {
+               log_error("Failed to find snapshot segtype");
+               return NULL;
+       }
+
+       if (!(seg = alloc_lv_segment(lv->vg->cmd->mem, segtype, lv, old_le_count,
+                                    lv->le_count - old_le_count, status, 0,
+                                    NULL, 0, lv->le_count - old_le_count,
+                                    0, 0, 0, NULL))) {
+               log_error("Couldn't allocate new snapshot segment.");
+               return NULL;
+       }
+
+       dm_list_add(&lv->segments, &seg->list);
+       lv->status |= VIRTUAL;
+
+       return seg;
+}
+
+void release_lv_segment_area(struct lv_segment *seg, uint32_t s,
+                            uint32_t area_reduction)
+{
+       if (seg_type(seg, s) == AREA_UNASSIGNED)
+               return;
+
+       if (seg_type(seg, s) == AREA_PV) {
+               if (release_pv_segment(seg_pvseg(seg, s), area_reduction) &&
+                   seg->area_len == area_reduction)
+                       seg_type(seg, s) = AREA_UNASSIGNED;
+               return;
+       }
+
+       if (seg_lv(seg, s)->status & MIRROR_IMAGE) {
+               lv_reduce(seg_lv(seg, s), area_reduction);
+               return;
+       }
+
+       if (area_reduction == seg->area_len) {
+               log_very_verbose("Remove %s:%" PRIu32 "[%" PRIu32 "] from "
+                                "the top of LV %s:%" PRIu32,
+                                seg->lv->name, seg->le, s,
+                                seg_lv(seg, s)->name, seg_le(seg, s));
+
+               remove_seg_from_segs_using_this_lv(seg_lv(seg, s), seg);
+               seg_lv(seg, s) = NULL;
+               seg_le(seg, s) = 0;
+               seg_type(seg, s) = AREA_UNASSIGNED;
+       }
+}
+
+/*
+ * Move a segment area from one segment to another
+ */
+int move_lv_segment_area(struct lv_segment *seg_to, uint32_t area_to,
+                        struct lv_segment *seg_from, uint32_t area_from)
+{
+       struct physical_volume *pv;
+       struct logical_volume *lv;
+       uint32_t pe, le;
+
+       switch (seg_type(seg_from, area_from)) {
+       case AREA_PV:
+               pv = seg_pv(seg_from, area_from);
+               pe = seg_pe(seg_from, area_from);
+
+               release_lv_segment_area(seg_from, area_from,
+                                       seg_from->area_len);
+               release_lv_segment_area(seg_to, area_to, seg_to->area_len);
+
+               if (!set_lv_segment_area_pv(seg_to, area_to, pv, pe))
+                       return_0;
+
+               break;
+
+       case AREA_LV:
+               lv = seg_lv(seg_from, area_from);
+               le = seg_le(seg_from, area_from);
+
+               release_lv_segment_area(seg_from, area_from,
+                                       seg_from->area_len);
+               release_lv_segment_area(seg_to, area_to, seg_to->area_len);
+
+               if (!set_lv_segment_area_lv(seg_to, area_to, lv, le, 0))
+                       return_0;
+
+               break;
+
+       case AREA_UNASSIGNED:
+               release_lv_segment_area(seg_to, area_to, seg_to->area_len);
+       }
+
+       return 1;
+}
+
+/*
+ * Link part of a PV to an LV segment.
+ */
+int set_lv_segment_area_pv(struct lv_segment *seg, uint32_t area_num,
+                          struct physical_volume *pv, uint32_t pe)
+{
+       seg->areas[area_num].type = AREA_PV;
+
+       if (!(seg_pvseg(seg, area_num) =
+             assign_peg_to_lvseg(pv, pe, seg->area_len, seg, area_num)))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Link one LV segment to another.  Assumes sizes already match.
+ */
+int set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
+                          struct logical_volume *lv, uint32_t le,
+                          uint64_t status)
+{
+       log_very_verbose("Stack %s:%" PRIu32 "[%" PRIu32 "] on LV %s:%" PRIu32,
+                        seg->lv->name, seg->le, area_num, lv->name, le);
+
+       seg->areas[area_num].type = AREA_LV;
+       seg_lv(seg, area_num) = lv;
+       seg_le(seg, area_num) = le;
+       lv->status |= status;
+
+       if (!add_seg_to_segs_using_this_lv(lv, seg))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Prepare for adding parallel areas to an existing segment.
+ */
+static int _lv_segment_add_areas(struct logical_volume *lv,
+                                struct lv_segment *seg,
+                                uint32_t new_area_count)
+{
+       struct lv_segment_area *newareas;
+       uint32_t areas_sz = new_area_count * sizeof(*newareas);
+
+       if (!(newareas = dm_pool_zalloc(lv->vg->cmd->mem, areas_sz)))
+               return_0;
+
+       memcpy(newareas, seg->areas, seg->area_count * sizeof(*seg->areas));
+
+       seg->areas = newareas;
+       seg->area_count = new_area_count;
+
+       return 1;
+}
+
+/*
+ * Reduce the size of an lv_segment.  New size can be zero.
+ */
+static int _lv_segment_reduce(struct lv_segment *seg, uint32_t reduction)
+{
+       uint32_t area_reduction, s;
+
+       /* Caller must ensure exact divisibility */
+       if (seg_is_striped(seg)) {
+               if (reduction % seg->area_count) {
+                       log_error("Segment extent reduction %" PRIu32
+                                 "not divisible by #stripes %" PRIu32,
+                                 reduction, seg->area_count);
+                       return 0;
+               }
+               area_reduction = (reduction / seg->area_count);
+       } else
+               area_reduction = reduction;
+
+       for (s = 0; s < seg->area_count; s++)
+               release_lv_segment_area(seg, s, area_reduction);
+
+       seg->len -= reduction;
+       seg->area_len -= area_reduction;
+
+       return 1;
+}
+
+/*
+ * Entry point for all LV reductions in size.
+ */
+static int _lv_reduce(struct logical_volume *lv, uint32_t extents, int delete)
+{
+       struct lv_segment *seg;
+       uint32_t count = extents;
+       uint32_t reduction;
+
+       dm_list_iterate_back_items(seg, &lv->segments) {
+               if (!count)
+                       break;
+
+               if (seg->len <= count) {
+                       /* remove this segment completely */
+                       /* FIXME Check this is safe */
+                       if (seg->log_lv && !lv_remove(seg->log_lv))
+                               return_0;
+                       dm_list_del(&seg->list);
+                       reduction = seg->len;
+               } else
+                       reduction = count;
+
+               if (!_lv_segment_reduce(seg, reduction))
+                       return_0;
+               count -= reduction;
+       }
+
+       lv->le_count -= extents;
+       lv->size = (uint64_t) lv->le_count * lv->vg->extent_size;
+
+       if (!delete)
+               return 1;
+
+       /* Remove the LV if it is now empty */
+       if (!lv->le_count && !unlink_lv_from_vg(lv))
+               return_0;
+       else if (lv->vg->fid->fmt->ops->lv_setup &&
+                  !lv->vg->fid->fmt->ops->lv_setup(lv->vg->fid, lv))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Empty an LV.
+ */
+int lv_empty(struct logical_volume *lv)
+{
+       return _lv_reduce(lv, lv->le_count, 0);
+}
+
+/*
+ * Empty an LV and add error segment.
+ */
+int replace_lv_with_error_segment(struct logical_volume *lv)
+{
+       uint32_t len = lv->le_count;
+
+       if (!lv_empty(lv))
+               return_0;
+
+       /*
+        * Since we are replacing the whatever-was-there with
+        * an error segment, we should also clear any flags
+        * that suggest it is anything other than "error".
+        */
+       lv->status &= ~MIRRORED;
+
+       /* FIXME: Should we bug if we find a log_lv attached? */
+
+       if (!lv_add_virtual_segment(lv, 0, len,
+                                   get_segtype_from_string(lv->vg->cmd,
+                                                           "error")))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Remove given number of extents from LV.
+ */
+int lv_reduce(struct logical_volume *lv, uint32_t extents)
+{
+       return _lv_reduce(lv, extents, 1);
+}
+
+/*
+ * Completely remove an LV.
+ */
+int lv_remove(struct logical_volume *lv)
+{
+
+       if (!lv_reduce(lv, lv->le_count))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * A set of contiguous physical extents allocated
+ */
+struct alloced_area {
+       struct dm_list list;
+
+       struct physical_volume *pv;
+       uint32_t pe;
+       uint32_t len;
+};
+
+/*
+ * Details of an allocation attempt
+ */
+struct alloc_handle {
+       struct cmd_context *cmd;
+       struct dm_pool *mem;
+
+       alloc_policy_t alloc;           /* Overall policy */
+       uint32_t new_extents;           /* Number of new extents required */
+       uint32_t area_count;            /* Number of parallel areas */
+       uint32_t area_multiple;         /* seg->len = area_len * area_multiple */
+       uint32_t log_area_count;        /* Number of parallel logs */
+       uint32_t log_len;               /* Length of log */
+       uint32_t region_size;           /* Mirror region size */
+       uint32_t total_area_len;        /* Total number of parallel extents */
+
+       const struct config_node *cling_tag_list_cn;
+
+       struct dm_list *parallel_areas; /* PVs to avoid */
+
+       /*
+        * Contains area_count lists of areas allocated to data stripes
+        * followed by log_area_count lists of areas allocated to log stripes.
+        */
+       struct dm_list alloced_areas[0];
+};
+
+static uint32_t _calc_area_multiple(const struct segment_type *segtype,
+                                   const uint32_t area_count, const uint32_t stripes)
+{
+       if (!area_count)
+               return 1;
+
+       /* Striped */
+       if (segtype_is_striped(segtype))
+               return area_count;
+
+       /* Mirrored stripes */
+       if (stripes)
+               return stripes;
+
+       /* Mirrored */
+       return 1;
+}
+
+/*
+ * Returns log device size in extents, algorithm from kernel code
+ */
+#define BYTE_SHIFT 3
+static uint32_t mirror_log_extents(uint32_t region_size, uint32_t pe_size, uint32_t area_len)
+{
+       size_t area_size, bitset_size, log_size, region_count;
+
+       area_size = area_len * pe_size;
+       region_count = dm_div_up(area_size, region_size);
+
+       /* Work out how many "unsigned long"s we need to hold the bitset. */
+       bitset_size = dm_round_up(region_count, sizeof(uint32_t) << BYTE_SHIFT);
+       bitset_size >>= BYTE_SHIFT;
+
+       /* Log device holds both header and bitset. */
+       log_size = dm_round_up((MIRROR_LOG_OFFSET << SECTOR_SHIFT) + bitset_size, 1 << SECTOR_SHIFT);
+       log_size >>= SECTOR_SHIFT;
+
+       return dm_div_up(log_size, pe_size);
+}
+
+/*
+ * Preparation for a specific allocation attempt
+ * stripes and mirrors refer to the parallel areas used for data.
+ * If log_area_count > 1 it is always mirrored (not striped).
+ */
+static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
+                                       struct dm_pool *mem,
+                                       const struct segment_type *segtype,
+                                       alloc_policy_t alloc,
+                                       uint32_t new_extents,
+                                       uint32_t mirrors,
+                                       uint32_t stripes,
+                                       uint32_t log_area_count,
+                                       uint32_t extent_size,
+                                       uint32_t region_size,
+                                       struct dm_list *parallel_areas)
+{
+       struct alloc_handle *ah;
+       uint32_t s, area_count;
+
+       /* FIXME Caller should ensure this */
+       if (mirrors && !stripes)
+               stripes = 1;
+
+       if (segtype_is_virtual(segtype))
+               area_count = 0;
+       else if (mirrors > 1)
+               area_count = mirrors * stripes;
+       else
+               area_count = stripes;
+
+       if (!(ah = dm_pool_zalloc(mem, sizeof(*ah) + sizeof(ah->alloced_areas[0]) * (area_count + log_area_count)))) {
+               log_error("allocation handle allocation failed");
+               return NULL;
+       }
+
+       ah->cmd = cmd;
+
+       if (segtype_is_virtual(segtype))
+               return ah;
+
+       if (!(area_count + log_area_count)) {
+               log_error(INTERNAL_ERROR "_alloc_init called for non-virtual segment with no disk space.");
+               return NULL;
+       }
+
+       if (!(ah->mem = dm_pool_create("allocation", 1024))) {
+               log_error("allocation pool creation failed");
+               return NULL;
+       }
+
+       ah->new_extents = new_extents;
+       ah->area_count = area_count;
+       ah->log_area_count = log_area_count;
+       ah->region_size = region_size;
+       ah->alloc = alloc;
+       ah->area_multiple = _calc_area_multiple(segtype, area_count, stripes);
+
+       ah->log_len = log_area_count ? mirror_log_extents(ah->region_size, extent_size, ah->new_extents / ah->area_multiple) : 0;
+
+       for (s = 0; s < ah->area_count + ah->log_area_count; s++)
+               dm_list_init(&ah->alloced_areas[s]);
+
+       ah->parallel_areas = parallel_areas;
+
+       ah->cling_tag_list_cn = find_config_tree_node(cmd, "allocation/cling_tag_list");
+
+       return ah;
+}
+
+void alloc_destroy(struct alloc_handle *ah)
+{
+       if (ah->mem)
+               dm_pool_destroy(ah->mem);
+}
+
+static int _log_parallel_areas(struct dm_pool *mem, struct dm_list *parallel_areas)
+{
+       struct seg_pvs *spvs;
+       struct pv_list *pvl;
+       char *pvnames;
+
+       if (!parallel_areas)
+               return 1;
+
+       dm_list_iterate_items(spvs, parallel_areas) {
+               if (!dm_pool_begin_object(mem, 256)) {
+                       log_error("dm_pool_begin_object failed");
+                       return 0;
+               }
+
+               dm_list_iterate_items(pvl, &spvs->pvs) {
+                       if (!dm_pool_grow_object(mem, pv_dev_name(pvl->pv), strlen(pv_dev_name(pvl->pv)))) {
+                               log_error("dm_pool_grow_object failed");
+                               dm_pool_abandon_object(mem);
+                               return 0;
+                       }
+                       if (!dm_pool_grow_object(mem, " ", 1)) {
+                               log_error("dm_pool_grow_object failed");
+                               dm_pool_abandon_object(mem);
+                               return 0;
+                       }
+               }
+
+               if (!dm_pool_grow_object(mem, "\0", 1)) {
+                       log_error("dm_pool_grow_object failed");
+                       dm_pool_abandon_object(mem);
+                       return 0;
+               }
+
+               pvnames = dm_pool_end_object(mem);
+               log_debug("Parallel PVs at LE %" PRIu32 " length %" PRIu32 ": %s",
+                         spvs->le, spvs->len, pvnames);
+               dm_pool_free(mem, pvnames);
+       }
+
+       return 1;
+}
+
+static int _setup_alloced_segment(struct logical_volume *lv, uint64_t status,
+                                 uint32_t area_count,
+                                 uint32_t stripe_size,
+                                 const struct segment_type *segtype,
+                                 struct alloced_area *aa,
+                                 uint32_t region_size)
+{
+       uint32_t s, extents, area_multiple;
+       struct lv_segment *seg;
+
+       area_multiple = _calc_area_multiple(segtype, area_count, 0);
+
+       if (!(seg = alloc_lv_segment(lv->vg->cmd->mem, segtype, lv,
+                                    lv->le_count,
+                                    aa[0].len * area_multiple,
+                                    status, stripe_size, NULL,
+                                    area_count,
+                                    aa[0].len, 0u, region_size, 0u, NULL))) {
+               log_error("Couldn't allocate new LV segment.");
+               return 0;
+       }
+
+       for (s = 0; s < area_count; s++)
+               if (!set_lv_segment_area_pv(seg, s, aa[s].pv, aa[s].pe))
+                       return_0;
+
+       dm_list_add(&lv->segments, &seg->list);
+
+       extents = aa[0].len * area_multiple;
+       lv->le_count += extents;
+       lv->size += (uint64_t) extents *lv->vg->extent_size;
+
+       if (segtype_is_mirrored(segtype))
+               lv->status |= MIRRORED;
+
+       return 1;
+}
+
+static int _setup_alloced_segments(struct logical_volume *lv,
+                                  struct dm_list *alloced_areas,
+                                  uint32_t area_count,
+                                  uint64_t status,
+                                  uint32_t stripe_size,
+                                  const struct segment_type *segtype,
+                                  uint32_t region_size)
+{
+       struct alloced_area *aa;
+
+       dm_list_iterate_items(aa, &alloced_areas[0]) {
+               if (!_setup_alloced_segment(lv, status, area_count,
+                                           stripe_size, segtype, aa,
+                                           region_size))
+                       return_0;
+       }
+
+       return 1;
+}
+
+/*
+ * This function takes a list of pv_areas and adds them to allocated_areas.
+ * If the complete area is not needed then it gets split.
+ * The part used is removed from the pv_map so it can't be allocated twice.
+ */
+static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t needed,
+                               struct pv_area_used *areas, uint32_t *allocated,
+                               unsigned log_needs_allocating, uint32_t ix_log_offset)
+{
+       uint32_t area_len, len, remaining;
+       uint32_t s;
+       uint32_t ix_log_skip = 0; /* How many areas to skip in middle of array to reach log areas */
+       uint32_t total_area_count = ah->area_count + (log_needs_allocating ? ah->log_area_count : 0);
+       struct alloced_area *aa;
+
+       if (!total_area_count) {
+               log_error(INTERNAL_ERROR "_alloc_parallel_area called without any allocation to do.");
+               return 1;
+       }
+
+       remaining = needed - *allocated;
+       area_len = remaining / ah->area_multiple;
+
+       /* Reduce area_len to the smallest of the areas */
+       for (s = 0; s < ah->area_count; s++)
+               if (area_len > areas[s].used)
+                       area_len = areas[s].used;
+
+       if (!(aa = dm_pool_alloc(ah->mem, sizeof(*aa) * total_area_count))) {
+               log_error("alloced_area allocation failed");
+               return 0;
+       }
+
+       /*
+        * Areas consists of area_count areas for data stripes, then
+        * ix_log_skip areas to skip, then log_area_count areas to use for the
+        * log, then some areas too small for the log.
+        */
+       len = area_len;
+       for (s = 0; s < total_area_count; s++) {
+               if (s == ah->area_count) {
+                       ix_log_skip = ix_log_offset - ah->area_count;
+                       len = ah->log_len;
+               }
+
+               aa[s].pv = areas[s + ix_log_skip].pva->map->pv;
+               aa[s].pe = areas[s + ix_log_skip].pva->start;
+               aa[s].len = len;
+
+               log_debug("Allocating parallel area %" PRIu32
+                         " on %s start PE %" PRIu32 " length %" PRIu32 ".",
+                         s, dev_name(aa[s].pv->dev), aa[s].pe, len);
+
+               consume_pv_area(areas[s + ix_log_skip].pva, len);
+
+               dm_list_add(&ah->alloced_areas[s], &aa[s].list);
+       }
+
+       ah->total_area_len += area_len;
+
+       *allocated += area_len * ah->area_multiple;
+
+       return 1;
+}
+
+/* For striped mirrors, all the areas are counted, through the mirror layer */
+static uint32_t _stripes_per_mimage(struct lv_segment *seg)
+{
+       struct lv_segment *last_lvseg;
+
+       if (seg_is_mirrored(seg) && seg->area_count && seg_type(seg, 0) == AREA_LV) {
+               last_lvseg = dm_list_item(dm_list_last(&seg_lv(seg, 0)->segments), struct lv_segment);
+               if (seg_is_striped(last_lvseg))
+                       return last_lvseg->area_count;
+       }
+
+       return 1;
+}
+
+/*
+ * Call fn for each AREA_PV used by the LV segment at lv:le of length *max_seg_len.
+ * If any constituent area contains more than one segment, max_seg_len is
+ * reduced to cover only the first.
+ * fn should return 0 on error, 1 to continue scanning or >1 to terminate without error.
+ * In the last case, this function passes on the return code.
+ */
+static int _for_each_pv(struct cmd_context *cmd, struct logical_volume *lv,
+                       uint32_t le, uint32_t len, struct lv_segment *seg,
+                       uint32_t *max_seg_len,
+                       uint32_t first_area, uint32_t max_areas,
+                       int top_level_area_index,
+                       int only_single_area_segments,
+                       int (*fn)(struct cmd_context *cmd,
+                                 struct pv_segment *peg, uint32_t s,
+                                 void *data),
+                       void *data)
+{
+       uint32_t s;
+       uint32_t remaining_seg_len, area_len, area_multiple;
+       uint32_t stripes_per_mimage = 1;
+       int r = 1;
+
+       if (!seg && !(seg = find_seg_by_le(lv, le))) {
+               log_error("Failed to find segment for %s extent %" PRIu32,
+                         lv->name, le);
+               return 0;
+       }
+
+       /* Remaining logical length of segment */
+       remaining_seg_len = seg->len - (le - seg->le);
+
+       if (remaining_seg_len > len)
+               remaining_seg_len = len;
+
+       if (max_seg_len && *max_seg_len > remaining_seg_len)
+               *max_seg_len = remaining_seg_len;
+
+       area_multiple = _calc_area_multiple(seg->segtype, seg->area_count, 0);
+       area_len = remaining_seg_len / area_multiple ? : 1;
+
+       /* For striped mirrors, all the areas are counted, through the mirror layer */
+       if (top_level_area_index == -1)
+               stripes_per_mimage = _stripes_per_mimage(seg);
+
+       for (s = first_area;
+            s < seg->area_count && (!max_areas || s <= max_areas);
+            s++) {
+               if (seg_type(seg, s) == AREA_LV) {
+                       if (!(r = _for_each_pv(cmd, seg_lv(seg, s),
+                                              seg_le(seg, s) +
+                                              (le - seg->le) / area_multiple,
+                                              area_len, NULL, max_seg_len, 0,
+                                              (stripes_per_mimage == 1) && only_single_area_segments ? 1U : 0U,
+                                              top_level_area_index != -1 ? top_level_area_index : (int) s * stripes_per_mimage,
+                                              only_single_area_segments, fn,
+                                              data)))
+                               stack;
+               } else if (seg_type(seg, s) == AREA_PV)
+                       if (!(r = fn(cmd, seg_pvseg(seg, s), top_level_area_index != -1 ? (uint32_t) top_level_area_index + s : s, data)))
+                               stack;
+               if (r != 1)
+                       return r;
+       }
+
+       /* FIXME only_single_area_segments used as workaround to skip log LV - needs new param? */
+       if (!only_single_area_segments && seg_is_mirrored(seg) && seg->log_lv) {
+               if (!(r = _for_each_pv(cmd, seg->log_lv, 0, seg->log_lv->le_count, NULL,
+                                      NULL, 0, 0, 0, only_single_area_segments,
+                                      fn, data)))
+                       stack;
+               if (r != 1)
+                       return r;
+       }
+
+       /* FIXME Add snapshot cow LVs etc. */
+
+       return 1;
+}
+
+static int _comp_area(const void *l, const void *r)
+{
+       const struct pv_area_used *lhs = (const struct pv_area_used *) l;
+       const struct pv_area_used *rhs = (const struct pv_area_used *) r;
+
+       if (lhs->used < rhs->used)
+               return 1;
+
+       else if (lhs->used > rhs->used)
+               return -1;
+
+       return 0;
+}
+
+/*
+ * Search for pvseg that matches condition
+ */
+struct pv_match {
+       int (*condition)(struct pv_match *pvmatch, struct pv_segment *pvseg, struct pv_area *pva);
+
+       struct pv_area_used *areas;
+       struct pv_area *pva;
+       uint32_t areas_size;
+       const struct config_node *cling_tag_list_cn;
+       int s;  /* Area index of match */
+};
+
+/*
+ * Is PV area on the same PV?
+ */
+static int _is_same_pv(struct pv_match *pvmatch __attribute((unused)), struct pv_segment *pvseg, struct pv_area *pva)
+{
+       if (pvseg->pv != pva->map->pv)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Does PV area have a tag listed in allocation/cling_tag_list that 
+ * matches a tag of the PV of the existing segment?
+ */
+static int _has_matching_pv_tag(struct pv_match *pvmatch, struct pv_segment *pvseg, struct pv_area *pva)
+{
+       const struct config_value *cv;
+       const char *str;
+       const char *tag_matched;
+
+       for (cv = pvmatch->cling_tag_list_cn->v; cv; cv = cv->next) {
+               if (cv->type != CFG_STRING) {
+                       log_error("Ignoring invalid string in config file entry "
+                                 "allocation/cling_tag_list");
+                       continue;
+               }
+               str = cv->v.str;
+               if (!*str) {
+                       log_error("Ignoring empty string in config file entry "
+                                 "allocation/cling_tag_list");
+                       continue;
+               }
+
+               if (*str != '@') {
+                       log_error("Ignoring string not starting with @ in config file entry "
+                                 "allocation/cling_tag_list: %s", str);
+                       continue;
+               }
+
+               str++;
+
+               if (!*str) {
+                       log_error("Ignoring empty tag in config file entry "
+                                 "allocation/cling_tag_list");
+                       continue;
+               }
+
+               /* Wildcard matches any tag against any tag. */
+               if (!strcmp(str, "*")) {
+                       if (!str_list_match_list(&pvseg->pv->tags, &pva->map->pv->tags, &tag_matched))
+                               continue;
+                       else {
+                               log_debug("Matched allocation PV tag %s on existing %s with free space on %s.",
+                                         tag_matched, pv_dev_name(pvseg->pv), pv_dev_name(pva->map->pv));
+                               return 1;
+                       }
+               }
+
+               if (!str_list_match_item(&pvseg->pv->tags, str) ||
+                   !str_list_match_item(&pva->map->pv->tags, str))
+                       continue;
+               else {
+                       log_debug("Matched allocation PV tag %s on existing %s with free space on %s.",
+                                 str, pv_dev_name(pvseg->pv), pv_dev_name(pva->map->pv));
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Is PV area contiguous to PV segment?
+ */
+static int _is_contiguous(struct pv_match *pvmatch __attribute((unused)), struct pv_segment *pvseg, struct pv_area *pva)
+{
+       if (pvseg->pv != pva->map->pv)
+               return 0;
+
+       if (pvseg->pe + pvseg->len != pva->start)
+               return 0;
+
+       return 1;
+}
+
+static int _is_condition(struct cmd_context *cmd __attribute__((unused)),
+                        struct pv_segment *pvseg, uint32_t s,
+                        void *data)
+{
+       struct pv_match *pvmatch = data;
+
+       if (!pvmatch->condition(pvmatch, pvseg, pvmatch->pva))
+               return 1;       /* Continue */
+
+       if (s >= pvmatch->areas_size)
+               return 1;
+
+       /*
+        * Only used for cling and contiguous policies so it's safe to say all
+        * the available space is used.
+        */
+       pvmatch->areas[s].pva = pvmatch->pva;
+       pvmatch->areas[s].used = pvmatch->pva->count;
+
+       log_debug("Trying allocation area %" PRIu32 " on %s start PE %" PRIu32
+                 " length %" PRIu32 ".",
+                 s, dev_name(pvmatch->pva->map->pv->dev), pvmatch->pva->start, 
+                 pvmatch->pva->count);
+
+       return 2;       /* Finished */
+}
+
+/*
+ * Is pva on same PV as any existing areas?
+ */
+static int _check_cling(struct cmd_context *cmd,
+                       const struct config_node *cling_tag_list_cn,
+                       struct lv_segment *prev_lvseg, struct pv_area *pva,
+                       struct pv_area_used *areas, uint32_t areas_size)
+{
+       struct pv_match pvmatch;
+       int r;
+
+       pvmatch.condition = cling_tag_list_cn ? _has_matching_pv_tag : _is_same_pv;
+       pvmatch.areas = areas;
+       pvmatch.areas_size = areas_size;
+       pvmatch.pva = pva;
+       pvmatch.cling_tag_list_cn = cling_tag_list_cn;
+
+       /* FIXME Cope with stacks by flattening */
+       if (!(r = _for_each_pv(cmd, prev_lvseg->lv,
+                              prev_lvseg->le + prev_lvseg->len - 1, 1, NULL, NULL,
+                              0, 0, -1, 1,
+                              _is_condition, &pvmatch)))
+               stack;
+
+       if (r != 2)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Is pva contiguous to any existing areas or on the same PV?
+ */
+static int _check_contiguous(struct cmd_context *cmd,
+                            struct lv_segment *prev_lvseg, struct pv_area *pva,
+                            struct pv_area_used *areas, uint32_t areas_size)
+{
+       struct pv_match pvmatch;
+       int r;
+
+       pvmatch.condition = _is_contiguous;
+       pvmatch.areas = areas;
+       pvmatch.areas_size = areas_size;
+       pvmatch.pva = pva;
+       pvmatch.cling_tag_list_cn = NULL;
+
+       /* FIXME Cope with stacks by flattening */
+       if (!(r = _for_each_pv(cmd, prev_lvseg->lv,
+                              prev_lvseg->le + prev_lvseg->len - 1, 1, NULL, NULL,
+                              0, 0, -1, 1,
+                              _is_condition, &pvmatch)))
+               stack;
+
+       if (r != 2)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Choose sets of parallel areas to use, respecting any constraints.
+ */
+static int _find_parallel_space(struct alloc_handle *ah, alloc_policy_t alloc,
+                               struct dm_list *pvms, struct pv_area_used **areas_ptr,
+                               uint32_t *areas_size_ptr, unsigned can_split,
+                               struct lv_segment *prev_lvseg,
+                               uint32_t *allocated, uint32_t *log_needs_allocating, uint32_t needed)
+{
+       struct pv_map *pvm;
+       struct pv_area *pva;
+       struct pv_list *pvl;
+       unsigned already_found_one = 0;
+       unsigned contiguous = 0, cling = 0, use_cling_tags = 0, preferred_count = 0;
+       unsigned ix, last_ix;
+       unsigned ix_offset = 0; /* Offset for non-preferred allocations */
+       unsigned ix_log_offset; /* Offset to start of areas to use for log */
+       unsigned too_small_for_log_count; /* How many too small for log? */
+       uint32_t max_parallel;  /* Maximum extents to allocate */
+       uint32_t next_le;
+       uint32_t required;      /* Extents we're trying to obtain from a given area */
+       struct seg_pvs *spvs;
+       struct dm_list *parallel_pvs;
+       uint32_t free_pes;
+       struct alloced_area *aa;
+       uint32_t s;
+       uint32_t total_extents_needed = (needed - *allocated) * ah->area_count / ah->area_multiple;
+
+       /* Is there enough total space? */
+       free_pes = pv_maps_size(pvms);
+       if (total_extents_needed > free_pes) {
+               log_error("Insufficient free space: %" PRIu32 " extents needed,"
+                         " but only %" PRIu32 " available",
+                         total_extents_needed, free_pes);
+               return 0;
+       }
+
+       /* FIXME Select log PV appropriately if there isn't one yet */
+
+       /* Are there any preceding segments we must follow on from? */
+       if (prev_lvseg) {
+               ix_offset = _stripes_per_mimage(prev_lvseg) * prev_lvseg->area_count;
+               if ((alloc == ALLOC_CONTIGUOUS))
+                       contiguous = 1;
+               else if ((alloc == ALLOC_CLING))
+                       cling = 1;
+               else if ((alloc == ALLOC_CLING_BY_TAGS)) {
+                       cling = 1;
+                       use_cling_tags = 1;
+               } else
+                       ix_offset = 0;
+       }
+
+       /* FIXME This algorithm needs a lot of cleaning up! */
+       /* FIXME anywhere doesn't find all space yet */
+       /* ix_offset holds the number of allocations that must be contiguous */
+       /* ix holds the number of areas found on other PVs */
+       do {
+               ix = 0;
+               preferred_count = 0;
+
+               parallel_pvs = NULL;
+               max_parallel = needed;
+
+               /*
+                * If there are existing parallel PVs, avoid them and reduce
+                * the maximum we can allocate in one go accordingly.
+                */
+               if (ah->parallel_areas) {
+                       next_le = (prev_lvseg ? prev_lvseg->le + prev_lvseg->len : 0) + *allocated / ah->area_multiple;
+                       dm_list_iterate_items(spvs, ah->parallel_areas) {
+                               if (next_le >= spvs->le + spvs->len)
+                                       continue;
+
+                               if (max_parallel > (spvs->le + spvs->len) * ah->area_multiple)
+                                       max_parallel = (spvs->le + spvs->len) * ah->area_multiple;
+                               parallel_pvs = &spvs->pvs;
+                               break;
+                       }
+               }
+
+               do {
+                       /*
+                        * Provide for escape from the loop if no progress is made.
+                        * This should not happen: ALLOC_ANYWHERE should be able to use
+                        * all available space. (If there aren't enough extents, the code
+                        * should not reach this point.)
+                        */
+                       last_ix = ix;
+
+                       /*
+                        * Put the smallest area of each PV that is at least the
+                        * size we need into areas array.  If there isn't one
+                        * that fits completely and we're allowed more than one
+                        * LV segment, then take the largest remaining instead.
+                        */
+                       dm_list_iterate_items(pvm, pvms) {
+                               if (dm_list_empty(&pvm->areas))
+                                       continue;       /* Next PV */
+
+                               if (alloc != ALLOC_ANYWHERE) {
+                                       /* Don't allocate onto the log pv */
+                                       if (ah->log_area_count)
+                                               dm_list_iterate_items(aa, &ah->alloced_areas[ah->area_count])
+                                                       for (s = 0; s < ah->log_area_count; s++)
+                                                               if (!aa[s].pv)
+                                                                       goto next_pv;
+
+                                       /* Avoid PVs used by existing parallel areas */
+                                       if (parallel_pvs)
+                                               dm_list_iterate_items(pvl, parallel_pvs)
+                                                       if (pvm->pv == pvl->pv)
+                                                               goto next_pv;
+                               }
+
+                               already_found_one = 0;
+                               /* First area in each list is the largest */
+                               dm_list_iterate_items(pva, &pvm->areas) {
+                                       /* Skip fully-reserved areas (which are not currently removed from the list). */
+                                       if (!pva->unreserved)
+                                               continue;
+                                       if (contiguous) {
+                                               if (prev_lvseg &&
+                                                   _check_contiguous(ah->cmd,
+                                                                     prev_lvseg,
+                                                                     pva, *areas_ptr,
+                                                                     *areas_size_ptr)) {
+                                                       preferred_count++;
+                                                       goto next_pv;
+                                               }
+                                               continue;
+                                       }
+
+                                       if (cling) {
+                                               if (prev_lvseg &&
+                                                   _check_cling(ah->cmd,
+                                                                use_cling_tags ? ah->cling_tag_list_cn : NULL,
+                                                                prev_lvseg,
+                                                                pva, *areas_ptr,
+                                                                *areas_size_ptr)) {
+                                                       preferred_count++;
+                                               }
+                                               goto next_pv;
+                                       }
+
+                                       /* Is it big enough on its own? */
+                                       if (pva->unreserved * ah->area_multiple <
+                                           max_parallel - *allocated &&
+                                           ((!can_split && !ah->log_area_count) ||
+                                            (already_found_one &&
+                                             !(alloc == ALLOC_ANYWHERE))))
+                                               goto next_pv;
+
+                                       /*
+                                        * Except with ALLOC_ANYWHERE, replace first area with this
+                                        * one which is smaller but still big enough.
+                                        */
+                                       if (!already_found_one ||
+                                           alloc == ALLOC_ANYWHERE) {
+                                               ix++;
+                                               already_found_one = 1;
+                                       }
+
+                                       required = (max_parallel - *allocated) / ah->area_multiple;
+
+                                       if (alloc == ALLOC_ANYWHERE) {
+                                               /*
+                                                * Update amount unreserved - effectively splitting an area 
+                                                * into two or more parts.  If the whole stripe doesn't fit,
+                                                * reduce amount we're looking for.
+                                                */
+                                               if (ix + ix_offset - 1 >= ah->area_count)
+                                                       required = ah->log_len;
+                                               if (required >= pva->unreserved) {
+                                                       required = pva->unreserved;
+                                                       pva->unreserved = 0;
+                                               } else {
+                                                       pva->unreserved -= required;
+                                                       reinsert_reduced_pv_area(pva);
+                                               }
+                                       } else {
+                                               if (required < ah->log_len)
+                                                       required = ah->log_len;
+                                               if (required > pva->count)
+                                                       required = pva->count;
+                                       }
+
+                                       /* Expand areas array if needed after an area was split. */
+                                       if (ix + ix_offset > *areas_size_ptr) {
+                                               *areas_size_ptr *= 2;
+                                               if (!(*areas_ptr = dm_realloc(*areas_ptr,
+                                                                            sizeof(**areas_ptr) *
+                                                                            (*areas_size_ptr)))) {
+                                                       log_error("Memory reallocation for parallel areas failed.");
+                                                       return 0;
+                                               }
+                                       }
+                                       (*areas_ptr)[ix + ix_offset - 1].pva = pva;
+                                               (*areas_ptr)[ix + ix_offset - 1].used = required;
+                                       log_debug("Trying allocation area %" PRIu32 " on %s start PE %" PRIu32
+                                                 " length %" PRIu32 " leaving %" PRIu32 ".",
+                                                 ix + ix_offset - 1, dev_name(pva->map->pv->dev), pva->start, required,
+                                                 (alloc == ALLOC_ANYWHERE) ? pva->unreserved : pva->count - required);
+                               }
+                       next_pv:
+                               /* With ALLOC_ANYWHERE we ignore further PVs once we have at least enough areas */
+                               /* With cling and contiguous we stop if we found a match for *all* the areas */
+                               /* FIXME Rename these variables! */
+                               if ((alloc == ALLOC_ANYWHERE &&
+                                   ix + ix_offset >= ah->area_count + (*log_needs_allocating ? ah->log_area_count : 0)) ||
+                                   (preferred_count == ix_offset &&
+                                    (ix_offset == ah->area_count + (*log_needs_allocating ? ah->log_area_count : 0))))
+                                       break;
+                       }
+               } while (alloc == ALLOC_ANYWHERE && last_ix != ix && ix < ah->area_count + (*log_needs_allocating ? ah->log_area_count : 0));
+
+               if (preferred_count < ix_offset)
+                       break;
+
+               if (ix + ix_offset < ah->area_count +
+                  (*log_needs_allocating ? ah->log_area_count : 0))
+                       break;
+
+               /* Sort the areas so we allocate from the biggest */
+               if (ix > 1)
+                       qsort((*areas_ptr) + ix_offset, ix, sizeof(**areas_ptr),
+                             _comp_area);
+
+               /*
+                * First time around, if there's a log, allocate it on the
+                * smallest device that has space for it.
+                */
+               too_small_for_log_count = 0;
+               ix_log_offset = 0;
+
+               /* FIXME This logic is due to its heritage and can be simplified! */
+               if (*log_needs_allocating) {
+                       /* How many areas are too small for the log? */
+                       while (too_small_for_log_count < ix_offset + ix &&
+                              (*((*areas_ptr) + ix_offset + ix - 1 -
+                                 too_small_for_log_count)).used < ah->log_len)
+                               too_small_for_log_count++;
+                       ix_log_offset = ix_offset + ix - too_small_for_log_count - ah->log_area_count;
+               }
+
+               if (ix + ix_offset < ah->area_count +
+                   (*log_needs_allocating ? ah->log_area_count +
+                                           too_small_for_log_count : 0))
+                       break;
+
+               if (!_alloc_parallel_area(ah, max_parallel, *areas_ptr, allocated,
+                                         *log_needs_allocating, ix_log_offset))
+                       return_0;
+
+               *log_needs_allocating = 0;
+
+       } while ((alloc != ALLOC_CONTIGUOUS) && *allocated != needed && can_split);
+
+       return 1;
+}
+
+/*
+ * Allocate several segments, each the same size, in parallel.
+ * If mirrored_pv and mirrored_pe are supplied, it is used as
+ * the first area, and additional areas are allocated parallel to it.
+ */
+static int _allocate(struct alloc_handle *ah,
+                    struct volume_group *vg,
+                    struct logical_volume *lv,
+                    unsigned can_split,
+                    struct dm_list *allocatable_pvs)
+{
+       struct pv_area_used *areas;
+       uint32_t allocated = lv ? lv->le_count : 0;
+       uint32_t old_allocated;
+       struct lv_segment *prev_lvseg = NULL;
+       int r = 0;
+       struct dm_list *pvms;
+       uint32_t areas_size;
+       alloc_policy_t alloc;
+       unsigned log_needs_allocating = 0;
+
+       if (allocated >= ah->new_extents && !ah->log_area_count) {
+               log_error("_allocate called with no work to do!");
+               return 1;
+       }
+
+       if (ah->log_area_count)
+               log_needs_allocating = 1;
+
+       if (ah->alloc == ALLOC_CONTIGUOUS)
+               can_split = 0;
+
+       if (lv && !dm_list_empty(&lv->segments))
+               prev_lvseg = dm_list_item(dm_list_last(&lv->segments),
+                                      struct lv_segment);
+       /*
+        * Build the sets of available areas on the pv's.
+        */
+       if (!(pvms = create_pv_maps(ah->mem, vg, allocatable_pvs)))
+               return_0;
+
+       if (!_log_parallel_areas(ah->mem, ah->parallel_areas))
+               stack;
+
+       areas_size = dm_list_size(pvms);
+       if (areas_size && areas_size < (ah->area_count + ah->log_area_count)) {
+               if (ah->alloc != ALLOC_ANYWHERE) {
+                       log_error("Not enough PVs with free space available "
+                                 "for parallel allocation.");
+                       log_error("Consider --alloc anywhere if desperate.");
+                       return 0;
+               }
+               areas_size = ah->area_count + ah->log_area_count;
+       }
+
+       /* Upper bound if none of the PVs in prev_lvseg is in pvms */
+       /* FIXME Work size out properly */
+       if (prev_lvseg)
+               areas_size += _stripes_per_mimage(prev_lvseg) * prev_lvseg->area_count;
+
+       /* Allocate an array of pv_areas to hold the largest space on each PV */
+       if (!(areas = dm_malloc(sizeof(*areas) * areas_size))) {
+               log_error("Couldn't allocate areas array.");
+               return 0;
+       }
+
+       /*
+        * cling includes implicit cling_by_tags
+        * but it does nothing unless the lvm.conf setting is present.
+        */
+       if (ah->alloc == ALLOC_CLING)
+               ah->alloc = ALLOC_CLING_BY_TAGS;
+
+       /* Attempt each defined allocation policy in turn */
+       for (alloc = ALLOC_CONTIGUOUS; alloc < ALLOC_INHERIT; alloc++) {
+               /* Skip cling_by_tags if no list defined */
+               if (alloc == ALLOC_CLING_BY_TAGS && !ah->cling_tag_list_cn)
+                       continue;
+               old_allocated = allocated;
+               log_debug("Trying allocation using %s policy.  "
+                         "Need %" PRIu32 " extents for %" PRIu32 " parallel areas and %" PRIu32 " log areas of %" PRIu32 " extents. "
+                         "(Total %" PRIu32 " extents.)",
+                         get_alloc_string(alloc),
+                         (ah->new_extents - allocated) / ah->area_multiple,
+                         ah->area_count, log_needs_allocating ? ah->log_area_count : 0,
+                         log_needs_allocating ? ah->log_len : 0,
+                         (ah->new_extents - allocated) * ah->area_count / ah->area_multiple +
+                               (log_needs_allocating ? ah->log_area_count * ah->log_len : 0));
+               if (!_find_parallel_space(ah, alloc, pvms, &areas,
+                                         &areas_size, can_split,
+                                         prev_lvseg, &allocated, &log_needs_allocating, ah->new_extents))
+                       goto_out;
+               if ((allocated == ah->new_extents && !log_needs_allocating) || (ah->alloc == alloc) ||
+                   (!can_split && (allocated != old_allocated)))
+                       break;
+       }
+
+       if (allocated != ah->new_extents) {
+               log_error("Insufficient suitable %sallocatable extents "
+                         "for logical volume %s: %u more required",
+                         can_split ? "" : "contiguous ",
+                         lv ? lv->name : "",
+                         (ah->new_extents - allocated) * ah->area_count
+                         / ah->area_multiple);
+               goto out;
+       }
+
+       if (log_needs_allocating) {
+               log_error("Insufficient extents for log allocation "
+                         "for logical volume %s.",
+                         lv ? lv->name : "");
+               goto out;
+       }
+
+       r = 1;
+
+      out:
+       dm_free(areas);
+       return r;
+}
+
+int lv_add_virtual_segment(struct logical_volume *lv, uint64_t status,
+                          uint32_t extents, const struct segment_type *segtype)
+{
+       struct lv_segment *seg;
+
+       if (!(seg = alloc_lv_segment(lv->vg->cmd->mem, segtype, lv,
+                                    lv->le_count, extents, status, 0,
+                                    NULL, 0, extents, 0, 0, 0, NULL))) {
+               log_error("Couldn't allocate new zero segment.");
+               return 0;
+       }
+
+       dm_list_add(&lv->segments, &seg->list);
+
+       lv->le_count += extents;
+       lv->size += (uint64_t) extents *lv->vg->extent_size;
+
+       lv->status |= VIRTUAL;
+
+       return 1;
+}
+
+/*
+ * Entry point for all extent allocations.
+ */
+struct alloc_handle *allocate_extents(struct volume_group *vg,
+                                     struct logical_volume *lv,
+                                     const struct segment_type *segtype,
+                                     uint32_t stripes,
+                                     uint32_t mirrors, uint32_t log_count,
+                                     uint32_t region_size, uint32_t extents,
+                                     struct dm_list *allocatable_pvs,
+                                     alloc_policy_t alloc,
+                                     struct dm_list *parallel_areas)
+{
+       struct alloc_handle *ah;
+       uint32_t new_extents;
+
+       if (segtype_is_virtual(segtype)) {
+               log_error("allocate_extents does not handle virtual segments");
+               return NULL;
+       }
+
+       if (vg->fid->fmt->ops->segtype_supported &&
+           !vg->fid->fmt->ops->segtype_supported(vg->fid, segtype)) {
+               log_error("Metadata format (%s) does not support required "
+                         "LV segment type (%s).", vg->fid->fmt->name,
+                         segtype->name);
+               log_error("Consider changing the metadata format by running "
+                         "vgconvert.");
+               return NULL;
+       }
+
+       if (alloc == ALLOC_INHERIT)
+               alloc = vg->alloc;
+
+       new_extents = (lv ? lv->le_count : 0) + extents;
+       if (!(ah = _alloc_init(vg->cmd, vg->cmd->mem, segtype, alloc,
+                              new_extents, mirrors, stripes, log_count,
+                              vg->extent_size, region_size,
+                              parallel_areas)))
+               return_NULL;
+
+       if (!segtype_is_virtual(segtype) &&
+           !_allocate(ah, vg, lv, 1, allocatable_pvs)) {
+               alloc_destroy(ah);
+               return_NULL;
+       }
+
+       return ah;
+}
+
+/*
+ * Add new segments to an LV from supplied list of areas.
+ */
+int lv_add_segment(struct alloc_handle *ah,
+                  uint32_t first_area, uint32_t num_areas,
+                  struct logical_volume *lv,
+                  const struct segment_type *segtype,
+                  uint32_t stripe_size,
+                  uint64_t status,
+                  uint32_t region_size)
+{
+       if (!segtype) {
+               log_error("Missing segtype in lv_add_segment().");
+               return 0;
+       }
+
+       if (segtype_is_virtual(segtype)) {
+               log_error("lv_add_segment cannot handle virtual segments");
+               return 0;
+       }
+
+       if ((status & MIRROR_LOG) && dm_list_size(&lv->segments)) {
+               log_error("Log segments can only be added to an empty LV");
+               return 0;
+       }
+
+       if (!_setup_alloced_segments(lv, &ah->alloced_areas[first_area],
+                                    num_areas, status,
+                                    stripe_size, segtype,
+                                    region_size))
+               return_0;
+
+       if ((segtype->flags & SEG_CAN_SPLIT) && !lv_merge_segments(lv)) {
+               log_error("Couldn't merge segments after extending "
+                         "logical volume.");
+               return 0;
+       }
+
+       if (lv->vg->fid->fmt->ops->lv_setup &&
+           !lv->vg->fid->fmt->ops->lv_setup(lv->vg->fid, lv))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * "mirror" segment type doesn't support split.
+ * So, when adding mirrors to linear LV segment, first split it,
+ * then convert it to "mirror" and add areas.
+ */
+static struct lv_segment *_convert_seg_to_mirror(struct lv_segment *seg,
+                                                uint32_t region_size,
+                                                struct logical_volume *log_lv)
+{
+       struct lv_segment *newseg;
+       uint32_t s;
+
+       if (!seg_is_striped(seg)) {
+               log_error("Can't convert non-striped segment to mirrored.");
+               return NULL;
+       }
+
+       if (seg->area_count > 1) {
+               log_error("Can't convert striped segment with multiple areas "
+                         "to mirrored.");
+               return NULL;
+       }
+
+       if (!(newseg = alloc_lv_segment(seg->lv->vg->cmd->mem,
+                                       get_segtype_from_string(seg->lv->vg->cmd, "mirror"),
+                                       seg->lv, seg->le, seg->len,
+                                       seg->status, seg->stripe_size,
+                                       log_lv,
+                                       seg->area_count, seg->area_len,
+                                       seg->chunk_size, region_size,
+                                       seg->extents_copied, NULL))) {
+               log_error("Couldn't allocate converted LV segment");
+               return NULL;
+       }
+
+       for (s = 0; s < seg->area_count; s++)
+               if (!move_lv_segment_area(newseg, s, seg, s))
+                       return_NULL;
+
+       seg->pvmove_source_seg = NULL; /* Not maintained after allocation */
+
+       dm_list_add(&seg->list, &newseg->list);
+       dm_list_del(&seg->list);
+
+       return newseg;
+}
+
+/*
+ * Add new areas to mirrored segments
+ */
+int lv_add_mirror_areas(struct alloc_handle *ah,
+                       struct logical_volume *lv, uint32_t le,
+                       uint32_t region_size)
+{
+       struct alloced_area *aa;
+       struct lv_segment *seg;
+       uint32_t current_le = le;
+       uint32_t s, old_area_count, new_area_count;
+
+       dm_list_iterate_items(aa, &ah->alloced_areas[0]) {
+               if (!(seg = find_seg_by_le(lv, current_le))) {
+                       log_error("Failed to find segment for %s extent %"
+                                 PRIu32, lv->name, current_le);
+                       return 0;
+               }
+
+               /* Allocator assures aa[0].len <= seg->area_len */
+               if (aa[0].len < seg->area_len) {
+                       if (!lv_split_segment(lv, seg->le + aa[0].len)) {
+                               log_error("Failed to split segment at %s "
+                                         "extent %" PRIu32, lv->name, le);
+                               return 0;
+                       }
+               }
+
+               if (!seg_is_mirrored(seg) &&
+                   (!(seg = _convert_seg_to_mirror(seg, region_size, NULL))))
+                       return_0;
+
+               old_area_count = seg->area_count;
+               new_area_count = old_area_count + ah->area_count;
+
+               if (!_lv_segment_add_areas(lv, seg, new_area_count))
+                       return_0;
+
+               for (s = 0; s < ah->area_count; s++) {
+                       if (!set_lv_segment_area_pv(seg, s + old_area_count,
+                                                   aa[s].pv, aa[s].pe))
+                               return_0;
+               }
+
+               current_le += seg->area_len;
+       }
+
+       lv->status |= MIRRORED;
+
+       if (lv->vg->fid->fmt->ops->lv_setup &&
+           !lv->vg->fid->fmt->ops->lv_setup(lv->vg->fid, lv))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Add mirror image LVs to mirrored segments
+ */
+int lv_add_mirror_lvs(struct logical_volume *lv,
+                     struct logical_volume **sub_lvs,
+                     uint32_t num_extra_areas,
+                     uint64_t status, uint32_t region_size)
+{
+       struct lv_segment *seg;
+       uint32_t old_area_count, new_area_count;
+       uint32_t m;
+       struct segment_type *mirror_segtype;
+
+       seg = first_seg(lv);
+
+       if (dm_list_size(&lv->segments) != 1 || seg_type(seg, 0) != AREA_LV) {
+               log_error("Mirror layer must be inserted before adding mirrors");
+               return_0;
+       }
+
+       mirror_segtype = get_segtype_from_string(lv->vg->cmd, "mirror");
+       if (seg->segtype != mirror_segtype)
+               if (!(seg = _convert_seg_to_mirror(seg, region_size, NULL)))
+                       return_0;
+
+       if (region_size && region_size != seg->region_size) {
+               log_error("Conflicting region_size");
+               return 0;
+       }
+
+       old_area_count = seg->area_count;
+       new_area_count = old_area_count + num_extra_areas;
+
+       if (!_lv_segment_add_areas(lv, seg, new_area_count)) {
+               log_error("Failed to allocate widened LV segment for %s.",
+                         lv->name);
+               return 0;
+       }
+
+       for (m = 0; m < old_area_count; m++)
+               seg_lv(seg, m)->status |= status;
+
+       for (m = old_area_count; m < new_area_count; m++) {
+               if (!set_lv_segment_area_lv(seg, m, sub_lvs[m - old_area_count],
+                                           0, status))
+                       return_0;
+               lv_set_hidden(sub_lvs[m - old_area_count]);
+       }
+
+       lv->status |= MIRRORED;
+
+       return 1;
+}
+
+/*
+ * Turn an empty LV into a mirror log.
+ *
+ * FIXME: Mirrored logs are built inefficiently.
+ * A mirrored log currently uses the same layout that a mirror
+ * LV uses.  The mirror layer sits on top of AREA_LVs which form the
+ * legs, rather on AREA_PVs.  This is done to allow re-use of the
+ * various mirror functions to also handle the mirrored LV that makes
+ * up the log.
+ *
+ * If we used AREA_PVs under the mirror layer of a log, we could
+ * assemble it all at once by calling 'lv_add_segment' with the
+ * appropriate segtype (mirror/stripe), like this:
+ *     lv_add_segment(ah, ah->area_count, ah->log_area_count,
+ *                    log_lv, segtype, 0, MIRROR_LOG, 0);
+ *
+ * For now, we use the same mechanism to build a mirrored log as we
+ * do for building a mirrored LV: 1) create initial LV, 2) add a
+ * mirror layer, and 3) add the remaining copy LVs
+ */
+int lv_add_log_segment(struct alloc_handle *ah, uint32_t first_area,
+                      struct logical_volume *log_lv, uint64_t status)
+{
+
+       return lv_add_segment(ah, ah->area_count + first_area, 1, log_lv,
+                             get_segtype_from_string(log_lv->vg->cmd,
+                                                     "striped"),
+                             0, status, 0);
+}
+
+static int _lv_extend_mirror(struct alloc_handle *ah,
+                            struct logical_volume *lv,
+                            uint32_t extents, uint32_t first_area,
+                            uint32_t stripes, uint32_t stripe_size)
+{
+       struct lv_segment *seg;
+       uint32_t m, s;
+
+       seg = first_seg(lv);
+       for (m = first_area, s = 0; s < seg->area_count; s++) {
+               if (is_temporary_mirror_layer(seg_lv(seg, s))) {
+                       if (!_lv_extend_mirror(ah, seg_lv(seg, s), extents, m, stripes, stripe_size))
+                               return_0;
+                       m += lv_mirror_count(seg_lv(seg, s));
+                       continue;
+               }
+
+               if (!lv_add_segment(ah, m, stripes, seg_lv(seg, s),
+                                   get_segtype_from_string(lv->vg->cmd,
+                                                           "striped"),
+                                   stripe_size, 0, 0)) {
+                       log_error("Aborting. Failed to extend %s.",
+                                 seg_lv(seg, s)->name);
+                       return 0;
+               }
+               m += stripes;
+       }
+       seg->area_len += extents;
+       seg->len += extents;
+       lv->le_count += extents;
+       lv->size += (uint64_t) extents *lv->vg->extent_size;
+
+       return 1;
+}
+
+/*
+ * Entry point for single-step LV allocation + extension.
+ */
+int lv_extend(struct logical_volume *lv,
+             const struct segment_type *segtype,
+             uint32_t stripes, uint32_t stripe_size,
+             uint32_t mirrors, uint32_t extents,
+             struct physical_volume *mirrored_pv __attribute__((unused)),
+             uint32_t mirrored_pe __attribute__((unused)),
+             uint64_t status, struct dm_list *allocatable_pvs,
+             alloc_policy_t alloc)
+{
+       int r = 1;
+       struct alloc_handle *ah;
+
+       if (segtype_is_virtual(segtype))
+               return lv_add_virtual_segment(lv, status, extents, segtype);
+
+       if (!(ah = allocate_extents(lv->vg, lv, segtype, stripes, mirrors, 0, 0,
+                                   extents, allocatable_pvs, alloc, NULL)))
+               return_0;
+
+       if (mirrors < 2)
+               r = lv_add_segment(ah, 0, ah->area_count, lv, segtype,
+                                  stripe_size, status, 0);
+       else
+               r = _lv_extend_mirror(ah, lv, extents, 0, stripes, stripe_size);
+
+       alloc_destroy(ah);
+       return r;
+}
+
+/*
+ * Minimal LV renaming function.
+ * Metadata transaction should be made by caller.
+ * Assumes new_name is allocated from cmd->mem pool.
+ */
+static int _rename_single_lv(struct logical_volume *lv, char *new_name)
+{
+       struct volume_group *vg = lv->vg;
+
+       if (find_lv_in_vg(vg, new_name)) {
+               log_error("Logical volume \"%s\" already exists in "
+                         "volume group \"%s\"", new_name, vg->name);
+               return 0;
+       }
+
+       if (lv->status & LOCKED) {
+               log_error("Cannot rename locked LV %s", lv->name);
+               return 0;
+       }
+
+       lv->name = new_name;
+
+       return 1;
+}
+
+/*
+ * Rename sub LV.
+ * 'lv_name_old' and 'lv_name_new' are old and new names of the main LV.
+ */
+static int _rename_sub_lv(struct cmd_context *cmd,
+                         struct logical_volume *lv,
+                         const char *lv_name_old, const char *lv_name_new)
+{
+       char *suffix, *new_name;
+       size_t len;
+
+       /*
+        * A sub LV name starts with lv_name_old + '_'.
+        * The suffix follows lv_name_old and includes '_'.
+        */
+       len = strlen(lv_name_old);
+       if (strncmp(lv->name, lv_name_old, len) || lv->name[len] != '_') {
+               log_error("Cannot rename \"%s\": name format not recognized "
+                         "for internal LV \"%s\"",
+                         lv_name_old, lv->name);
+               return 0;
+       }
+       suffix = lv->name + len;
+
+       /*
+        * Compose a new name for sub lv:
+        *   e.g. new name is "lvol1_mlog"
+        *      if the sub LV is "lvol0_mlog" and
+        *      a new name for main LV is "lvol1"
+        */
+       len = strlen(lv_name_new) + strlen(suffix) + 1;
+       new_name = dm_pool_alloc(cmd->mem, len);
+       if (!new_name) {
+               log_error("Failed to allocate space for new name");
+               return 0;
+       }
+       if (!dm_snprintf(new_name, len, "%s%s", lv_name_new, suffix)) {
+               log_error("Failed to create new name");
+               return 0;
+       }
+
+       /* Rename it */
+       return _rename_single_lv(lv, new_name);
+}
+
+/* Callback for _for_each_sub_lv */
+static int _rename_cb(struct cmd_context *cmd, struct logical_volume *lv,
+                     void *data)
+{
+       struct lv_names *lv_names = (struct lv_names *) data;
+
+       return _rename_sub_lv(cmd, lv, lv_names->old, lv_names->new);
+}
+
+/*
+ * Loop down sub LVs and call "func" for each.
+ * "func" is responsible to log necessary information on failure.
+ */
+static int _for_each_sub_lv(struct cmd_context *cmd, struct logical_volume *lv,
+                           int (*func)(struct cmd_context *cmd,
+                                       struct logical_volume *lv,
+                                       void *data),
+                           void *data)
+{
+       struct logical_volume *org;
+       struct lv_segment *seg;
+       uint32_t s;
+
+       if (lv_is_cow(lv) && lv_is_virtual_origin(org = origin_from_cow(lv)))
+               if (!func(cmd, org, data))
+                       return_0;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (seg->log_lv && !func(cmd, seg->log_lv, data))
+                       return_0;
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_LV)
+                               continue;
+                       if (!func(cmd, seg_lv(seg, s), data))
+                               return_0;
+                       if (!_for_each_sub_lv(cmd, seg_lv(seg, s), func, data))
+                               return_0;
+               }
+       }
+
+       return 1;
+}
+
+
+/*
+ * Core of LV renaming routine.
+ * VG must be locked by caller.
+ */
+int lv_rename(struct cmd_context *cmd, struct logical_volume *lv,
+             const char *new_name)
+{
+       struct volume_group *vg = lv->vg;
+       struct lv_names lv_names;
+       DM_LIST_INIT(lvs_changed);
+       struct lv_list lvl, lvl2, *lvlp;
+       int r = 0;
+
+       /* rename is not allowed on sub LVs */
+       if (!lv_is_visible(lv)) {
+               log_error("Cannot rename internal LV \"%s\".", lv->name);
+               return 0;
+       }
+
+       if (find_lv_in_vg(vg, new_name)) {
+               log_error("Logical volume \"%s\" already exists in "
+                         "volume group \"%s\"", new_name, vg->name);
+               return 0;
+       }
+
+       if (lv->status & LOCKED) {
+               log_error("Cannot rename locked LV %s", lv->name);
+               return 0;
+       }
+
+       if (!archive(vg))
+               return 0;
+
+       /* rename sub LVs */
+       lv_names.old = lv->name;
+       lv_names.new = new_name;
+       if (!_for_each_sub_lv(cmd, lv, _rename_cb, (void *) &lv_names))
+               return 0;
+
+       /* rename main LV */
+       if (!(lv->name = dm_pool_strdup(cmd->mem, new_name))) {
+               log_error("Failed to allocate space for new name");
+               return 0;
+       }
+
+       lvl.lv = lv;
+       dm_list_add(&lvs_changed, &lvl.list);
+
+       /* rename active virtual origin too */
+       if (lv_is_cow(lv) && lv_is_virtual_origin(lvl2.lv = origin_from_cow(lv)))
+               dm_list_add_h(&lvs_changed, &lvl2.list);
+
+       log_verbose("Writing out updated volume group");
+       if (!vg_write(vg))
+               return 0;
+
+
+       if (!suspend_lvs(cmd, &lvs_changed)) {
+               vg_revert(vg);
+               goto_out;
+       }
+
+       if (!(r = vg_commit(vg)))
+               stack;
+
+       /*
+        * FIXME: resume LVs in reverse order to prevent memory
+        * lock imbalance when resuming virtual snapshot origin
+        * (resume of snapshot resumes origin too)
+        */
+       dm_list_iterate_back_items(lvlp, &lvs_changed)
+               if (!resume_lv(cmd, lvlp->lv))
+                       stack;
+out:
+       backup(vg);
+       return r;
+}
+
+char *generate_lv_name(struct volume_group *vg, const char *format,
+                      char *buffer, size_t len)
+{
+       struct lv_list *lvl;
+       int high = -1, i;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (sscanf(lvl->lv->name, format, &i) != 1)
+                       continue;
+
+               if (i > high)
+                       high = i;
+       }
+
+       if (dm_snprintf(buffer, len, format, high + 1) < 0)
+               return NULL;
+
+       return buffer;
+}
+
+int vg_max_lv_reached(struct volume_group *vg)
+{
+       if (!vg->max_lv)
+               return 0;
+
+       if (vg->max_lv > vg_visible_lvs(vg))
+               return 0;
+
+       log_verbose("Maximum number of logical volumes (%u) reached "
+                   "in volume group %s", vg->max_lv, vg->name);
+
+       return 1;
+}
+
+struct logical_volume *alloc_lv(struct dm_pool *mem)
+{
+       struct logical_volume *lv;
+
+       if (!(lv = dm_pool_zalloc(mem, sizeof(*lv)))) {
+               log_error("Unable to allocate logical volume structure");
+               return NULL;
+       }
+
+       lv->snapshot = NULL;
+       dm_list_init(&lv->snapshot_segs);
+       dm_list_init(&lv->segments);
+       dm_list_init(&lv->tags);
+       dm_list_init(&lv->segs_using_this_lv);
+       dm_list_init(&lv->rsites);
+
+       return lv;
+}
+
+/*
+ * Create a new empty LV.
+ */
+struct logical_volume *lv_create_empty(const char *name,
+                                      union lvid *lvid,
+                                      uint64_t status,
+                                      alloc_policy_t alloc,
+                                      struct volume_group *vg)
+{
+       struct format_instance *fi = vg->fid;
+       struct logical_volume *lv;
+       char dname[NAME_LEN];
+
+       if (vg_max_lv_reached(vg))
+               stack;
+
+       if (strstr(name, "%d") &&
+           !(name = generate_lv_name(vg, name, dname, sizeof(dname)))) {
+               log_error("Failed to generate unique name for the new "
+                         "logical volume");
+               return NULL;
+       } else if (find_lv_in_vg(vg, name)) {
+               log_error("Unable to create LV %s in Volume Group %s: "
+                         "name already in use.", name, vg->name);
+               return NULL;
+       }
+
+       log_verbose("Creating logical volume %s", name);
+
+       if (!(lv = alloc_lv(vg->vgmem)))
+               return_NULL;
+
+       if (!(lv->name = dm_pool_strdup(vg->vgmem, name)))
+               goto_bad;
+
+       lv->status = status;
+       lv->alloc = alloc;
+       lv->read_ahead = vg->cmd->default_settings.read_ahead;
+       lv->major = -1;
+       lv->minor = -1;
+       lv->size = UINT64_C(0);
+       lv->le_count = 0;
+
+       if (lvid)
+               lv->lvid = *lvid;
+
+       if (!link_lv_to_vg(vg, lv))
+               goto_bad;
+       if (fi->fmt->ops->lv_setup && !fi->fmt->ops->lv_setup(fi, lv))
+               goto_bad;
+       return lv;
+bad:
+       dm_pool_free(vg->vgmem, lv);
+       return NULL;
+}
+
+static int _add_pvs(struct cmd_context *cmd, struct pv_segment *peg,
+                   uint32_t s __attribute__((unused)), void *data)
+{
+       struct seg_pvs *spvs = (struct seg_pvs *) data;
+       struct pv_list *pvl;
+
+       /* Don't add again if it's already on list. */
+       if (find_pv_in_pv_list(&spvs->pvs, peg->pv))
+                       return 1;
+
+       if (!(pvl = dm_pool_alloc(cmd->mem, sizeof(*pvl)))) {
+               log_error("pv_list allocation failed");
+               return 0;
+       }
+
+       pvl->pv = peg->pv;
+
+       dm_list_add(&spvs->pvs, &pvl->list);
+
+       return 1;
+}
+
+/*
+ * Construct dm_list of segments of LVs showing which PVs they use.
+ * For pvmove we use the *parent* LV so we can pick up stripes & existing mirrors etc.
+ */
+struct dm_list *build_parallel_areas_from_lv(struct cmd_context *cmd,
+                                            struct logical_volume *lv,
+                                            unsigned use_pvmove_parent_lv)
+{
+       struct dm_list *parallel_areas;
+       struct seg_pvs *spvs;
+       uint32_t current_le = 0;
+       struct lv_segment * uninitialized_var(seg);
+
+       if (!(parallel_areas = dm_pool_alloc(cmd->mem, sizeof(*parallel_areas)))) {
+               log_error("parallel_areas allocation failed");
+               return NULL;
+       }
+
+       dm_list_init(parallel_areas);
+
+       do {
+               if (!(spvs = dm_pool_zalloc(cmd->mem, sizeof(*spvs)))) {
+                       log_error("allocation failed");
+                       return NULL;
+               }
+
+               dm_list_init(&spvs->pvs);
+
+               spvs->le = current_le;
+               spvs->len = lv->le_count - current_le;
+
+               dm_list_add(parallel_areas, &spvs->list);
+
+               if (use_pvmove_parent_lv && !(seg = find_seg_by_le(lv, current_le))) {
+                       log_error("Failed to find segment for %s extent %" PRIu32,
+                                 lv->name, current_le);
+                       return 0;
+               }
+
+               /* Find next segment end */
+               /* FIXME Unnecessary nesting! */
+               if (!_for_each_pv(cmd, use_pvmove_parent_lv ? seg->pvmove_source_seg->lv : lv,
+                                 use_pvmove_parent_lv ? seg->pvmove_source_seg->le : current_le,
+                                 use_pvmove_parent_lv ? spvs->len * _calc_area_multiple(seg->pvmove_source_seg->segtype, seg->pvmove_source_seg->area_count, 0) : spvs->len,
+                                 use_pvmove_parent_lv ? seg->pvmove_source_seg : NULL,
+                                 &spvs->len,
+                                 0, 0, -1, 0, _add_pvs, (void *) spvs))
+                       return_NULL;
+
+               current_le = spvs->le + spvs->len;
+       } while (current_le < lv->le_count);
+
+       /* FIXME Merge adjacent segments with identical PV lists (avoids need for contiguous allocation attempts between successful allocations) */
+
+       return parallel_areas;
+}
+
+int link_lv_to_vg(struct volume_group *vg, struct logical_volume *lv)
+{
+       struct lv_list *lvl;
+
+       if (vg_max_lv_reached(vg))
+               stack;
+
+       if (!(lvl = dm_pool_zalloc(vg->vgmem, sizeof(*lvl))))
+               return_0;
+
+       lvl->lv = lv;
+       lv->vg = vg;
+       dm_list_add(&vg->lvs, &lvl->list);
+
+       return 1;
+}
+
+int unlink_lv_from_vg(struct logical_volume *lv)
+{
+       struct lv_list *lvl;
+
+       if (!(lvl = find_lv_in_vg(lv->vg, lv->name)))
+               return_0;
+
+       dm_list_del(&lvl->list);
+
+       return 1;
+}
+
+void lv_set_visible(struct logical_volume *lv)
+{
+       if (lv_is_visible(lv))
+               return;
+
+       lv->status |= VISIBLE_LV;
+
+       log_debug("LV %s in VG %s is now visible.",  lv->name, lv->vg->name);
+}
+
+void lv_set_hidden(struct logical_volume *lv)
+{
+       if (!lv_is_visible(lv))
+               return;
+
+       lv->status &= ~VISIBLE_LV;
+
+       log_debug("LV %s in VG %s is now hidden.",  lv->name, lv->vg->name);
+}
+
+int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
+                    const force_t force)
+{
+       struct volume_group *vg;
+       struct lvinfo info;
+       struct logical_volume *origin = NULL;
+       int was_merging = 0;
+
+       vg = lv->vg;
+
+       if (!vg_check_status(vg, LVM_WRITE))
+               return 0;
+
+       if (lv_is_origin(lv)) {
+               log_error("Can't remove logical volume \"%s\" under snapshot",
+                         lv->name);
+               return 0;
+       }
+
+       if (lv->status & MIRROR_IMAGE) {
+               log_error("Can't remove logical volume %s used by a mirror",
+                         lv->name);
+               return 0;
+       }
+
+       if (lv->status & MIRROR_LOG) {
+               log_error("Can't remove logical volume %s used as mirror log",
+                         lv->name);
+               return 0;
+       }
+
+       if (lv->status & LOCKED) {
+               log_error("Can't remove locked LV %s", lv->name);
+               return 0;
+       }
+
+       /* FIXME Ensure not referred to by another existing LVs */
+
+       if (lv_info(cmd, lv, 0, &info, 1, 0)) {
+               if (info.open_count) {
+                       log_error("Can't remove open logical volume \"%s\"",
+                                 lv->name);
+                       return 0;
+               }
+
+               if (lv_is_active(lv) && (force == PROMPT) &&
+                   lv_is_visible(lv) &&
+                   yes_no_prompt("Do you really want to remove active "
+                                 "%slogical volume %s? [y/n]: ",
+                                 vg_is_clustered(vg) ? "clustered " : "",
+                                 lv->name) == 'n') {
+                       log_error("Logical volume %s not removed", lv->name);
+                       return 0;
+               }
+       }
+
+       if (!archive(vg))
+               return 0;
+
+       if (lv_is_cow(lv)) {
+               origin = origin_from_cow(lv);
+               was_merging = lv_is_merging_origin(origin);
+               log_verbose("Removing snapshot %s", lv->name);
+               /* vg_remove_snapshot() will preload origin if it was merging */
+               if (!vg_remove_snapshot(lv))
+                       return_0;
+       }
+
+       if (!deactivate_lv(cmd, lv)) {
+               log_error("Unable to deactivate logical volume \"%s\"",
+                         lv->name);
+               return 0;
+       }
+
+       log_verbose("Releasing logical volume \"%s\"", lv->name);
+       if (!lv_remove(lv)) {
+               log_error("Error releasing logical volume \"%s\"", lv->name);
+               return 0;
+       }
+
+       /* store it on disks */
+       if (!vg_write(vg) || !vg_commit(vg))
+               return_0;
+
+       /* If no snapshots left, and was not merging, reload without -real. */
+       if (origin && (!lv_is_origin(origin) && !was_merging)) {
+               if (!suspend_lv(cmd, origin)) {
+                       log_error("Failed to refresh %s without snapshot.", origin->name);
+                       return 0;
+               }
+               if (!resume_lv(cmd, origin)) {
+                       log_error("Failed to resume %s.", origin->name);
+                       return 0;
+               }
+       }
+
+       backup(vg);
+
+       if (lv_is_visible(lv))
+               log_print("Logical volume \"%s\" successfully removed", lv->name);
+
+       return 1;
+}
+
+/*
+ * remove LVs with its dependencies - LV leaf nodes should be removed first
+ */
+int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume *lv,
+                               const force_t force, unsigned level)
+{
+       struct dm_list *snh, *snht;
+
+       if (lv_is_cow(lv)) {
+               /* A merging snapshot cannot be removed directly */
+               if (lv_is_merging_cow(lv) && !level) {
+                       log_error("Can't remove merging snapshot logical volume \"%s\"",
+                                 lv->name);
+                       return 0;
+               }
+       }
+
+       if (lv_is_origin(lv)) {
+               /* remove snapshot LVs first */
+               dm_list_iterate_safe(snh, snht, &lv->snapshot_segs) {
+                       if (!lv_remove_with_dependencies(cmd, dm_list_struct_base(snh, struct lv_segment,
+                                                                                 origin_list)->cow,
+                                                        force, level + 1))
+                               return 0;
+               }
+       }
+
+       return lv_remove_single(cmd, lv, force);
+}
+
+/*
+ * insert_layer_for_segments_on_pv() inserts a layer segment for a segment area.
+ * However, layer modification could split the underlying layer segment.
+ * This function splits the parent area according to keep the 1:1 relationship
+ * between the parent area and the underlying layer segment.
+ * Since the layer LV might have other layers below, build_parallel_areas()
+ * is used to find the lowest-level segment boundaries.
+ */
+static int _split_parent_area(struct lv_segment *seg, uint32_t s,
+                             struct dm_list *layer_seg_pvs)
+{
+       uint32_t parent_area_len, parent_le, layer_le;
+       uint32_t area_multiple;
+       struct seg_pvs *spvs;
+
+       if (seg_is_striped(seg))
+               area_multiple = seg->area_count;
+       else
+               area_multiple = 1;
+
+       parent_area_len = seg->area_len;
+       parent_le = seg->le;
+       layer_le = seg_le(seg, s);
+
+       while (parent_area_len > 0) {
+               /* Find the layer segment pointed at */
+               if (!(spvs = _find_seg_pvs_by_le(layer_seg_pvs, layer_le))) {
+                       log_error("layer segment for %s:%" PRIu32 " not found",
+                                 seg->lv->name, parent_le);
+                       return 0;
+               }
+
+               if (spvs->le != layer_le) {
+                       log_error("Incompatible layer boundary: "
+                                 "%s:%" PRIu32 "[%" PRIu32 "] on %s:%" PRIu32,
+                                 seg->lv->name, parent_le, s,
+                                 seg_lv(seg, s)->name, layer_le);
+                       return 0;
+               }
+
+               if (spvs->len < parent_area_len) {
+                       parent_le += spvs->len * area_multiple;
+                       if (!lv_split_segment(seg->lv, parent_le))
+                               return_0;
+               }
+
+               parent_area_len -= spvs->len;
+               layer_le += spvs->len;
+       }
+
+       return 1;
+}
+
+/*
+ * Split the parent LV segments if the layer LV below it is splitted.
+ */
+int split_parent_segments_for_layer(struct cmd_context *cmd,
+                                   struct logical_volume *layer_lv)
+{
+       struct lv_list *lvl;
+       struct logical_volume *parent_lv;
+       struct lv_segment *seg;
+       uint32_t s;
+       struct dm_list *parallel_areas;
+
+       if (!(parallel_areas = build_parallel_areas_from_lv(cmd, layer_lv, 0)))
+               return_0;
+
+       /* Loop through all LVs except itself */
+       dm_list_iterate_items(lvl, &layer_lv->vg->lvs) {
+               parent_lv = lvl->lv;
+               if (parent_lv == layer_lv)
+                       continue;
+
+               /* Find all segments that point at the layer LV */
+               dm_list_iterate_items(seg, &parent_lv->segments) {
+                       for (s = 0; s < seg->area_count; s++) {
+                               if (seg_type(seg, s) != AREA_LV ||
+                                   seg_lv(seg, s) != layer_lv)
+                                       continue;
+
+                               if (!_split_parent_area(seg, s, parallel_areas))
+                                       return_0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+/* Remove a layer from the LV */
+int remove_layers_for_segments(struct cmd_context *cmd,
+                              struct logical_volume *lv,
+                              struct logical_volume *layer_lv,
+                              uint64_t status_mask, struct dm_list *lvs_changed)
+{
+       struct lv_segment *seg, *lseg;
+       uint32_t s;
+       int lv_changed = 0;
+       struct lv_list *lvl;
+
+       log_very_verbose("Removing layer %s for segments of %s",
+                        layer_lv->name, lv->name);
+
+       /* Find all segments that point at the temporary mirror */
+       dm_list_iterate_items(seg, &lv->segments) {
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_LV ||
+                           seg_lv(seg, s) != layer_lv)
+                               continue;
+
+                       /* Find the layer segment pointed at */
+                       if (!(lseg = find_seg_by_le(layer_lv, seg_le(seg, s)))) {
+                               log_error("Layer segment found: %s:%" PRIu32,
+                                         layer_lv->name, seg_le(seg, s));
+                               return 0;
+                       }
+
+                       /* Check the segment params are compatible */
+                       if (!seg_is_striped(lseg) || lseg->area_count != 1) {
+                               log_error("Layer is not linear: %s:%" PRIu32,
+                                         layer_lv->name, lseg->le);
+                               return 0;
+                       }
+                       if ((lseg->status & status_mask) != status_mask) {
+                               log_error("Layer status does not match: "
+                                         "%s:%" PRIu32 " status: 0x%" PRIx64 "/0x%" PRIx64,
+                                         layer_lv->name, lseg->le,
+                                         lseg->status, status_mask);
+                               return 0;
+                       }
+                       if (lseg->le != seg_le(seg, s) ||
+                           lseg->area_len != seg->area_len) {
+                               log_error("Layer boundary mismatch: "
+                                         "%s:%" PRIu32 "-%" PRIu32 " on "
+                                         "%s:%" PRIu32 " / "
+                                         "%" PRIu32 "-%" PRIu32 " / ",
+                                         lv->name, seg->le, seg->area_len,
+                                         layer_lv->name, seg_le(seg, s),
+                                         lseg->le, lseg->area_len);
+                               return 0;
+                       }
+
+                       if (!move_lv_segment_area(seg, s, lseg, 0))
+                               return_0;
+
+                       /* Replace mirror with error segment */
+                       if (!(lseg->segtype =
+                             get_segtype_from_string(lv->vg->cmd, "error"))) {
+                               log_error("Missing error segtype");
+                               return 0;
+                       }
+                       lseg->area_count = 0;
+
+                       /* First time, add LV to list of LVs affected */
+                       if (!lv_changed && lvs_changed) {
+                               if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+                                       log_error("lv_list alloc failed");
+                                       return 0;
+                               }
+                               lvl->lv = lv;
+                               dm_list_add(lvs_changed, &lvl->list);
+                               lv_changed = 1;
+                       }
+               }
+       }
+       if (lv_changed && !lv_merge_segments(lv))
+               stack;
+
+       return 1;
+}
+
+/* Remove a layer */
+int remove_layers_for_segments_all(struct cmd_context *cmd,
+                                  struct logical_volume *layer_lv,
+                                  uint64_t status_mask,
+                                  struct dm_list *lvs_changed)
+{
+       struct lv_list *lvl;
+       struct logical_volume *lv1;
+
+       /* Loop through all LVs except the temporary mirror */
+       dm_list_iterate_items(lvl, &layer_lv->vg->lvs) {
+               lv1 = lvl->lv;
+               if (lv1 == layer_lv)
+                       continue;
+
+               if (!remove_layers_for_segments(cmd, lv1, layer_lv,
+                                               status_mask, lvs_changed))
+                       return_0;
+       }
+
+       if (!lv_empty(layer_lv))
+               return_0;
+
+       return 1;
+}
+
+static int _move_lv_segments(struct logical_volume *lv_to,
+                            struct logical_volume *lv_from,
+                            uint64_t set_status, uint64_t reset_status)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv_to->segments) {
+               if (seg->origin) {
+                       log_error("Can't move snapshot segment");
+                       return 0;
+               }
+       }
+
+       lv_to->segments = lv_from->segments;
+       lv_to->segments.n->p = &lv_to->segments;
+       lv_to->segments.p->n = &lv_to->segments;
+
+       dm_list_iterate_items(seg, &lv_to->segments) {
+               seg->lv = lv_to;
+               seg->status &= ~reset_status;
+               seg->status |= set_status;
+       }
+
+       dm_list_init(&lv_from->segments);
+
+       lv_to->le_count = lv_from->le_count;
+       lv_to->size = lv_from->size;
+
+       lv_from->le_count = 0;
+       lv_from->size = 0;
+
+       return 1;
+}
+
+/* Remove a layer from the LV */
+int remove_layer_from_lv(struct logical_volume *lv,
+                        struct logical_volume *layer_lv)
+{
+       struct logical_volume *parent;
+       struct lv_segment *parent_seg;
+       struct segment_type *segtype;
+
+       log_very_verbose("Removing layer %s for %s", layer_lv->name, lv->name);
+
+       if (!(parent_seg = get_only_segment_using_this_lv(layer_lv))) {
+               log_error("Failed to find layer %s in %s",
+               layer_lv->name, lv->name);
+               return 0;
+       }
+       parent = parent_seg->lv;
+
+       /*
+        * Before removal, the layer should be cleaned up,
+        * i.e. additional segments and areas should have been removed.
+        */
+       if (dm_list_size(&parent->segments) != 1 ||
+           parent_seg->area_count != 1 ||
+           seg_type(parent_seg, 0) != AREA_LV ||
+           layer_lv != seg_lv(parent_seg, 0) ||
+           parent->le_count != layer_lv->le_count)
+               return_0;
+
+       if (!lv_empty(parent))
+               return_0;
+
+       if (!_move_lv_segments(parent, layer_lv, 0, 0))
+               return_0;
+
+       /* Replace the empty layer with error segment */
+       segtype = get_segtype_from_string(lv->vg->cmd, "error");
+       if (!lv_add_virtual_segment(layer_lv, 0, parent->le_count, segtype))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Create and insert a linear LV "above" lv_where.
+ * After the insertion, a new LV named lv_where->name + suffix is created
+ * and all segments of lv_where is moved to the new LV.
+ * lv_where will have a single segment which maps linearly to the new LV.
+ */
+struct logical_volume *insert_layer_for_lv(struct cmd_context *cmd,
+                                          struct logical_volume *lv_where,
+                                          uint64_t status,
+                                          const char *layer_suffix)
+{
+       struct logical_volume *layer_lv;
+       char *name;
+       size_t len;
+       struct segment_type *segtype;
+       struct lv_segment *mapseg;
+
+       /* create an empty layer LV */
+       len = strlen(lv_where->name) + 32;
+       if (!(name = alloca(len))) {
+               log_error("layer name allocation failed. "
+                         "Remove new LV and retry.");
+               return NULL;
+       }
+
+       if (dm_snprintf(name, len, "%s%s", lv_where->name, layer_suffix) < 0) {
+               log_error("layer name allocation failed. "
+                         "Remove new LV and retry.");
+               return NULL;
+       }
+
+       if (!(layer_lv = lv_create_empty(name, NULL, LVM_READ | LVM_WRITE,
+                                        ALLOC_INHERIT, lv_where->vg))) {
+               log_error("Creation of layer LV failed");
+               return NULL;
+       }
+
+       if (lv_is_active(lv_where) && strstr(name, "_mimagetmp")) {
+               log_very_verbose("Creating transient LV %s for mirror conversion in VG %s.", name, lv_where->vg->name);
+
+               segtype = get_segtype_from_string(cmd, "error");
+
+               if (!lv_add_virtual_segment(layer_lv, 0, lv_where->le_count, segtype)) {
+                       log_error("Creation of transient LV %s for mirror conversion in VG %s failed.", name, lv_where->vg->name);
+                       return NULL;
+               }
+
+               if (!vg_write(lv_where->vg)) {
+                       log_error("Failed to write intermediate VG %s metadata for mirror conversion.", lv_where->vg->name);
+                       return NULL;
+               }
+
+               if (!vg_commit(lv_where->vg)) {
+                       log_error("Failed to commit intermediate VG %s metadata for mirror conversion.", lv_where->vg->name);
+                       vg_revert(lv_where->vg);
+                       return NULL;
+               }
+
+               if (!activate_lv(cmd, layer_lv)) {
+                       log_error("Failed to resume transient error LV %s for mirror conversion in VG %s.", name, lv_where->vg->name);
+                       return NULL;
+               }
+       }
+
+       log_very_verbose("Inserting layer %s for %s",
+                        layer_lv->name, lv_where->name);
+
+       if (!_move_lv_segments(layer_lv, lv_where, 0, 0))
+               return_NULL;
+
+       if (!(segtype = get_segtype_from_string(cmd, "striped")))
+               return_NULL;
+
+       /* allocate a new linear segment */
+       if (!(mapseg = alloc_lv_segment(cmd->mem, segtype,
+                                       lv_where, 0, layer_lv->le_count,
+                                       status, 0, NULL, 1, layer_lv->le_count,
+                                       0, 0, 0, NULL)))
+               return_NULL;
+
+       /* map the new segment to the original underlying are */
+       if (!set_lv_segment_area_lv(mapseg, 0, layer_lv, 0, 0))
+               return_NULL;
+
+       /* add the new segment to the layer LV */
+       dm_list_add(&lv_where->segments, &mapseg->list);
+       lv_where->le_count = layer_lv->le_count;
+       lv_where->size = lv_where->le_count * lv_where->vg->extent_size;
+
+       return layer_lv;
+}
+
+/*
+ * Extend and insert a linear layer LV beneath the source segment area.
+ */
+static int _extend_layer_lv_for_segment(struct logical_volume *layer_lv,
+                                       struct lv_segment *seg, uint32_t s,
+                                       uint64_t status)
+{
+       struct lv_segment *mapseg;
+       struct segment_type *segtype;
+       struct physical_volume *src_pv = seg_pv(seg, s);
+       uint32_t src_pe = seg_pe(seg, s);
+
+       if (seg_type(seg, s) != AREA_PV && seg_type(seg, s) != AREA_LV)
+               return_0;
+
+       if (!(segtype = get_segtype_from_string(layer_lv->vg->cmd, "striped")))
+               return_0;
+
+       /* FIXME Incomplete message? Needs more context */
+       log_very_verbose("Inserting %s:%" PRIu32 "-%" PRIu32 " of %s/%s",
+                        pv_dev_name(src_pv),
+                        src_pe, src_pe + seg->area_len - 1,
+                        seg->lv->vg->name, seg->lv->name);
+
+       /* allocate a new segment */
+       if (!(mapseg = alloc_lv_segment(layer_lv->vg->cmd->mem, segtype,
+                                       layer_lv, layer_lv->le_count,
+                                       seg->area_len, status, 0,
+                                       NULL, 1, seg->area_len, 0, 0, 0, seg)))
+               return_0;
+
+       /* map the new segment to the original underlying are */
+       if (!move_lv_segment_area(mapseg, 0, seg, s))
+               return_0;
+
+       /* add the new segment to the layer LV */
+       dm_list_add(&layer_lv->segments, &mapseg->list);
+       layer_lv->le_count += seg->area_len;
+       layer_lv->size += seg->area_len * layer_lv->vg->extent_size;
+
+       /* map the original area to the new segment */
+       if (!set_lv_segment_area_lv(seg, s, layer_lv, mapseg->le, 0))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Match the segment area to PEs in the pvl
+ * (the segment area boundary should be aligned to PE ranges by
+ *  _adjust_layer_segments() so that there is no partial overlap.)
+ */
+static int _match_seg_area_to_pe_range(struct lv_segment *seg, uint32_t s,
+                                      struct pv_list *pvl)
+{
+       struct pe_range *per;
+       uint32_t pe_start, per_end;
+
+       if (!pvl)
+               return 1;
+
+       if (seg_type(seg, s) != AREA_PV || seg_dev(seg, s) != pvl->pv->dev)
+               return 0;
+
+       pe_start = seg_pe(seg, s);
+
+       /* Do these PEs match to any of the PEs in pvl? */
+       dm_list_iterate_items(per, pvl->pe_ranges) {
+               per_end = per->start + per->count - 1;
+
+               if ((pe_start < per->start) || (pe_start > per_end))
+                       continue;
+
+               /* FIXME Missing context in this message - add LV/seg details */
+               log_debug("Matched PE range %s:%" PRIu32 "-%" PRIu32 " against "
+                         "%s %" PRIu32 " len %" PRIu32, dev_name(pvl->pv->dev),
+                         per->start, per_end, dev_name(seg_dev(seg, s)),
+                         seg_pe(seg, s), seg->area_len);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * For each segment in lv_where that uses a PV in pvl directly,
+ * split the segment if it spans more than one underlying PV.
+ */
+static int _align_segment_boundary_to_pe_range(struct logical_volume *lv_where,
+                                              struct pv_list *pvl)
+{
+       struct lv_segment *seg;
+       struct pe_range *per;
+       uint32_t pe_start, pe_end, per_end, stripe_multiplier, s;
+
+       if (!pvl)
+               return 1;
+
+       /* Split LV segments to match PE ranges */
+       dm_list_iterate_items(seg, &lv_where->segments) {
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_PV ||
+                           seg_dev(seg, s) != pvl->pv->dev)
+                               continue;
+
+                       /* Do these PEs match with the condition? */
+                       dm_list_iterate_items(per, pvl->pe_ranges) {
+                               pe_start = seg_pe(seg, s);
+                               pe_end = pe_start + seg->area_len - 1;
+                               per_end = per->start + per->count - 1;
+
+                               /* No overlap? */
+                               if ((pe_end < per->start) ||
+                                   (pe_start > per_end))
+                                       continue;
+
+                               if (seg_is_striped(seg))
+                                       stripe_multiplier = seg->area_count;
+                               else
+                                       stripe_multiplier = 1;
+
+                               if ((per->start != pe_start &&
+                                    per->start > pe_start) &&
+                                   !lv_split_segment(lv_where, seg->le +
+                                                     (per->start - pe_start) *
+                                                     stripe_multiplier))
+                                       return_0;
+
+                               if ((per_end != pe_end &&
+                                    per_end < pe_end) &&
+                                   !lv_split_segment(lv_where, seg->le +
+                                                     (per_end - pe_start + 1) *
+                                                     stripe_multiplier))
+                                       return_0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Scan lv_where for segments on a PV in pvl, and for each one found
+ * append a linear segment to lv_layer and insert it between the two.
+ *
+ * If pvl is empty, a layer is placed under the whole of lv_where.
+ * If the layer is inserted, lv_where is added to lvs_changed.
+ */
+int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
+                                   struct logical_volume *lv_where,
+                                   struct logical_volume *layer_lv,
+                                   uint64_t status,
+                                   struct pv_list *pvl,
+                                   struct dm_list *lvs_changed)
+{
+       struct lv_segment *seg;
+       struct lv_list *lvl;
+       int lv_used = 0;
+       uint32_t s;
+
+       log_very_verbose("Inserting layer %s for segments of %s on %s",
+                        layer_lv->name, lv_where->name,
+                        pvl ? pv_dev_name(pvl->pv) : "any");
+
+       if (!_align_segment_boundary_to_pe_range(lv_where, pvl))
+               return_0;
+
+       /* Work through all segments on the supplied PV */
+       dm_list_iterate_items(seg, &lv_where->segments) {
+               for (s = 0; s < seg->area_count; s++) {
+                       if (!_match_seg_area_to_pe_range(seg, s, pvl))
+                               continue;
+
+                       /* First time, add LV to list of LVs affected */
+                       if (!lv_used && lvs_changed) {
+                               if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+                                       log_error("lv_list alloc failed");
+                                       return 0;
+                               }
+                               lvl->lv = lv_where;
+                               dm_list_add(lvs_changed, &lvl->list);
+                               lv_used = 1;
+                       }
+
+                       if (!_extend_layer_lv_for_segment(layer_lv, seg, s,
+                                                         status)) {
+                               log_error("Failed to insert segment in layer "
+                                         "LV %s under %s:%" PRIu32 "-%" PRIu32,
+                                         layer_lv->name, lv_where->name,
+                                         seg->le, seg->le + seg->len);
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Initialize the LV with 'value'.
+ */
+int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
+          uint64_t sectors, int value)
+{
+       struct device *dev;
+       char *name;
+
+       /*
+        * FIXME:
+        * <clausen> also, more than 4k
+        * <clausen> say, reiserfs puts it's superblock 32k in, IIRC
+        * <ejt_> k, I'll drop a fixme to that effect
+        *         (I know the device is at least 4k, but not 32k)
+        */
+       if (!(name = dm_pool_alloc(cmd->mem, PATH_MAX))) {
+               log_error("Name allocation failed - device not cleared");
+               return 0;
+       }
+
+       if (dm_snprintf(name, PATH_MAX, "%s%s/%s", cmd->dev_dir,
+                       lv->vg->name, lv->name) < 0) {
+               log_error("Name too long - device not cleared (%s)", lv->name);
+               return 0;
+       }
+
+       log_verbose("Clearing start of logical volume \"%s\"", lv->name);
+
+       if (!(dev = dev_cache_get(name, NULL))) {
+               log_error("%s: not found: device not cleared", name);
+               return 0;
+       }
+
+       if (!dev_open_quiet(dev))
+               return_0;
+
+       if (!sectors)
+               sectors = UINT64_C(4096) >> SECTOR_SHIFT;
+
+       if (sectors > lv->size)
+               sectors = lv->size;
+
+       if (!dev_set(dev, UINT64_C(0), (size_t) sectors << SECTOR_SHIFT, value))
+               stack;
+
+       dev_flush(dev);
+
+       if (!dev_close_immediate(dev))
+                stack;
+
+       return 1;
+}
+
+
+static struct logical_volume *_create_virtual_origin(struct cmd_context *cmd,
+                                                    struct volume_group *vg,
+                                                    const char *lv_name,
+                                                    uint32_t permission,
+                                                    uint64_t voriginextents)
+{
+       const struct segment_type *segtype;
+       size_t len;
+       char *vorigin_name;
+       struct logical_volume *lv;
+
+       if (!(segtype = get_segtype_from_string(cmd, "zero"))) {
+               log_error("Zero segment type for virtual origin not found");
+               return NULL;
+       }
+
+       len = strlen(lv_name) + 32;
+       if (!(vorigin_name = alloca(len)) ||
+           dm_snprintf(vorigin_name, len, "%s_vorigin", lv_name) < 0) {
+               log_error("Virtual origin name allocation failed.");
+               return NULL;
+       }
+
+       if (!(lv = lv_create_empty(vorigin_name, NULL, permission,
+                                  ALLOC_INHERIT, vg)))
+               return_NULL;
+
+       if (!lv_extend(lv, segtype, 1, 0, 1, voriginextents, NULL, 0u, 0u,
+                      NULL, ALLOC_INHERIT))
+               return_NULL;
+
+       /* store vg on disk(s) */
+       if (!vg_write(vg) || !vg_commit(vg))
+               return_NULL;
+
+       backup(vg);
+
+       return lv;
+}
+
+int lv_create_single(struct volume_group *vg,
+                    struct lvcreate_params *lp)
+{
+       struct cmd_context *cmd = vg->cmd;
+       uint32_t size_rest;
+       uint64_t status = UINT64_C(0);
+       struct logical_volume *lv, *org = NULL;
+       int origin_active = 0;
+       struct lvinfo info;
+
+       if (lp->lv_name && find_lv_in_vg(vg, lp->lv_name)) {
+               log_error("Logical volume \"%s\" already exists in "
+                         "volume group \"%s\"", lp->lv_name, lp->vg_name);
+               return 0;
+       }
+
+       if (vg_max_lv_reached(vg)) {
+               log_error("Maximum number of logical volumes (%u) reached "
+                         "in volume group %s", vg->max_lv, vg->name);
+               return 0;
+       }
+
+       if (lp->mirrors > 1 && !(vg->fid->fmt->features & FMT_SEGMENTS)) {
+               log_error("Metadata does not support mirroring.");
+               return 0;
+       }
+
+       if (lp->read_ahead != DM_READ_AHEAD_AUTO &&
+           lp->read_ahead != DM_READ_AHEAD_NONE &&
+           (vg->fid->fmt->features & FMT_RESTRICTED_READAHEAD) &&
+           (lp->read_ahead < 2 || lp->read_ahead > 120)) {
+               log_error("Metadata only supports readahead values between 2 and 120.");
+               return 0;
+       }
+
+       if (lp->stripe_size > vg->extent_size) {
+               log_error("Reducing requested stripe size %s to maximum, "
+                         "physical extent size %s",
+                         display_size(cmd, (uint64_t) lp->stripe_size),
+                         display_size(cmd, (uint64_t) vg->extent_size));
+               lp->stripe_size = vg->extent_size;
+       }
+
+       /* Need to check the vg's format to verify this - the cmd format isn't setup properly yet */
+       if (lp->stripes > 1 &&
+           !(vg->fid->fmt->features & FMT_UNLIMITED_STRIPESIZE) &&
+           (lp->stripe_size > STRIPE_SIZE_MAX)) {
+               log_error("Stripe size may not exceed %s",
+                         display_size(cmd, (uint64_t) STRIPE_SIZE_MAX));
+               return 0;
+       }
+
+       if ((size_rest = lp->extents % lp->stripes)) {
+               log_print("Rounding size (%d extents) up to stripe boundary "
+                         "size (%d extents)", lp->extents,
+                         lp->extents - size_rest + lp->stripes);
+               lp->extents = lp->extents - size_rest + lp->stripes;
+       }
+
+       if (lp->zero && !activation()) {
+               log_error("Can't wipe start of new LV without using "
+                         "device-mapper kernel driver");
+               return 0;
+       }
+
+       status |= lp->permission | VISIBLE_LV;
+
+       if (lp->snapshot) {
+               if (!activation()) {
+                       log_error("Can't create snapshot without using "
+                                 "device-mapper kernel driver");
+                       return 0;
+               }
+               /* FIXME Allow exclusive activation. */
+               if (vg_is_clustered(vg)) {
+                       log_error("Clustered snapshots are not yet supported.");
+                       return 0;
+               }
+
+               /* Must zero cow */
+               status |= LVM_WRITE;
+
+               if (lp->voriginsize)
+                       origin_active = 1;
+               else {
+
+                       if (!(org = find_lv(vg, lp->origin))) {
+                               log_error("Couldn't find origin volume '%s'.",
+                                         lp->origin);
+                               return 0;
+                       }
+                       if (lv_is_virtual_origin(org)) {
+                               log_error("Can't share virtual origins. "
+                                         "Use --virtualsize.");
+                               return 0;
+                       }
+                       if (lv_is_cow(org)) {
+                               log_error("Snapshots of snapshots are not "
+                                         "supported yet.");
+                               return 0;
+                       }
+                       if (org->status & LOCKED) {
+                               log_error("Snapshots of locked devices are not "
+                                         "supported yet");
+                               return 0;
+                       }
+                       if (lv_is_merging_origin(org)) {
+                               log_error("Snapshots of an origin that has a "
+                                         "merging snapshot is not supported");
+                               return 0;
+                       }
+                       if ((org->status & MIRROR_IMAGE) ||
+                           (org->status & MIRROR_LOG)) {
+                               log_error("Snapshots of mirror %ss "
+                                         "are not supported",
+                                         (org->status & MIRROR_LOG) ?
+                                         "log" : "image");
+                               return 0;
+                       }
+
+                       if (!lv_info(cmd, org, 0, &info, 0, 0)) {
+                               log_error("Check for existence of snapshot "
+                                         "origin '%s' failed.", org->name);
+                               return 0;
+                       }
+                       origin_active = info.exists;
+               }
+       }
+
+       if (!lp->extents) {
+               log_error("Unable to create new logical volume with no extents");
+               return 0;
+       }
+
+       if (lp->snapshot && (lp->extents * vg->extent_size < 2 * lp->chunk_size)) {
+               log_error("Unable to create a snapshot smaller than 2 chunks.");
+               return 0;
+       }
+
+       if (!seg_is_virtual(lp) &&
+           vg->free_count < lp->extents) {
+               log_error("Insufficient free extents (%u) in volume group %s: "
+                         "%u required", vg->free_count, vg->name, lp->extents);
+               return 0;
+       }
+
+       if (lp->stripes > dm_list_size(lp->pvh) && lp->alloc != ALLOC_ANYWHERE) {
+               log_error("Number of stripes (%u) must not exceed "
+                         "number of physical volumes (%d)", lp->stripes,
+                         dm_list_size(lp->pvh));
+               return 0;
+       }
+
+       if (lp->mirrors > 1 && !activation()) {
+               log_error("Can't create mirror without using "
+                         "device-mapper kernel driver.");
+               return 0;
+       }
+
+       /* The snapshot segment gets created later */
+       if (lp->snapshot &&
+           !(lp->segtype = get_segtype_from_string(cmd, "striped")))
+               return_0;
+
+       if (!archive(vg))
+               return 0;
+
+       if (!dm_list_empty(&lp->tags)) {
+               if (!(vg->fid->fmt->features & FMT_TAGS)) {
+                       log_error("Volume group %s does not support tags",
+                                 vg->name);
+                       return 0;
+               }
+       }
+
+       if (lp->mirrors > 1) {
+               init_mirror_in_sync(lp->nosync);
+
+               if (lp->nosync) {
+                       log_warn("WARNING: New mirror won't be synchronised. "
+                                 "Don't read what you didn't write!");
+                       status |= MIRROR_NOTSYNCED;
+               }
+       }
+
+       if (!(lv = lv_create_empty(lp->lv_name ? lp->lv_name : "lvol%d", NULL,
+                                  status, lp->alloc, vg)))
+               return_0;
+
+       if (lp->read_ahead != lv->read_ahead) {
+               log_verbose("Setting read ahead sectors");
+               lv->read_ahead = lp->read_ahead;
+       }
+
+       if (lp->minor >= 0) {
+               lv->major = lp->major;
+               lv->minor = lp->minor;
+               lv->status |= FIXED_MINOR;
+               log_verbose("Setting device number to (%d, %d)", lv->major,
+                           lv->minor);
+       }
+
+       if (!dm_list_empty(&lp->tags))
+               dm_list_splice(&lv->tags, &lp->tags);
+
+       if (!lv_extend(lv, lp->segtype, lp->stripes, lp->stripe_size,
+                      1, lp->extents, NULL, 0u, 0u, lp->pvh, lp->alloc))
+               return_0;
+
+       if (lp->mirrors > 1) {
+               if (!lv_add_mirrors(cmd, lv, lp->mirrors - 1, lp->stripes,
+                                   lp->stripe_size,
+                                   adjusted_mirror_region_size(
+                                               vg->extent_size,
+                                               lv->le_count,
+                                               lp->region_size),
+                                   lp->log_count, lp->pvh, lp->alloc,
+                                   MIRROR_BY_LV |
+                                   (lp->nosync ? MIRROR_SKIP_INIT_SYNC : 0))) {
+                       stack;
+                       goto revert_new_lv;
+               }
+       }
+
+       /* store vg on disk(s) */
+       if (!vg_write(vg) || !vg_commit(vg))
+               return_0;
+
+       backup(vg);
+
+       init_dmeventd_monitor(lp->activation_monitoring);
+
+       if (lp->snapshot) {
+               if (!activate_lv_excl(cmd, lv)) {
+                       log_error("Aborting. Failed to activate snapshot "
+                                 "exception store.");
+                       goto revert_new_lv;
+               }
+       } else if (!activate_lv(cmd, lv)) {
+               log_error("Failed to activate new LV.");
+               if (lp->zero)
+                       goto deactivate_and_revert_new_lv;
+               return 0;
+       }
+
+       if (!lp->zero && !lp->snapshot)
+               log_warn("WARNING: \"%s\" not zeroed", lv->name);
+       else if (!set_lv(cmd, lv, UINT64_C(0), 0)) {
+               log_error("Aborting. Failed to wipe %s.",
+                         lp->snapshot ? "snapshot exception store" :
+                                        "start of new LV");
+               goto deactivate_and_revert_new_lv;
+       }
+
+       if (lp->snapshot) {
+               /* Reset permission after zeroing */
+               if (!(lp->permission & LVM_WRITE))
+                       lv->status &= ~LVM_WRITE;
+
+               /* COW area must be deactivated if origin is not active */
+               if (!origin_active && !deactivate_lv(cmd, lv)) {
+                       log_error("Aborting. Couldn't deactivate snapshot "
+                                 "COW area. Manual intervention required.");
+                       return 0;
+               }
+
+               /* A virtual origin must be activated explicitly. */
+               if (lp->voriginsize &&
+                   (!(org = _create_virtual_origin(cmd, vg, lv->name,
+                                                   lp->permission,
+                                                   lp->voriginextents)) ||
+                    !activate_lv(cmd, org))) {
+                       log_error("Couldn't create virtual origin for LV %s",
+                                 lv->name);
+                       if (org && !lv_remove(org))
+                               stack;
+                       goto deactivate_and_revert_new_lv;
+               }
+
+               /* cow LV remains active and becomes snapshot LV */
+
+               if (!vg_add_snapshot(org, lv, NULL,
+                                    org->le_count, lp->chunk_size)) {
+                       log_error("Couldn't create snapshot.");
+                       goto deactivate_and_revert_new_lv;
+               }
+
+               /* store vg on disk(s) */
+               if (!vg_write(vg))
+                       return_0;
+
+               if (!suspend_lv(cmd, org)) {
+                       log_error("Failed to suspend origin %s", org->name);
+                       vg_revert(vg);
+                       return 0;
+               }
+
+               if (!vg_commit(vg))
+                       return_0;
+
+               if (!resume_lv(cmd, org)) {
+                       log_error("Problem reactivating origin %s", org->name);
+                       return 0;
+               }
+       }
+       /* FIXME out of sequence */
+       backup(vg);
+
+       log_print("Logical volume \"%s\" created", lv->name);
+
+       /*
+        * FIXME: as a sanity check we could try reading the
+        * last block of the device ?
+        */
+
+       return 1;
+
+deactivate_and_revert_new_lv:
+       if (!deactivate_lv(cmd, lv)) {
+               log_error("Unable to deactivate failed new LV. "
+                         "Manual intervention required.");
+               return 0;
+       }
+
+revert_new_lv:
+       /* FIXME Better to revert to backup of metadata? */
+       if (!lv_remove(lv) || !vg_write(vg) || !vg_commit(vg))
+               log_error("Manual intervention may be required to remove "
+                         "abandoned LV(s) before retrying.");
+       else
+               backup(vg);
+
+       return 0;
+}
+
diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c
new file mode 100644 (file)
index 0000000..3b8895c
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "toolcontext.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "str_list.h"
+#include "segtype.h"
+
+/*
+ * Attempt to merge two adjacent segments.
+ * Currently only supports striped segments on AREA_PV.
+ * Returns success if successful, in which case 'first'
+ * gets adjusted to contain both areas.
+ */
+static int _merge(struct lv_segment *first, struct lv_segment *second)
+{
+       if (!first || !second || first->segtype != second->segtype ||
+           !first->segtype->ops->merge_segments)
+               return 0;
+
+       return first->segtype->ops->merge_segments(first, second);
+}
+
+int lv_merge_segments(struct logical_volume *lv)
+{
+       struct dm_list *segh, *t;
+       struct lv_segment *current, *prev = NULL;
+
+       if (lv->status & LOCKED || lv->status & PVMOVE)
+               return 1;
+
+       dm_list_iterate_safe(segh, t, &lv->segments) {
+               current = dm_list_item(segh, struct lv_segment);
+
+               if (_merge(prev, current))
+                       dm_list_del(&current->list);
+               else
+                       prev = current;
+       }
+
+       return 1;
+}
+
+#define ERROR_MAX 100
+#define inc_error_count \
+       if (error_count++ > ERROR_MAX)  \
+               goto out
+
+/*
+ * Verify that an LV's segments are consecutive, complete and don't overlap.
+ */
+int check_lv_segments(struct logical_volume *lv, int complete_vg)
+{
+       struct lv_segment *seg, *seg2;
+       uint32_t le = 0;
+       unsigned seg_count = 0, seg_found;
+       uint32_t area_multiplier, s;
+       struct seg_list *sl;
+       int error_count = 0;
+       struct replicator_site *rsite;
+       struct replicator_device *rdev;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               seg_count++;
+               if (seg->le != le) {
+                       log_error("LV %s invalid: segment %u should begin at "
+                                 "LE %" PRIu32 " (found %" PRIu32 ").",
+                                 lv->name, seg_count, le, seg->le);
+                       inc_error_count;
+               }
+
+               area_multiplier = segtype_is_striped(seg->segtype) ?
+                                       seg->area_count : 1;
+
+               if (seg->area_len * area_multiplier != seg->len) {
+                       log_error("LV %s: segment %u has inconsistent "
+                                 "area_len %u",
+                                 lv->name, seg_count, seg->area_len);
+                       inc_error_count;
+               }
+
+               if (complete_vg && seg->log_lv) {
+                       if (!seg_is_mirrored(seg)) {
+                               log_error("LV %s: segment %u has log LV but "
+                                         "is not mirrored",
+                                         lv->name, seg_count);
+                               inc_error_count;
+                       }
+
+                       if (!(seg->log_lv->status & MIRROR_LOG)) {
+                               log_error("LV %s: segment %u log LV %s is not "
+                                         "a mirror log",
+                                          lv->name, seg_count, seg->log_lv->name);
+                               inc_error_count;
+                       }
+
+                       if (!(seg2 = first_seg(seg->log_lv)) ||
+                           find_mirror_seg(seg2) != seg) {
+                               log_error("LV %s: segment %u log LV does not "
+                                         "point back to mirror segment",
+                                          lv->name, seg_count);
+                               inc_error_count;
+                       }
+               }
+
+               if (complete_vg && seg->status & MIRROR_IMAGE) {
+                       if (!find_mirror_seg(seg) ||
+                           !seg_is_mirrored(find_mirror_seg(seg))) {
+                               log_error("LV %s: segment %u mirror image "
+                                         "is not mirrored",
+                                         lv->name, seg_count);
+                               inc_error_count;
+                       }
+               }
+
+               if (seg_is_snapshot(seg)) {
+                       if (seg->cow && seg->cow == seg->origin) {
+                               log_error("LV %s: segment %u has same LV %s for "
+                                         "both origin and snapshot",
+                                         lv->name, seg_count, seg->cow->name);
+                               inc_error_count;
+                       }
+               }
+
+               if (seg_is_replicator(seg) && !check_replicator_segment(seg))
+                       inc_error_count;
+
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) == AREA_UNASSIGNED) {
+                               log_error("LV %s: segment %u has unassigned "
+                                         "area %u.",
+                                         lv->name, seg_count, s);
+                               inc_error_count;
+                       } else if (seg_type(seg, s) == AREA_PV) {
+                               if (!seg_pvseg(seg, s) ||
+                                   seg_pvseg(seg, s)->lvseg != seg ||
+                                   seg_pvseg(seg, s)->lv_area != s) {
+                                       log_error("LV %s: segment %u has "
+                                                 "inconsistent PV area %u",
+                                                 lv->name, seg_count, s);
+                                       inc_error_count;
+                               }
+                       } else {
+                               if (!seg_lv(seg, s) ||
+                                   seg_lv(seg, s)->vg != lv->vg ||
+                                   seg_lv(seg, s) == lv) {
+                                       log_error("LV %s: segment %u has "
+                                                 "inconsistent LV area %u",
+                                                 lv->name, seg_count, s);
+                                       inc_error_count;
+                               }
+
+                               if (complete_vg && seg_lv(seg, s) &&
+                                   (seg_lv(seg, s)->status & MIRROR_IMAGE) &&
+                                   (!(seg2 = find_seg_by_le(seg_lv(seg, s),
+                                                           seg_le(seg, s))) ||
+                                    find_mirror_seg(seg2) != seg)) {
+                                       log_error("LV %s: segment %u mirror "
+                                                 "image %u missing mirror ptr",
+                                                 lv->name, seg_count, s);
+                                       inc_error_count;
+                               }
+
+/* FIXME I don't think this ever holds?
+                               if (seg_le(seg, s) != le) {
+                                       log_error("LV %s: segment %u has "
+                                                 "inconsistent LV area %u "
+                                                 "size",
+                                                 lv->name, seg_count, s);
+                                       inc_error_count;
+                               }
+ */
+                               seg_found = 0;
+                               dm_list_iterate_items(sl, &seg_lv(seg, s)->segs_using_this_lv)
+                                       if (sl->seg == seg)
+                                               seg_found++;
+                               if (!seg_found) {
+                                       log_error("LV %s segment %d uses LV %s,"
+                                                 " but missing ptr from %s to %s",
+                                                 lv->name, seg_count,
+                                                 seg_lv(seg, s)->name,
+                                                 seg_lv(seg, s)->name, lv->name);
+                                       inc_error_count;
+                               } else if (seg_found > 1) {
+                                       log_error("LV %s has duplicated links "
+                                                 "to LV %s segment %d",
+                                                 seg_lv(seg, s)->name,
+                                                 lv->name, seg_count);
+                                       inc_error_count;
+                               }
+                       }
+               }
+
+               le += seg->len;
+       }
+
+       dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+               seg = sl->seg;
+               seg_found = 0;
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_LV)
+                               continue;
+                       if (lv == seg_lv(seg, s))
+                               seg_found++;
+               }
+               if (seg_is_replicator_dev(seg)) {
+                       dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+                               dm_list_iterate_items(rdev, &rsite->rdevices) {
+                                       if (lv == rdev->lv || lv == rdev->slog)
+                                               seg_found++;
+                               }
+                       }
+                       if (lv == seg->replicator)
+                               seg_found++;
+               }
+               if (seg_is_replicator(seg) && lv == seg->rlog_lv)
+                               seg_found++;
+               if (seg->log_lv == lv)
+                       seg_found++;
+               if (!seg_found) {
+                       log_error("LV %s is used by LV %s:%" PRIu32 "-%" PRIu32
+                                 ", but missing ptr from %s to %s",
+                                 lv->name, seg->lv->name, seg->le,
+                                 seg->le + seg->len - 1,
+                                 seg->lv->name, lv->name);
+                       inc_error_count;
+               } else if (seg_found != sl->count) {
+                       log_error("Reference count mismatch: LV %s has %d "
+                                 "links to LV %s:%" PRIu32 "-%" PRIu32
+                                 ", which has %d links",
+                                 lv->name, sl->count, seg->lv->name, seg->le,
+                                 seg->le + seg->len - 1, seg_found);
+                       inc_error_count;
+               }
+
+               seg_found = 0;
+               dm_list_iterate_items(seg2, &seg->lv->segments)
+                       if (sl->seg == seg2) {
+                               seg_found++;
+                               break;
+                       }
+               if (!seg_found) {
+                       log_error("LV segment %s:%" PRIu32 "-%" PRIu32
+                                 " is incorrectly listed as being used by LV %s",
+                                 seg->lv->name, seg->le, seg->le + seg->len - 1,
+                                 lv->name);
+                       inc_error_count;
+               }
+       }
+
+       if (le != lv->le_count) {
+               log_error("LV %s: inconsistent LE count %u != %u",
+                         lv->name, le, lv->le_count);
+               inc_error_count;
+       }
+
+out:
+       return !error_count;
+}
+
+/*
+ * Split the supplied segment at the supplied logical extent
+ * NB Use LE numbering that works across stripes PV1: 0,2,4 PV2: 1,3,5 etc.
+ */
+static int _lv_split_segment(struct logical_volume *lv, struct lv_segment *seg,
+                            uint32_t le)
+{
+       struct lv_segment *split_seg;
+       uint32_t s;
+       uint32_t offset = le - seg->le;
+       uint32_t area_offset;
+
+       if (!seg_can_split(seg)) {
+               log_error("Unable to split the %s segment at LE %" PRIu32
+                         " in LV %s", seg->segtype->name, le, lv->name);
+               return 0;
+       }
+
+       /* Clone the existing segment */
+       if (!(split_seg = alloc_lv_segment(lv->vg->vgmem, seg->segtype,
+                                          seg->lv, seg->le, seg->len,
+                                          seg->status, seg->stripe_size,
+                                          seg->log_lv,
+                                          seg->area_count, seg->area_len,
+                                          seg->chunk_size, seg->region_size,
+                                          seg->extents_copied, seg->pvmove_source_seg))) {
+               log_error("Couldn't allocate cloned LV segment.");
+               return 0;
+       }
+
+       if (!str_list_dup(lv->vg->vgmem, &split_seg->tags, &seg->tags)) {
+               log_error("LV segment tags duplication failed");
+               return 0;
+       }
+
+       /* In case of a striped segment, the offset has to be / stripes */
+       area_offset = offset;
+       if (seg_is_striped(seg))
+               area_offset /= seg->area_count;
+
+       split_seg->area_len -= area_offset;
+       seg->area_len = area_offset;
+
+       split_seg->len -= offset;
+       seg->len = offset;
+
+       split_seg->le = seg->le + seg->len;
+
+       /* Adjust the PV mapping */
+       for (s = 0; s < seg->area_count; s++) {
+               seg_type(split_seg, s) = seg_type(seg, s);
+
+               /* Split area at the offset */
+               switch (seg_type(seg, s)) {
+               case AREA_LV:
+                       if (!set_lv_segment_area_lv(split_seg, s, seg_lv(seg, s),
+                                                   seg_le(seg, s) + seg->area_len, 0))
+                               return_0;
+                       log_debug("Split %s:%u[%u] at %u: %s LE %u", lv->name,
+                                 seg->le, s, le, seg_lv(seg, s)->name,
+                                 seg_le(split_seg, s));
+                       break;
+
+               case AREA_PV:
+                       if (!(seg_pvseg(split_seg, s) =
+                            assign_peg_to_lvseg(seg_pv(seg, s),
+                                                seg_pe(seg, s) +
+                                                    seg->area_len,
+                                                seg_pvseg(seg, s)->len -
+                                                    seg->area_len,
+                                                split_seg, s)))
+                               return_0;
+                       log_debug("Split %s:%u[%u] at %u: %s PE %u", lv->name,
+                                 seg->le, s, le,
+                                 dev_name(seg_dev(seg, s)),
+                                 seg_pe(split_seg, s));
+                       break;
+
+               case AREA_UNASSIGNED:
+                       log_error("Unassigned area %u found in segment", s);
+                       return 0;
+               }
+       }
+
+       /* Add split off segment to the list _after_ the original one */
+       dm_list_add_h(&seg->list, &split_seg->list);
+
+       return 1;
+}
+
+/*
+ * Ensure there's a segment boundary at the given logical extent
+ */
+int lv_split_segment(struct logical_volume *lv, uint32_t le)
+{
+       struct lv_segment *seg;
+
+       if (!(seg = find_seg_by_le(lv, le))) {
+               log_error("Segment with extent %" PRIu32 " in LV %s not found",
+                         le, lv->name);
+               return 0;
+       }
+
+       /* This is a segment start already */
+       if (le == seg->le)
+               return 1;
+
+       if (!_lv_split_segment(lv, seg, le))
+               return_0;
+
+       if (!vg_validate(lv->vg))
+               return_0;
+
+       return 1;
+}
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
new file mode 100644 (file)
index 0000000..6994e0c
--- /dev/null
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This is the representation of LVM metadata that is being adapted
+ * for library export.
+ */
+
+#ifndef _LVM_METADATA_EXPORTED_H
+#define _LVM_METADATA_EXPORTED_H
+
+#include "uuid.h"
+#include "pv.h"
+#include "vg.h"
+#include "lv.h"
+#include "lvm-percent.h"
+
+#define MAX_STRIPES 128U
+#define SECTOR_SHIFT 9L
+#define SECTOR_SIZE ( 1L << SECTOR_SHIFT )
+#define STRIPE_SIZE_MIN ( (unsigned) lvm_getpagesize() >> SECTOR_SHIFT)        /* PAGESIZE in sectors */
+#define STRIPE_SIZE_MAX ( 512L * 1024L >> SECTOR_SHIFT)        /* 512 KB in sectors */
+#define STRIPE_SIZE_LIMIT ((UINT_MAX >> 2) + 1)
+#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT)    /* 512 KB in sectors */
+#define MAX_RESTRICTED_LVS 255 /* Used by FMT_RESTRICTED_LVIDS */
+
+/* Layer suffix */
+#define MIRROR_SYNC_LAYER "_mimagetmp"
+
+/* Various flags */
+/* Note that the bits no longer necessarily correspond to LVM1 disk format */
+
+#define PARTIAL_VG             0x00000001U     /* VG */
+#define EXPORTED_VG            0x00000002U     /* VG PV */
+#define RESIZEABLE_VG          0x00000004U     /* VG */
+
+/* May any free extents on this PV be used or must they be left free? */
+#define ALLOCATABLE_PV                 0x00000008U     /* PV */
+
+//#define SPINDOWN_LV                  0x00000010U     /* LV */
+//#define BADBLOCK_ON          0x00000020U     /* LV */
+#define VISIBLE_LV             0x00000040U     /* LV */
+#define FIXED_MINOR            0x00000080U     /* LV */
+/* FIXME Remove when metadata restructuring is completed */
+#define SNAPSHOT               0x00001000U     /* LV - internal use only */
+#define PVMOVE                 0x00002000U     /* VG LV SEG */
+#define LOCKED                 0x00004000U     /* LV */
+#define MIRRORED               0x00008000U     /* LV - internal use only */
+//#define VIRTUAL                      0x00010000U     /* LV - internal use only */
+#define MIRROR_LOG             0x00020000U     /* LV */
+#define MIRROR_IMAGE           0x00040000U     /* LV */
+#define MIRROR_NOTSYNCED       0x00080000U     /* LV */
+//#define ACTIVATE_EXCL                0x00100000U     /* LV - internal use only */
+//#define PRECOMMITTED         0x00200000U     /* VG - internal use only */
+#define CONVERTING             0x00400000U     /* LV */
+
+#define MISSING_PV              0x00800000U    /* PV */
+#define PARTIAL_LV              0x01000000U    /* LV - derived flag, not
+                                                  written out in metadata*/
+
+//#define POSTORDER_FLAG       0x02000000U /* Not real flags, reserved for
+//#define POSTORDER_OPEN_FLAG  0x04000000U    temporary use inside vg_read_internal. */
+//#define VIRTUAL_ORIGIN       0x08000000U     /* LV - internal use only */
+
+#define MERGING                        0x10000000U     /* LV SEG */
+
+#define REPLICATOR             0x20000000U     /* LV -internal use only for replicator */
+#define REPLICATOR_LOG         0x40000000U     /* LV -internal use only for replicator-dev */
+
+#define LVM_READ               0x00000100U     /* LV VG */
+#define LVM_WRITE              0x00000200U     /* LV VG */
+#define CLUSTERED              0x00000400U     /* VG */
+//#define SHARED               0x00000800U     /* VG */
+
+/* Format features flags */
+#define FMT_SEGMENTS           0x00000001U     /* Arbitrary segment params? */
+#define FMT_MDAS               0x00000002U     /* Proper metadata areas? */
+#define FMT_TAGS               0x00000004U     /* Tagging? */
+#define FMT_UNLIMITED_VOLS     0x00000008U     /* Unlimited PVs/LVs? */
+#define FMT_RESTRICTED_LVIDS   0x00000010U     /* LVID <= 255 */
+#define FMT_ORPHAN_ALLOCATABLE 0x00000020U     /* Orphan PV allocatable? */
+//#define FMT_PRECOMMIT                0x00000040U     /* Supports pre-commit? */
+#define FMT_RESIZE_PV          0x00000080U     /* Supports pvresize? */
+#define FMT_UNLIMITED_STRIPESIZE 0x00000100U   /* Unlimited stripe size? */
+#define FMT_RESTRICTED_READAHEAD 0x00000200U   /* Readahead restricted to 2-120? */
+
+/* Mirror conversion type flags */
+#define MIRROR_BY_SEG          0x00000001U     /* segment-by-segment mirror */
+#define MIRROR_BY_LV           0x00000002U     /* mirror using whole mimage LVs */
+#define MIRROR_SKIP_INIT_SYNC  0x00000010U     /* skip initial sync */
+
+/* vg_read and vg_read_for_update flags */
+#define READ_ALLOW_INCONSISTENT        0x00010000U
+#define READ_ALLOW_EXPORTED    0x00020000U
+#define READ_WITHOUT_LOCK       0x00040000U
+
+/* A meta-flag, useful with toollib for_each_* functions. */
+#define READ_FOR_UPDATE        0x00100000U
+
+/* vg's "read_status" field */
+#define FAILED_INCONSISTENT    0x00000001U
+#define FAILED_LOCKING         0x00000002U
+#define FAILED_NOTFOUND                0x00000004U
+#define FAILED_READ_ONLY       0x00000008U
+#define FAILED_EXPORTED                0x00000010U
+#define FAILED_RESIZEABLE      0x00000020U
+#define FAILED_CLUSTERED       0x00000040U
+#define FAILED_ALLOCATION      0x00000080U
+#define FAILED_EXIST           0x00000100U
+#define SUCCESS                        0x00000000U
+
+#define VGMETADATACOPIES_ALL UINT32_MAX
+#define VGMETADATACOPIES_UNMANAGED 0
+
+/* Ordered list - see lv_manip.c */
+typedef enum {
+       AREA_UNASSIGNED,
+       AREA_PV,
+       AREA_LV
+} area_type_t;
+
+/*
+ * Whether or not to force an operation.
+ */
+typedef enum {
+       PROMPT = 0, /* Issue yes/no prompt to confirm operation */
+       DONT_PROMPT = 1, /* Skip yes/no prompt */
+       DONT_PROMPT_OVERRIDE = 2 /* Skip prompt + override a second condition */
+} force_t;
+
+struct cmd_context;
+struct format_handler;
+struct labeller;
+
+struct format_type {
+       struct dm_list list;
+       struct cmd_context *cmd;
+       struct format_handler *ops;
+       struct labeller *labeller;
+       const char *name;
+       const char *alias;
+       const char *orphan_vg_name;
+       uint32_t features;
+       void *library;
+       void *private;
+};
+
+struct pv_segment {
+       struct dm_list list;    /* Member of pv->segments: ordered list
+                                * covering entire data area on this PV */
+
+       struct physical_volume *pv;
+       uint32_t pe;
+       uint32_t len;
+
+       struct lv_segment *lvseg;       /* NULL if free space */
+       uint32_t lv_area;       /* Index to area in LV segment */
+};
+
+#define pvseg_is_allocated(pvseg) ((pvseg)->lvseg)
+
+struct format_instance {
+       const struct format_type *fmt;
+       /*
+        * Each mda in a vg is on exactly one of the below lists.
+        * MDAs on the 'in_use' list will be read from / written to
+        * disk, while MDAs on the 'ignored' list will not be read
+        * or written to.
+        */
+       struct dm_list metadata_areas_in_use;
+       struct dm_list metadata_areas_ignored;
+       void *private;
+};
+
+/* There will be one area for each stripe */
+struct lv_segment_area {
+       area_type_t type;
+       union {
+               struct {
+                       struct pv_segment *pvseg;
+               } pv;
+               struct {
+                       struct logical_volume *lv;
+                       uint32_t le;
+               } lv;
+       } u;
+};
+
+struct segment_type;
+
+/* List with vg_name, vgid and flags */
+struct cmd_vg {
+       struct dm_list list;
+       const char *vg_name;
+       const char *vgid;
+       uint32_t flags;
+       struct volume_group *vg;
+};
+
+/* ++ Replicator datatypes */
+typedef enum {
+       REPLICATOR_STATE_PASSIVE,
+       REPLICATOR_STATE_ACTIVE,
+       NUM_REPLICATOR_STATE
+} replicator_state_t;
+
+struct replicator_site {
+       struct dm_list list;            /* Chained list of sites */
+       struct dm_list rdevices;        /* Device list */
+
+       struct logical_volume *replicator; /* Reference to replicator */
+
+       const char *name;               /* Site name */
+       const char *vg_name;            /* VG name */
+       struct volume_group *vg;        /* resolved vg  (activate/deactive) */
+       unsigned site_index;
+       replicator_state_t state;       /* Active or pasive state of site */
+       dm_replicator_mode_t op_mode;   /* Operation mode sync or async fail|warn|drop|stall */
+       uint64_t fall_behind_data;      /* Bytes */
+       uint32_t fall_behind_ios;       /* IO operations */
+       uint32_t fall_behind_timeout;   /* Seconds */
+};
+
+struct replicator_device {
+       struct dm_list list;            /* Chained list of devices from same site */
+
+       struct lv_segment *replicator_dev; /* Reference to replicator-dev segment */
+       struct replicator_site *rsite;  /* Reference to site parameters */
+
+       uint64_t device_index;
+       const char *name;               /* Device LV name */
+       struct logical_volume *lv;      /* LV from replicator site's VG */
+       struct logical_volume *slog;    /* Synclog lv from VG  */
+       const char *slog_name;          /* Debug - specify size of core synclog */
+};
+/* -- Replicator datatypes */
+
+struct lv_segment {
+       struct dm_list list;
+       struct logical_volume *lv;
+
+       const struct segment_type *segtype;
+       uint32_t le;
+       uint32_t len;
+
+       uint64_t status;
+
+       /* FIXME Fields depend on segment type */
+       uint32_t stripe_size;
+       uint32_t area_count;
+       uint32_t area_len;
+       uint32_t chunk_size;    /* For snapshots - in sectors */
+       struct logical_volume *origin;
+       struct logical_volume *cow;
+       struct dm_list origin_list;
+       uint32_t region_size;   /* For mirrors, replicators - in sectors */
+       uint32_t extents_copied;
+       struct logical_volume *log_lv;
+       struct lv_segment *pvmove_source_seg;
+       void *segtype_private;
+
+       struct dm_list tags;
+
+       struct lv_segment_area *areas;
+
+       struct logical_volume *replicator;/* For replicator-devs - link to replicator LV */
+       struct logical_volume *rlog_lv; /* For replicators */
+       const char *rlog_type;          /* For replicators */
+       uint64_t rdevice_index_highest; /* For replicators */
+       unsigned rsite_index_highest;   /* For replicators */
+};
+
+#define seg_type(seg, s)       (seg)->areas[(s)].type
+#define seg_pv(seg, s)         (seg)->areas[(s)].u.pv.pvseg->pv
+#define seg_lv(seg, s)         (seg)->areas[(s)].u.lv.lv
+
+struct pe_range {
+       struct dm_list list;
+       uint32_t start;         /* PEs */
+       uint32_t count;         /* PEs */
+};
+
+struct pv_list {
+       struct dm_list list;
+       struct physical_volume *pv;
+       struct dm_list *mdas;   /* Metadata areas */
+       struct dm_list *pe_ranges;      /* Ranges of PEs e.g. for allocation */
+};
+
+struct lv_list {
+       struct dm_list list;
+       struct logical_volume *lv;
+};
+
+struct pvcreate_params {
+       int zero;
+       uint64_t size;
+       uint64_t data_alignment;
+       uint64_t data_alignment_offset;
+       int pvmetadatacopies;
+       uint64_t pvmetadatasize;
+       int64_t labelsector;
+       struct id id; /* FIXME: redundant */
+       struct id *idp; /* 0 if no --uuid option */
+       uint64_t pe_start;
+       uint32_t extent_count;
+       uint32_t extent_size;
+       const char *restorefile; /* 0 if no --restorefile option */
+       force_t force;
+       unsigned yes;
+       unsigned metadataignore;
+};
+
+struct physical_volume *pvcreate_single(struct cmd_context *cmd,
+                                       const char *pv_name,
+                                       struct pvcreate_params *pp);
+void pvcreate_params_set_defaults(struct pvcreate_params *pp);
+
+/*
+* Utility functions
+*/
+int vg_write(struct volume_group *vg);
+int vg_commit(struct volume_group *vg);
+int vg_revert(struct volume_group *vg);
+struct volume_group *vg_read_internal(struct cmd_context *cmd, const char *vg_name,
+                            const char *vgid, int warnings, int *consistent);
+struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
+                               struct dm_list *mdas, uint64_t *label_sector,
+                               int warnings, int scan_label_only);
+struct dm_list *get_pvs(struct cmd_context *cmd);
+
+/*
+ * Add/remove LV to/from volume group
+ */
+int link_lv_to_vg(struct volume_group *vg, struct logical_volume *lv);
+int unlink_lv_from_vg(struct logical_volume *lv);
+void lv_set_visible(struct logical_volume *lv);
+void lv_set_hidden(struct logical_volume *lv);
+
+struct dm_list *get_vgnames(struct cmd_context *cmd, int include_internal);
+struct dm_list *get_vgids(struct cmd_context *cmd, int include_internal);
+int scan_vgs_for_pvs(struct cmd_context *cmd, int warnings);
+
+int pv_write(struct cmd_context *cmd, struct physical_volume *pv,
+            struct dm_list *mdas, int64_t label_sector);
+int move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
+           const char *pv_name);
+int move_pvs_used_by_lv(struct volume_group *vg_from,
+                       struct volume_group *vg_to,
+                       const char *lv_name);
+int is_global_vg(const char *vg_name);
+int is_orphan_vg(const char *vg_name);
+int vg_missing_pv_count(const struct volume_group *vg);
+int vgs_are_compatible(struct cmd_context *cmd,
+                      struct volume_group *vg_from,
+                      struct volume_group *vg_to);
+uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname);
+
+/*
+ * Return a handle to VG metadata.
+ */
+struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
+              const char *vgid, uint32_t flags);
+struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
+                         const char *vgid, uint32_t flags);
+
+/* 
+ * Test validity of a VG handle.
+ */
+uint32_t vg_read_error(struct volume_group *vg_handle);
+
+/* pe_start and pe_end relate to any existing data so that new metadata
+* areas can avoid overlap */
+struct physical_volume *pv_create(const struct cmd_context *cmd,
+                                 struct device *dev,
+                                 struct id *id,
+                                 uint64_t size,
+                                 unsigned long data_alignment,
+                                 unsigned long data_alignment_offset,
+                                 uint64_t pe_start,
+                                 uint32_t existing_extent_count,
+                                 uint32_t existing_extent_size,
+                                 int pvmetadatacopies, uint64_t pvmetadatasize,
+                                 unsigned metadataignore,
+                                 struct dm_list *mdas);
+int pv_resize(struct physical_volume *pv, struct volume_group *vg,
+             uint32_t new_pe_count);
+int pv_analyze(struct cmd_context *cmd, const char *pv_name,
+              uint64_t label_sector);
+
+/* FIXME: move internal to library */
+uint32_t pv_list_extents_free(const struct dm_list *pvh);
+
+struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name);
+int vg_remove_mdas(struct volume_group *vg);
+int vg_remove_check(struct volume_group *vg);
+void vg_remove_pvs(struct volume_group *vg);
+int vg_remove(struct volume_group *vg);
+int vg_rename(struct cmd_context *cmd, struct volume_group *vg,
+             const char *new_name);
+int vg_extend(struct volume_group *vg, int pv_count, char **pv_names,
+             struct pvcreate_params *pp);
+int vg_reduce(struct volume_group *vg, char *pv_name);
+int vg_change_tag(struct volume_group *vg, const char *tag, int add_tag);
+int vg_split_mdas(struct cmd_context *cmd, struct volume_group *vg_from,
+                 struct volume_group *vg_to);
+/* FIXME: Investigate refactoring these functions to take a pv ISO pv_list */
+void add_pvl_to_vgs(struct volume_group *vg, struct pv_list *pvl);
+void del_pvl_from_vgs(struct volume_group *vg, struct pv_list *pvl);
+
+/* FIXME: refactor / unexport when lvremove liblvm refactoring dones */
+int remove_lvs_in_vg(struct cmd_context *cmd,
+                    struct volume_group *vg,
+                    force_t force);
+/*
+ * free_vg() must be called on every struct volume_group allocated
+ * by vg_create() or vg_read_internal() to free it when no longer required.
+ */
+void free_vg(struct volume_group *vg);
+
+/* Manipulate LVs */
+struct logical_volume *lv_create_empty(const char *name,
+                                      union lvid *lvid,
+                                      uint64_t status,
+                                      alloc_policy_t alloc,
+                                      struct volume_group *vg);
+
+/* Write out LV contents */
+int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
+           uint64_t sectors, int value);
+
+int lv_change_tag(struct logical_volume *lv, const char *tag, int add_tag);
+
+/* Reduce the size of an LV by extents */
+int lv_reduce(struct logical_volume *lv, uint32_t extents);
+
+/* Empty an LV prior to deleting it */
+int lv_empty(struct logical_volume *lv);
+
+/* Empty an LV and add error segment */
+int replace_lv_with_error_segment(struct logical_volume *lv);
+
+/* Entry point for all LV extent allocations */
+int lv_extend(struct logical_volume *lv,
+             const struct segment_type *segtype,
+             uint32_t stripes, uint32_t stripe_size,
+             uint32_t mirrors, uint32_t extents,
+             struct physical_volume *mirrored_pv, uint32_t mirrored_pe,
+             uint64_t status, struct dm_list *allocatable_pvs,
+             alloc_policy_t alloc);
+
+/* lv must be part of lv->vg->lvs */
+int lv_remove(struct logical_volume *lv);
+
+int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
+                    force_t force);
+
+int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume *lv,
+                               force_t force, unsigned level);
+
+int lv_rename(struct cmd_context *cmd, struct logical_volume *lv,
+             const char *new_name);
+
+uint64_t extents_from_size(struct cmd_context *cmd, uint64_t size,
+                          uint32_t extent_size);
+
+/* FIXME: refactor and reduce the size of this struct! */
+struct lvcreate_params {
+       /* flags */
+       int snapshot; /* snap */
+       int zero; /* all */
+       int major; /* all */
+       int minor; /* all */
+       int log_count; /* mirror */
+       int nosync; /* mirror */
+       int activation_monitoring; /* all */
+
+       char *origin; /* snap */
+       const char *vg_name; /* all */
+       const char *lv_name; /* all */
+
+       uint32_t stripes; /* striped */
+       uint32_t stripe_size; /* striped */
+       uint32_t chunk_size; /* snapshot */
+       uint32_t region_size; /* mirror */
+
+       uint32_t mirrors; /* mirror */
+
+       const struct segment_type *segtype; /* all */
+
+       /* size */
+       uint32_t extents; /* all */
+       uint32_t voriginextents; /* snapshot */
+       uint64_t voriginsize; /* snapshot */
+       struct dm_list *pvh; /* all */
+
+       uint32_t permission; /* all */
+       uint32_t read_ahead; /* all */
+       alloc_policy_t alloc; /* all */
+
+       struct dm_list tags;    /* all */
+};
+
+int lv_create_single(struct volume_group *vg,
+                    struct lvcreate_params *lp);
+
+/*
+ * Functions for layer manipulation
+ */
+int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
+                                   struct logical_volume *lv_where,
+                                   struct logical_volume *layer_lv,
+                                   uint64_t status,
+                                   struct pv_list *pv,
+                                   struct dm_list *lvs_changed);
+int remove_layers_for_segments(struct cmd_context *cmd,
+                              struct logical_volume *lv,
+                              struct logical_volume *layer_lv,
+                              uint64_t status_mask, struct dm_list *lvs_changed);
+int remove_layers_for_segments_all(struct cmd_context *cmd,
+                                  struct logical_volume *layer_lv,
+                                  uint64_t status_mask,
+                                  struct dm_list *lvs_changed);
+int split_parent_segments_for_layer(struct cmd_context *cmd,
+                                   struct logical_volume *layer_lv);
+int remove_layer_from_lv(struct logical_volume *lv,
+                        struct logical_volume *layer_lv);
+struct logical_volume *insert_layer_for_lv(struct cmd_context *cmd,
+                                          struct logical_volume *lv_where,
+                                          uint64_t status,
+                                          const char *layer_suffix);
+
+/* Find a PV within a given VG */
+struct pv_list *find_pv_in_vg(const struct volume_group *vg,
+                             const char *pv_name);
+struct pv_list *find_pv_in_vg_by_uuid(const struct volume_group *vg,
+                                     const struct id *id);
+
+/* Find an LV within a given VG */
+struct lv_list *find_lv_in_vg(const struct volume_group *vg,
+                             const char *lv_name);
+
+/* FIXME Merge these functions with ones above */
+struct logical_volume *find_lv(const struct volume_group *vg,
+                              const char *lv_name);
+struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
+                                       const char *pv_name);
+
+const char *find_vgname_from_pvname(struct cmd_context *cmd,
+                                   const char *pvname);
+const char *find_vgname_from_pvid(struct cmd_context *cmd,
+                                 const char *pvid);
+/* Find LV segment containing given LE */
+struct lv_segment *first_seg(const struct logical_volume *lv);
+
+
+/*
+* Useful functions for managing snapshots.
+*/
+int lv_is_origin(const struct logical_volume *lv);
+int lv_is_virtual_origin(const struct logical_volume *lv);
+int lv_is_cow(const struct logical_volume *lv);
+int lv_is_merging_origin(const struct logical_volume *origin);
+int lv_is_merging_cow(const struct logical_volume *snapshot);
+
+/* Test if given LV is visible from user's perspective */
+int lv_is_visible(const struct logical_volume *lv);
+
+int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv);
+
+struct lv_segment *find_merging_cow(const struct logical_volume *origin);
+
+/* Given a cow LV, return return the snapshot lv_segment that uses it */
+struct lv_segment *find_cow(const struct logical_volume *lv);
+
+/* Given a cow LV, return its origin */
+struct logical_volume *origin_from_cow(const struct logical_volume *lv);
+
+void init_snapshot_seg(struct lv_segment *seg, struct logical_volume *origin,
+                      struct logical_volume *cow, uint32_t chunk_size, int merge);
+
+void init_snapshot_merge(struct lv_segment *cow_seg, struct logical_volume *origin);
+
+void clear_snapshot_merge(struct logical_volume *origin);
+
+int vg_add_snapshot(struct logical_volume *origin, struct logical_volume *cow,
+                   union lvid *lvid, uint32_t extent_count,
+                   uint32_t chunk_size);
+
+int vg_remove_snapshot(struct logical_volume *cow);
+
+int vg_check_status(const struct volume_group *vg, uint64_t status);
+
+
+/*
+ * Check if the VG reached maximal LVs count (if set)
+ */
+int vg_max_lv_reached(struct volume_group *vg);
+
+/*
+* Mirroring functions
+*/
+struct lv_segment *find_mirror_seg(struct lv_segment *seg);
+int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
+                  uint32_t mirrors, uint32_t stripes, uint32_t stripe_size,
+                  uint32_t region_size, uint32_t log_count,
+                  struct dm_list *pvs, alloc_policy_t alloc, uint32_t flags);
+int lv_split_mirror_images(struct logical_volume *lv, const char *split_lv_name,
+                          uint32_t split_count, struct dm_list *removable_pvs);
+int lv_remove_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
+                     uint32_t mirrors, uint32_t log_count,
+                     int (*is_removable)(struct logical_volume *, void *),
+                     void *removable_baton, uint64_t status_mask);
+
+int is_temporary_mirror_layer(const struct logical_volume *lv);
+struct logical_volume * find_temporary_mirror(const struct logical_volume *lv);
+int lv_is_mirrored(const struct logical_volume *lv);
+uint32_t lv_mirror_count(const struct logical_volume *lv);
+uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
+                                    uint32_t region_size);
+int remove_mirrors_from_segments(struct logical_volume *lv,
+                                uint32_t new_mirrors, uint64_t status_mask);
+int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv,
+                           uint32_t mirrors, uint32_t region_size,
+                           struct dm_list *allocatable_pvs, alloc_policy_t alloc);
+
+int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors,
+                        int (*is_removable)(struct logical_volume *, void *),
+                        void *removable_baton, unsigned remove_log);
+int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
+                     uint32_t mirrors, uint32_t stripes, uint32_t stripe_size, uint32_t region_size,
+                     struct dm_list *allocatable_pvs, alloc_policy_t alloc,
+                     uint32_t log_count);
+struct logical_volume *detach_mirror_log(struct lv_segment *seg);
+int attach_mirror_log(struct lv_segment *seg, struct logical_volume *lv);
+int remove_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+                     struct dm_list *removable_pvs, int force);
+int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+                  uint32_t log_count, uint32_t region_size,
+                  struct dm_list *allocatable_pvs, alloc_policy_t alloc);
+
+int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
+                             struct dm_list *removable_pvs, unsigned remove_log);
+int collapse_mirrored_lv(struct logical_volume *lv);
+int shift_mirror_images(struct lv_segment *mirrored_seg, unsigned mimage);
+
+/* ++  metadata/replicator_manip.c */
+int replicator_add_replicator_dev(struct logical_volume *replicator_lv,
+                                 struct lv_segment *rdev_seg);
+struct logical_volume *replicator_remove_replicator_dev(struct lv_segment *rdev_seg);
+int replicator_add_rlog(struct lv_segment *replicator_seg, struct logical_volume *rlog_lv);
+struct logical_volume *replicator_remove_rlog(struct lv_segment *replicator_seg);
+
+int replicator_dev_add_slog(struct replicator_device *rdev, struct logical_volume *slog_lv);
+struct logical_volume *replicator_dev_remove_slog(struct replicator_device *rdev);
+int replicator_dev_add_rimage(struct replicator_device *rdev, struct logical_volume *lv);
+struct logical_volume *replicator_dev_remove_rimage(struct replicator_device *rdev);
+
+int lv_is_active_replicator_dev(const struct logical_volume *lv);
+int lv_is_replicator(const struct logical_volume *lv);
+int lv_is_replicator_dev(const struct logical_volume *lv);
+int lv_is_rimage(const struct logical_volume *lv);
+int lv_is_rlog(const struct logical_volume *lv);
+int lv_is_slog(const struct logical_volume *lv);
+struct logical_volume *first_replicator_dev(const struct logical_volume *lv);
+/* --  metadata/replicator_manip.c */
+struct cmd_vg *cmd_vg_add(struct dm_pool *mem, struct dm_list *cmd_vgs,
+                         const char *vg_name, const char *vgid,
+                         uint32_t flags);
+struct cmd_vg *cmd_vg_lookup(struct dm_list *cmd_vgs,
+                            const char *vg_name, const char *vgid);
+int cmd_vg_read(struct cmd_context *cmd, struct dm_list *cmd_vgs);
+void free_cmd_vgs(struct dm_list *cmd_vgs);
+
+int find_replicator_vgs(struct logical_volume *lv);
+
+int lv_read_replicator_vgs(struct logical_volume *lv);
+void lv_release_replicator_vgs(struct logical_volume *lv);
+
+struct logical_volume *find_pvmove_lv(struct volume_group *vg,
+                                     struct device *dev, uint32_t lv_type);
+struct logical_volume *find_pvmove_lv_from_pvname(struct cmd_context *cmd,
+                                                 struct volume_group *vg,
+                                                 const char *name,
+                                                 const char *uuid,
+                                                 uint32_t lv_type);
+const char *get_pvmove_pvname_from_lv(struct logical_volume *lv);
+const char *get_pvmove_pvname_from_lv_mirr(struct logical_volume *lv_mirr);
+percent_t copy_percent(struct logical_volume *lv_mirr);
+struct dm_list *lvs_using_lv(struct cmd_context *cmd, struct volume_group *vg,
+                         struct logical_volume *lv);
+
+uint32_t find_free_lvnum(struct logical_volume *lv);
+char *generate_lv_name(struct volume_group *vg, const char *format,
+                      char *buffer, size_t len);
+
+/*
+* Begin skeleton for external LVM library
+*/
+int pv_change_metadataignore(struct physical_volume *pv, uint32_t mda_ignore);
+
+
+int vg_check_write_mode(struct volume_group *vg);
+#define vg_is_clustered(vg) (vg_status((vg)) & CLUSTERED)
+#define vg_is_exported(vg) (vg_status((vg)) & EXPORTED_VG)
+#define vg_is_resizeable(vg) (vg_status((vg)) & RESIZEABLE_VG)
+
+int lv_has_unknown_segments(const struct logical_volume *lv);
+int vg_has_unknown_segments(const struct volume_group *vg);
+
+struct vgcreate_params {
+       char *vg_name;
+       uint32_t extent_size;
+       size_t max_pv;
+       size_t max_lv;
+       alloc_policy_t alloc;
+       int clustered; /* FIXME: put this into a 'status' variable instead? */
+       uint32_t vgmetadatacopies;
+};
+
+int vgcreate_params_validate(struct cmd_context *cmd,
+                            struct vgcreate_params *vp);
+
+int validate_vg_rename_params(struct cmd_context *cmd,
+                             const char *vg_name_old,
+                             const char *vg_name_new);
+#endif
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
new file mode 100644 (file)
index 0000000..5f75a66
--- /dev/null
@@ -0,0 +1,4106 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "metadata.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "lvmcache.h"
+#include "memlock.h"
+#include "str_list.h"
+#include "pv_alloc.h"
+#include "segtype.h"
+#include "activate.h"
+#include "display.h"
+#include "locking.h"
+#include "archiver.h"
+#include "defaults.h"
+#include "filter-persistent.h"
+
+#include <math.h>
+#include <sys/param.h>
+
+static struct physical_volume *_pv_read(struct cmd_context *cmd,
+                                       struct dm_pool *pvmem,
+                                       const char *pv_name,
+                                       struct dm_list *mdas,
+                                       uint64_t *label_sector,
+                                       int warnings, int scan_label_only);
+
+static struct physical_volume *_find_pv_by_name(struct cmd_context *cmd,
+                                               const char *pv_name);
+
+static struct pv_list *_find_pv_in_vg(const struct volume_group *vg,
+                                     const char *pv_name);
+
+static struct pv_list *_find_pv_in_vg_by_uuid(const struct volume_group *vg,
+                                             const struct id *id);
+
+static uint32_t _vg_bad_status_bits(const struct volume_group *vg,
+                                   uint64_t status);
+
+const char _really_init[] =
+    "Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? ";
+
+static int _alignment_overrides_default(unsigned long data_alignment,
+                                       unsigned long default_pe_align)
+{
+       return data_alignment && (default_pe_align % data_alignment);
+}
+
+unsigned long set_pe_align(struct physical_volume *pv, unsigned long data_alignment)
+{
+       unsigned long default_pe_align, temp_pe_align;
+
+       if (pv->pe_align)
+               goto out;
+
+       if (data_alignment) {
+               /* Always use specified data_alignment */
+               pv->pe_align = data_alignment;
+               goto out;
+       }
+
+       default_pe_align = find_config_tree_int(pv->fmt->cmd,
+                                               "devices/default_data_alignment",
+                                               DEFAULT_DATA_ALIGNMENT);
+
+       if (default_pe_align)
+               /* align on 1 MiB multiple */
+               default_pe_align *= DEFAULT_PE_ALIGN;
+       else
+               /* align on 64 KiB multiple (old default) */
+               default_pe_align = DEFAULT_PE_ALIGN_OLD;
+
+       pv->pe_align = MAX((default_pe_align << SECTOR_SHIFT),
+                          lvm_getpagesize()) >> SECTOR_SHIFT;
+
+       if (!pv->dev)
+               goto out;
+
+       /*
+        * Align to stripe-width of underlying md device if present
+        */
+       if (find_config_tree_bool(pv->fmt->cmd, "devices/md_chunk_alignment",
+                                 DEFAULT_MD_CHUNK_ALIGNMENT)) {
+               temp_pe_align = dev_md_stripe_width(pv->fmt->cmd->sysfs_dir, pv->dev);
+               if (_alignment_overrides_default(temp_pe_align, default_pe_align))
+                       pv->pe_align = MAX(pv->pe_align, temp_pe_align);
+       }
+
+       /*
+        * Align to topology's minimum_io_size or optimal_io_size if present
+        * - minimum_io_size - the smallest request the device can perform
+        *   w/o incurring a read-modify-write penalty (e.g. MD's chunk size)
+        * - optimal_io_size - the device's preferred unit of receiving I/O
+        *   (e.g. MD's stripe width)
+        */
+       if (find_config_tree_bool(pv->fmt->cmd,
+                                 "devices/data_alignment_detection",
+                                 DEFAULT_DATA_ALIGNMENT_DETECTION)) {
+               temp_pe_align = dev_minimum_io_size(pv->fmt->cmd->sysfs_dir, pv->dev);
+               if (_alignment_overrides_default(temp_pe_align, default_pe_align))
+                       pv->pe_align = MAX(pv->pe_align, temp_pe_align);
+
+               temp_pe_align = dev_optimal_io_size(pv->fmt->cmd->sysfs_dir, pv->dev);
+               if (_alignment_overrides_default(temp_pe_align, default_pe_align))
+                       pv->pe_align = MAX(pv->pe_align, temp_pe_align);
+       }
+
+out:
+       log_very_verbose("%s: Setting PE alignment to %lu sectors.",
+                        dev_name(pv->dev), pv->pe_align);
+
+       return pv->pe_align;
+}
+
+unsigned long set_pe_align_offset(struct physical_volume *pv,
+                                 unsigned long data_alignment_offset)
+{
+       if (pv->pe_align_offset)
+               goto out;
+
+       if (data_alignment_offset) {
+               /* Always use specified data_alignment_offset */
+               pv->pe_align_offset = data_alignment_offset;
+               goto out;
+       }
+
+       if (!pv->dev)
+               goto out;
+
+       if (find_config_tree_bool(pv->fmt->cmd,
+                                 "devices/data_alignment_offset_detection",
+                                 DEFAULT_DATA_ALIGNMENT_OFFSET_DETECTION)) {
+               int align_offset = dev_alignment_offset(pv->fmt->cmd->sysfs_dir,
+                                                       pv->dev);
+               /* must handle a -1 alignment_offset; means dev is misaligned */
+               if (align_offset < 0)
+                       align_offset = 0;
+               pv->pe_align_offset = MAX(pv->pe_align_offset, align_offset);
+       }
+
+out:
+       log_very_verbose("%s: Setting PE alignment offset to %lu sectors.",
+                        dev_name(pv->dev), pv->pe_align_offset);
+
+       return pv->pe_align_offset;
+}
+
+void add_pvl_to_vgs(struct volume_group *vg, struct pv_list *pvl)
+{
+       dm_list_add(&vg->pvs, &pvl->list);
+       vg->pv_count++;
+       pvl->pv->vg = vg;
+}
+
+void del_pvl_from_vgs(struct volume_group *vg, struct pv_list *pvl)
+{
+       vg->pv_count--;
+       dm_list_del(&pvl->list);
+       pvl->pv->vg = NULL; /* orphan */
+}
+
+
+/**
+ * add_pv_to_vg - Add a physical volume to a volume group
+ * @vg - volume group to add to
+ * @pv_name - name of the pv (to be removed)
+ * @pv - physical volume to add to volume group
+ *
+ * Returns:
+ *  0 - failure
+ *  1 - success
+ * FIXME: remove pv_name - obtain safely from pv
+ */
+int add_pv_to_vg(struct volume_group *vg, const char *pv_name,
+                struct physical_volume *pv)
+{
+       struct pv_list *pvl;
+       struct format_instance *fid = vg->fid;
+       struct dm_pool *mem = vg->vgmem;
+       char uuid[64] __attribute__((aligned(8)));
+       struct dm_list *mdas;
+
+       log_verbose("Adding physical volume '%s' to volume group '%s'",
+                   pv_name, vg->name);
+
+       if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
+               log_error("pv_list allocation for '%s' failed", pv_name);
+               return 0;
+       }
+
+       if (!is_orphan_vg(pv->vg_name)) {
+               log_error("Physical volume '%s' is already in volume group "
+                         "'%s'", pv_name, pv->vg_name);
+               return 0;
+       }
+
+       if (pv->fmt != fid->fmt) {
+               log_error("Physical volume %s is of different format type (%s)",
+                         pv_name, pv->fmt->name);
+               return 0;
+       }
+
+       /* Ensure PV doesn't depend on another PV already in the VG */
+       if (pv_uses_vg(pv, vg)) {
+               log_error("Physical volume %s might be constructed from same "
+                         "volume group %s", pv_name, vg->name);
+               return 0;
+       }
+
+       if (!(pv->vg_name = dm_pool_strdup(mem, vg->name))) {
+               log_error("vg->name allocation failed for '%s'", pv_name);
+               return 0;
+       }
+
+       memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
+
+       /* Units of 512-byte sectors */
+       pv->pe_size = vg->extent_size;
+
+       /*
+        * pe_count must always be calculated by pv_setup
+        */
+       pv->pe_alloc_count = 0;
+
+       /*
+        * FIXME: this does not work entirely correctly in the case where a PV
+        * has 2 mdas and only one is ignored; ideally all non-ignored mdas
+        * should be placed on metadata_areas list and ignored on the
+        * metadata_areas_ignored list; however this requires another
+        * fairly complex refactoring to remove the 'mdas' parameter from both
+        * pv_setup and pv_write.  For now, we only put ignored mdas on the
+        * metadata_areas_ignored list if all mdas in the PV are ignored;
+        * otherwise, we use the non-ignored list.
+        */
+       if (!pv_mda_used_count(pv))
+               mdas = &fid->metadata_areas_ignored;
+       else
+               mdas = &fid->metadata_areas_in_use;
+
+       if (!fid->fmt->ops->pv_setup(fid->fmt, UINT64_C(0), 0,
+                                    vg->extent_size, 0, 0, 0UL, UINT64_C(0),
+                                    0, mdas, pv, vg)) {
+               log_error("Format-specific setup of physical volume '%s' "
+                         "failed.", pv_name);
+               return 0;
+       }
+
+       if (_find_pv_in_vg(vg, pv_name) ||
+           _find_pv_in_vg_by_uuid(vg, &pv->id)) {
+               if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+                       stack;
+                       uuid[0] = '\0';
+               }
+               log_error("Physical volume '%s (%s)' listed more than once.",
+                         pv_name, uuid);
+               return 0;
+       }
+
+       if (vg->pv_count && (vg->pv_count == vg->max_pv)) {
+               log_error("No space for '%s' - volume group '%s' "
+                         "holds max %d physical volume(s).", pv_name,
+                         vg->name, vg->max_pv);
+               return 0;
+       }
+
+       if (!alloc_pv_segment_whole_pv(mem, pv))
+               return_0;
+
+       if ((uint64_t) vg->extent_count + pv->pe_count > UINT32_MAX) {
+               log_error("Unable to add %s to %s: new extent count (%"
+                         PRIu64 ") exceeds limit (%" PRIu32 ").",
+                         pv_name, vg->name,
+                         (uint64_t) vg->extent_count + pv->pe_count,
+                         UINT32_MAX);
+               return 0;
+       }
+
+       pvl->pv = pv;
+       add_pvl_to_vgs(vg, pvl);
+       vg->extent_count += pv->pe_count;
+       vg->free_count += pv->pe_count;
+
+       return 1;
+}
+
+static int _copy_pv(struct dm_pool *pvmem,
+                   struct physical_volume *pv_to,
+                   struct physical_volume *pv_from)
+{
+       memcpy(pv_to, pv_from, sizeof(*pv_to));
+
+       if (!(pv_to->vg_name = dm_pool_strdup(pvmem, pv_from->vg_name)))
+               return_0;
+
+       if (!str_list_dup(pvmem, &pv_to->tags, &pv_from->tags))
+               return_0;
+
+       if (!peg_dup(pvmem, &pv_to->segments, &pv_from->segments))
+               return_0;
+
+       return 1;
+}
+
+static struct pv_list *_copy_pvl(struct dm_pool *pvmem, struct pv_list *pvl_from)
+{
+       struct pv_list *pvl_to = NULL;
+
+       if (!(pvl_to = dm_pool_zalloc(pvmem, sizeof(*pvl_to))))
+               return_NULL;
+
+       if (!(pvl_to->pv = dm_pool_alloc(pvmem, sizeof(*pvl_to->pv))))
+               goto_bad;
+
+       if(!_copy_pv(pvmem, pvl_to->pv, pvl_from->pv))
+               goto_bad;
+
+       return pvl_to;
+bad:
+       dm_pool_free(pvmem, pvl_to);
+       return NULL;
+}
+
+int get_pv_from_vg_by_id(const struct format_type *fmt, const char *vg_name,
+                        const char *vgid, const char *pvid,
+                        struct physical_volume *pv)
+{
+       struct volume_group *vg;
+       struct pv_list *pvl;
+       int r = 0, consistent = 0;
+
+       if (!(vg = vg_read_internal(fmt->cmd, vg_name, vgid, 1, &consistent))) {
+               log_error("get_pv_from_vg_by_id: vg_read_internal failed to read VG %s",
+                         vg_name);
+               return 0;
+       }
+
+       if (!consistent)
+               log_warn("WARNING: Volume group %s is not consistent",
+                        vg_name);
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (id_equal(&pvl->pv->id, (const struct id *) pvid)) {
+                       if (!_copy_pv(fmt->cmd->mem, pv, pvl->pv)) {
+                               log_error("internal PV duplication failed");
+                               r = 0;
+                               goto out;
+                       }
+                       r = 1;
+                       goto out;
+               }
+       }
+out:
+       free_vg(vg);
+       return r;
+}
+
+int move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
+           const char *pv_name)
+{
+       struct physical_volume *pv;
+       struct pv_list *pvl;
+
+       /* FIXME: handle tags */
+       if (!(pvl = find_pv_in_vg(vg_from, pv_name))) {
+               log_error("Physical volume %s not in volume group %s",
+                         pv_name, vg_from->name);
+               return 0;
+       }
+
+       if (_vg_bad_status_bits(vg_from, RESIZEABLE_VG) ||
+           _vg_bad_status_bits(vg_to, RESIZEABLE_VG))
+               return 0;
+
+       del_pvl_from_vgs(vg_from, pvl);
+       add_pvl_to_vgs(vg_to, pvl);
+
+       pv = pvl->pv;
+
+       vg_from->extent_count -= pv_pe_count(pv);
+       vg_to->extent_count += pv_pe_count(pv);
+
+       vg_from->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+       vg_to->free_count += pv_pe_count(pv) - pv_pe_alloc_count(pv);
+
+       return 1;
+}
+
+int move_pvs_used_by_lv(struct volume_group *vg_from,
+                       struct volume_group *vg_to,
+                       const char *lv_name)
+{
+       struct lv_segment *lvseg;
+       unsigned s;
+       struct lv_list *lvl;
+       struct logical_volume *lv;
+
+       /* FIXME: handle tags */
+       if (!(lvl = find_lv_in_vg(vg_from, lv_name))) {
+               log_error("Logical volume %s not in volume group %s",
+                         lv_name, vg_from->name);
+               return 0;
+       }
+
+       if (_vg_bad_status_bits(vg_from, RESIZEABLE_VG) ||
+           _vg_bad_status_bits(vg_to, RESIZEABLE_VG))
+               return 0;
+
+       dm_list_iterate_items(lvseg, &lvl->lv->segments) {
+               if (lvseg->log_lv)
+                       if (!move_pvs_used_by_lv(vg_from, vg_to,
+                                                    lvseg->log_lv->name))
+                               return_0;
+               for (s = 0; s < lvseg->area_count; s++) {
+                       if (seg_type(lvseg, s) == AREA_PV) {
+                               if (!move_pv(vg_from, vg_to,
+                                             pv_dev_name(seg_pv(lvseg, s))))
+                                       return_0;
+                       } else if (seg_type(lvseg, s) == AREA_LV) {
+                               lv = seg_lv(lvseg, s);
+                               if (!move_pvs_used_by_lv(vg_from, vg_to,
+                                                            lv->name))
+                                   return_0;
+                       }
+               }
+       }
+       return 1;
+}
+
+static int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name)
+{
+       char vg_path[PATH_MAX];
+
+       if (!validate_name(vg_name))
+               return_0;
+
+       snprintf(vg_path, PATH_MAX, "%s%s", cmd->dev_dir, vg_name);
+       if (path_exists(vg_path)) {
+               log_error("%s: already exists in filesystem", vg_path);
+               return 0;
+       }
+
+       return 1;
+}
+
+int validate_vg_rename_params(struct cmd_context *cmd,
+                             const char *vg_name_old,
+                             const char *vg_name_new)
+{
+       unsigned length;
+       char *dev_dir;
+
+       dev_dir = cmd->dev_dir;
+       length = strlen(dev_dir);
+
+       /* Check sanity of new name */
+       if (strlen(vg_name_new) > NAME_LEN - length - 2) {
+               log_error("New volume group path exceeds maximum length "
+                         "of %d!", NAME_LEN - length - 2);
+               return 0;
+       }
+
+       if (!validate_new_vg_name(cmd, vg_name_new)) {
+               log_error("New volume group name \"%s\" is invalid",
+                         vg_name_new);
+               return 0;
+       }
+
+       if (!strcmp(vg_name_old, vg_name_new)) {
+               log_error("Old and new volume group names must differ");
+               return 0;
+       }
+
+       return 1;
+}
+
+int vg_rename(struct cmd_context *cmd, struct volume_group *vg,
+             const char *new_name)
+{
+       struct dm_pool *mem = vg->vgmem;
+       struct pv_list *pvl;
+
+       vg->old_name = vg->name;
+
+       if (!(vg->name = dm_pool_strdup(mem, new_name))) {
+               log_error("vg->name allocation failed for '%s'", new_name);
+               return 0;
+       }
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (!(pvl->pv->vg_name = dm_pool_strdup(mem, new_name))) {
+                       log_error("pv->vg_name allocation failed for '%s'",
+                                 pv_dev_name(pvl->pv));
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+int remove_lvs_in_vg(struct cmd_context *cmd,
+                    struct volume_group *vg,
+                    force_t force)
+{
+       struct dm_list *lst;
+       struct lv_list *lvl;
+
+       while ((lst = dm_list_first(&vg->lvs))) {
+               lvl = dm_list_item(lst, struct lv_list);
+               if (!lv_remove_with_dependencies(cmd, lvl->lv, force, 0))
+                   return 0;
+       }
+
+       return 1;
+}
+
+int vg_remove_check(struct volume_group *vg)
+{
+       unsigned lv_count;
+
+       if (vg_read_error(vg) || vg_missing_pv_count(vg)) {
+               log_error("Volume group \"%s\" not found, is inconsistent "
+                         "or has PVs missing.", vg ? vg->name : "");
+               log_error("Consider vgreduce --removemissing if metadata "
+                         "is inconsistent.");
+               return 0;
+       }
+
+       if (!vg_check_status(vg, EXPORTED_VG))
+               return 0;
+
+       lv_count = vg_visible_lvs(vg);
+
+       if (lv_count) {
+               log_error("Volume group \"%s\" still contains %u "
+                         "logical volume(s)", vg->name, lv_count);
+               return 0;
+       }
+
+       if (!archive(vg))
+               return 0;
+
+       return 1;
+}
+
+void vg_remove_pvs(struct volume_group *vg)
+{
+       struct pv_list *pvl, *tpvl;
+
+       dm_list_iterate_items_safe(pvl, tpvl, &vg->pvs) {
+               del_pvl_from_vgs(vg, pvl);
+               dm_list_add(&vg->removed_pvs, &pvl->list);
+       }
+}
+
+int vg_remove(struct volume_group *vg)
+{
+       struct physical_volume *pv;
+       struct pv_list *pvl;
+       int ret = 1;
+
+       if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+               log_error("Can't get lock for orphan PVs");
+               return 0;
+       }
+
+       if (!vg_remove_mdas(vg)) {
+               log_error("vg_remove_mdas %s failed", vg->name);
+               unlock_vg(vg->cmd, VG_ORPHANS);
+               return 0;
+       }
+
+       /* init physical volumes */
+       dm_list_iterate_items(pvl, &vg->removed_pvs) {
+               pv = pvl->pv;
+               if (is_missing_pv(pv))
+                       continue;
+
+               log_verbose("Removing physical volume \"%s\" from "
+                           "volume group \"%s\"", pv_dev_name(pv), vg->name);
+               pv->vg_name = vg->fid->fmt->orphan_vg_name;
+               pv->status = ALLOCATABLE_PV;
+
+               if (!dev_get_size(pv_dev(pv), &pv->size)) {
+                       log_error("%s: Couldn't get size.", pv_dev_name(pv));
+                       ret = 0;
+                       continue;
+               }
+
+               /* FIXME Write to same sector label was read from */
+               if (!pv_write(vg->cmd, pv, NULL, INT64_C(-1))) {
+                       log_error("Failed to remove physical volume \"%s\""
+                                 " from volume group \"%s\"",
+                                 pv_dev_name(pv), vg->name);
+                       ret = 0;
+               }
+       }
+
+       backup_remove(vg->cmd, vg->name);
+
+       if (ret)
+               log_print("Volume group \"%s\" successfully removed", vg->name);
+       else
+               log_error("Volume group \"%s\" not properly removed", vg->name);
+
+       unlock_vg(vg->cmd, VG_ORPHANS);
+       return ret;
+}
+
+/*
+ * Extend a VG by a single PV / device path
+ *
+ * Parameters:
+ * - vg: handle of volume group to extend by 'pv_name'
+ * - pv_name: device path of PV to add to VG
+ * - pp: parameters to pass to implicit pvcreate; if NULL, do not pvcreate
+ *
+ */
+static int vg_extend_single_pv(struct volume_group *vg, char *pv_name,
+                              struct pvcreate_params *pp)
+{
+       struct physical_volume *pv;
+
+       pv = pv_by_path(vg->fid->fmt->cmd, pv_name);
+       if (!pv && !pp) {
+               log_error("%s not identified as an existing "
+                         "physical volume", pv_name);
+               return 0;
+       } else if (!pv && pp) {
+               pv = pvcreate_single(vg->cmd, pv_name, pp);
+               if (!pv)
+                       return 0;
+       }
+       if (!add_pv_to_vg(vg, pv_name, pv))
+               return 0;
+       return 1;
+}
+
+/*
+ * Extend a VG by a single PV / device path
+ *
+ * Parameters:
+ * - vg: handle of volume group to extend by 'pv_name'
+ * - pv_count: count of device paths of PVs
+ * - pv_names: device paths of PVs to add to VG
+ * - pp: parameters to pass to implicit pvcreate; if NULL, do not pvcreate
+ *
+ */
+int vg_extend(struct volume_group *vg, int pv_count, char **pv_names,
+             struct pvcreate_params *pp)
+{
+       int i;
+
+       if (_vg_bad_status_bits(vg, RESIZEABLE_VG))
+               return 0;
+
+       /* attach each pv */
+       for (i = 0; i < pv_count; i++) {
+               unescape_colons_and_at_signs(pv_names[i], NULL, NULL);
+               if (!vg_extend_single_pv(vg, pv_names[i], pp))
+                       goto bad;
+       }
+
+/* FIXME Decide whether to initialise and add new mdahs to format instance */
+
+       return 1;
+
+      bad:
+       log_error("Unable to add physical volume '%s' to "
+                 "volume group '%s'.", pv_names[i], vg->name);
+       return 0;
+}
+
+/* FIXME: use this inside vgreduce_single? */
+int vg_reduce(struct volume_group *vg, char *pv_name)
+{
+       struct physical_volume *pv;
+       struct pv_list *pvl;
+
+       if (_vg_bad_status_bits(vg, RESIZEABLE_VG))
+               return 0;
+
+       if (!archive(vg))
+               goto bad;
+
+       /* remove each pv */
+       if (!(pvl = find_pv_in_vg(vg, pv_name))) {
+               log_error("Physical volume %s not in volume group %s.",
+                         pv_name, vg->name);
+               goto bad;
+       }
+
+       pv = pvl->pv;
+
+       if (pv_pe_alloc_count(pv)) {
+               log_error("Physical volume %s still in use.",
+                         pv_name);
+               goto bad;
+       }
+
+       if (!dev_get_size(pv_dev(pv), &pv->size)) {
+               log_error("%s: Couldn't get size.", pv_name);
+               goto bad;
+       }
+
+       vg->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+       vg->extent_count -= pv_pe_count(pv);
+       del_pvl_from_vgs(vg, pvl);
+
+       /* add pv to the remove_pvs list */
+       dm_list_add(&vg->removed_pvs, &pvl->list);
+
+       return 1;
+
+      bad:
+       log_error("Unable to remove physical volume '%s' from "
+                 "volume group '%s'.", pv_name, vg->name);
+       return 0;
+}
+
+int lv_change_tag(struct logical_volume *lv, const char *tag, int add_tag)
+{
+       char *tag_new;
+
+       if (!(lv->vg->fid->fmt->features & FMT_TAGS)) {
+               log_error("Logical volume %s/%s does not support tags",
+                         lv->vg->name, lv->name);
+               return 0;
+       }
+
+       if (add_tag) {
+               if (!(tag_new = dm_pool_strdup(lv->vg->vgmem, tag))) {
+                       log_error("Failed to duplicate tag %s from %s/%s",
+                                 tag, lv->vg->name, lv->name);
+                       return 0;
+               }
+               if (!str_list_add(lv->vg->vgmem, &lv->tags, tag_new)) {
+                       log_error("Failed to add tag %s to %s/%s",
+                                 tag, lv->vg->name, lv->name);
+                       return 0;
+               }
+       } else {
+               if (!str_list_del(&lv->tags, tag)) {
+                       log_error("Failed to remove tag %s from %s/%s",
+                                 tag, lv->vg->name, lv->name);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+int vg_change_tag(struct volume_group *vg, const char *tag, int add_tag)
+{
+       char *tag_new;
+
+       if (!(vg->fid->fmt->features & FMT_TAGS)) {
+               log_error("Volume group %s does not support tags", vg->name);
+               return 0;
+       }
+
+       if (add_tag) {
+               if (!(tag_new = dm_pool_strdup(vg->vgmem, tag))) {
+                       log_error("Failed to duplicate tag %s from %s",
+                                 tag, vg->name);
+                       return 0;
+               }
+               if (!str_list_add(vg->vgmem, &vg->tags, tag_new)) {
+                       log_error("Failed to add tag %s to volume group %s",
+                                 tag, vg->name);
+                       return 0;
+               }
+       } else {
+               if (!str_list_del(&vg->tags, tag)) {
+                       log_error("Failed to remove tag %s from volume group "
+                                 "%s", tag, vg->name);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+const char *strip_dir(const char *vg_name, const char *dev_dir)
+{
+       size_t len = strlen(dev_dir);
+       if (!strncmp(vg_name, dev_dir, len))
+               vg_name += len;
+
+       return vg_name;
+}
+
+/*
+ * Validate parameters to vg_create() before calling.
+ * FIXME: Move inside vg_create library function.
+ * FIXME: Change vgcreate_params struct to individual gets/sets
+ */
+int vgcreate_params_validate(struct cmd_context *cmd,
+                            struct vgcreate_params *vp)
+{
+       if (!validate_new_vg_name(cmd, vp->vg_name)) {
+               log_error("New volume group name \"%s\" is invalid",
+                         vp->vg_name);
+               return 1;
+       }
+
+       if (vp->alloc == ALLOC_INHERIT) {
+               log_error("Volume Group allocation policy cannot inherit "
+                         "from anything");
+               return 1;
+       }
+
+       if (!vp->extent_size) {
+               log_error("Physical extent size may not be zero");
+               return 1;
+       }
+
+       if (!(cmd->fmt->features & FMT_UNLIMITED_VOLS)) {
+               if (!vp->max_lv)
+                       vp->max_lv = 255;
+               if (!vp->max_pv)
+                       vp->max_pv = 255;
+               if (vp->max_lv > 255 || vp->max_pv > 255) {
+                       log_error("Number of volumes may not exceed 255");
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Create a (struct volume_group) volume group handle from a struct volume_group pointer and a
+ * possible failure code or zero for success.
+ */
+static struct volume_group *_vg_make_handle(struct cmd_context *cmd,
+                            struct volume_group *vg,
+                            uint32_t failure)
+{
+       struct dm_pool *vgmem;
+
+       if (!vg) {
+               if (!(vgmem = dm_pool_create("lvm2 vg_handle", VG_MEMPOOL_CHUNK)) ||
+                   !(vg = dm_pool_zalloc(vgmem, sizeof(*vg)))) {
+                       log_error("Error allocating vg handle.");
+                       if (vgmem)
+                               dm_pool_destroy(vgmem);
+                       return_NULL;
+               }
+               vg->vgmem = vgmem;
+       }
+
+       vg->read_status = failure;
+
+       return (struct volume_group *)vg;
+}
+
+int lv_has_unknown_segments(const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+       /* foreach segment */
+       dm_list_iterate_items(seg, &lv->segments)
+               if (seg_unknown(seg))
+                       return 1;
+       return 0;
+}
+
+int vg_has_unknown_segments(const struct volume_group *vg)
+{
+       struct lv_list *lvl;
+
+       /* foreach LV */
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (lv_has_unknown_segments(lvl->lv))
+                       return 1;
+       return 0;
+}
+
+/*
+ * Create a VG with default parameters.
+ * Returns:
+ * - struct volume_group* with SUCCESS code: VG structure created
+ * - NULL or struct volume_group* with FAILED_* code: error creating VG structure
+ * Use vg_read_error() to determine success or failure.
+ * FIXME: cleanup usage of _vg_make_handle()
+ */
+struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name)
+{
+       struct volume_group *vg;
+       int consistent = 0;
+       struct dm_pool *mem;
+       uint32_t rc;
+
+       if (!validate_name(vg_name)) {
+               log_error("Invalid vg name %s", vg_name);
+               /* FIXME: use _vg_make_handle() w/proper error code */
+               return NULL;
+       }
+
+       rc = vg_lock_newname(cmd, vg_name);
+       if (rc != SUCCESS)
+               /* NOTE: let caller decide - this may be check for existence */
+               return _vg_make_handle(cmd, NULL, rc);
+
+       /* FIXME: Is this vg_read_internal necessary? Move it inside
+          vg_lock_newname? */
+       /* is this vg name already in use ? */
+       if ((vg = vg_read_internal(cmd, vg_name, NULL, 1, &consistent))) {
+               log_error("A volume group called '%s' already exists.", vg_name);
+               unlock_and_free_vg(cmd, vg, vg_name);
+               return _vg_make_handle(cmd, NULL, FAILED_EXIST);
+       }
+
+       if (!(mem = dm_pool_create("lvm2 vg_create", VG_MEMPOOL_CHUNK)))
+               goto_bad;
+
+       if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
+               goto_bad;
+
+       if (!id_create(&vg->id)) {
+               log_error("Couldn't create uuid for volume group '%s'.",
+                         vg_name);
+               goto bad;
+       }
+
+       /* Strip dev_dir if present */
+       vg_name = strip_dir(vg_name, cmd->dev_dir);
+
+       vg->vgmem = mem;
+       vg->cmd = cmd;
+
+       if (!(vg->name = dm_pool_strdup(mem, vg_name)))
+               goto_bad;
+
+       vg->seqno = 0;
+
+       vg->status = (RESIZEABLE_VG | LVM_READ | LVM_WRITE);
+       if (!(vg->system_id = dm_pool_alloc(mem, NAME_LEN)))
+               goto_bad;
+
+       *vg->system_id = '\0';
+
+       vg->extent_size = DEFAULT_EXTENT_SIZE * 2;
+       vg->extent_count = 0;
+       vg->free_count = 0;
+
+       vg->max_lv = DEFAULT_MAX_LV;
+       vg->max_pv = DEFAULT_MAX_PV;
+
+       vg->alloc = DEFAULT_ALLOC_POLICY;
+       vg->mda_copies = DEFAULT_VGMETADATACOPIES;
+
+       vg->pv_count = 0;
+       dm_list_init(&vg->pvs);
+
+       dm_list_init(&vg->lvs);
+
+       dm_list_init(&vg->tags);
+
+       /* initialize removed_pvs list */
+       dm_list_init(&vg->removed_pvs);
+
+       if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg_name,
+                                                      NULL, NULL))) {
+               log_error("Failed to create format instance");
+               goto bad;
+       }
+
+       if (vg->fid->fmt->ops->vg_setup &&
+           !vg->fid->fmt->ops->vg_setup(vg->fid, vg)) {
+               log_error("Format specific setup of volume group '%s' failed.",
+                         vg_name);
+               goto bad;
+       }
+       return _vg_make_handle(cmd, vg, SUCCESS);
+
+bad:
+       unlock_and_free_vg(cmd, vg, vg_name);
+       /* FIXME: use _vg_make_handle() w/proper error code */
+       return NULL;
+}
+
+uint64_t extents_from_size(struct cmd_context *cmd, uint64_t size,
+                          uint32_t extent_size)
+{
+       if (size % extent_size) {
+               size += extent_size - size % extent_size;
+               log_print("Rounding up size to full physical extent %s",
+                         display_size(cmd, size));
+       }
+
+       if (size > (uint64_t) UINT32_MAX * extent_size) {
+               log_error("Volume too large (%s) for extent size %s. "
+                         "Upper limit is %s.",
+                         display_size(cmd, size),
+                         display_size(cmd, (uint64_t) extent_size),
+                         display_size(cmd, (uint64_t) UINT32_MAX *
+                                      extent_size));
+               return 0;
+       }
+
+       return (uint64_t) size / extent_size;
+}
+
+/*
+ * Return random integer in [0,max) interval
+ *
+ * The loop rejects numbers that come from an "incomplete" slice of the
+ * RAND_MAX space (considering the number space [0, RAND_MAX] is divided
+ * into some "max"-sized slices and at most a single smaller slice,
+ * between [n*max, RAND_MAX] for suitable n -- numbers from this last slice
+ * are discarded because they could distort the distribution in favour of
+ * smaller numbers.
+ */
+static unsigned _even_rand( unsigned *seed, unsigned max )
+{
+       unsigned r, ret;
+
+       /* make sure distribution is even */
+       do {
+               r = (unsigned) rand_r( seed );
+               ret = r % max;
+       } while ( r - ret > RAND_MAX - max );
+
+       return ret;
+}
+
+static dm_bitset_t _bitset_with_random_bits(struct dm_pool *mem, uint32_t num_bits,
+                                           uint32_t num_set_bits, unsigned *seed)
+{
+       dm_bitset_t bs;
+       unsigned bit_selected;
+       char buf[32];
+       uint32_t i = num_bits - num_set_bits;
+
+       if (!(bs = dm_bitset_create(mem, (unsigned) num_bits))) {
+               log_error("Failed to allocate bitset for setting random bits.");
+               return NULL;
+       }
+
+        if (!dm_pool_begin_object(mem, 512)) {
+                log_error("dm_pool_begin_object failed for random list of bits.");
+               dm_pool_free(mem, bs);
+                return NULL;
+        }
+
+       /* Perform loop num_set_bits times, selecting one bit each time */
+       while (i++ < num_bits) {
+               /* Select a random bit between 0 and (i-1) inclusive. */
+               bit_selected = _even_rand(seed, i);
+
+               /*
+                * If the bit was already set, set the new bit that became
+                * choosable for the first time during this pass.
+                * This maintains a uniform probability distribution by compensating
+                * for being unable to select it until this pass.
+                */
+               if (dm_bit(bs, bit_selected))
+                       bit_selected = i - 1;
+
+               dm_bit_set(bs, bit_selected);
+
+               if (dm_snprintf(buf, sizeof(buf), "%u ", bit_selected) < 0) {
+                       log_error("snprintf random bit failed.");
+                       dm_pool_free(mem, bs);
+                       return NULL;
+               }
+               if (!dm_pool_grow_object(mem, buf, strlen(buf))) {
+                       log_error("Failed to generate list of random bits.");
+                       dm_pool_free(mem, bs);
+                       return NULL;
+               }
+       }
+
+       log_debug("Selected %" PRIu32 " random bits from %" PRIu32 ": %s", num_set_bits, num_bits, (char *) dm_pool_end_object(mem));
+
+       return bs;
+}
+
+static int _vg_ignore_mdas(struct volume_group *vg, uint32_t num_to_ignore)
+{
+       struct metadata_area *mda;
+       uint32_t mda_used_count = vg_mda_used_count(vg);
+       dm_bitset_t mda_to_ignore_bs;
+       int r = 1;
+
+       log_debug("Adjusting ignored mdas for %s: %" PRIu32 " of %" PRIu32 " mdas in use "
+                 "but %" PRIu32 " required.  Changing %" PRIu32 " mda.",
+                 vg->name, mda_used_count, vg_mda_count(vg), vg_mda_copies(vg), num_to_ignore);
+
+       if (!num_to_ignore)
+               return 1;
+
+       if (!(mda_to_ignore_bs = _bitset_with_random_bits(vg->vgmem, mda_used_count,
+                                                         num_to_ignore, &vg->cmd->rand_seed)))
+               return_0;
+
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use)
+               if (!mda_is_ignored(mda) && (--mda_used_count,
+                   dm_bit(mda_to_ignore_bs, mda_used_count))) {
+                       mda_set_ignored(mda, 1);
+                       if (!--num_to_ignore)
+                               goto out;
+               }
+
+       log_error(INTERNAL_ERROR "Unable to find %"PRIu32" metadata areas to ignore "
+                 "on volume group %s", num_to_ignore, vg->name);
+
+       r = 0;
+
+out:
+       dm_pool_free(vg->vgmem, mda_to_ignore_bs);
+       return r;
+}
+
+static int _vg_unignore_mdas(struct volume_group *vg, uint32_t num_to_unignore)
+{
+       struct metadata_area *mda, *tmda;
+       uint32_t mda_used_count = vg_mda_used_count(vg);
+       uint32_t mda_count = vg_mda_count(vg);
+       uint32_t mda_free_count = mda_count - mda_used_count;
+       dm_bitset_t mda_to_unignore_bs;
+       int r = 1;
+
+       if (!num_to_unignore)
+               return 1;
+
+       log_debug("Adjusting ignored mdas for %s: %" PRIu32 " of %" PRIu32 " mdas in use "
+                 "but %" PRIu32 " required.  Changing %" PRIu32 " mda.",
+                 vg->name, mda_used_count, mda_count, vg_mda_copies(vg), num_to_unignore);
+
+       if (!(mda_to_unignore_bs = _bitset_with_random_bits(vg->vgmem, mda_free_count,
+                                                           num_to_unignore, &vg->cmd->rand_seed)))
+               return_0;
+
+       dm_list_iterate_items_safe(mda, tmda, &vg->fid->metadata_areas_ignored)
+               if (mda_is_ignored(mda) && (--mda_free_count,
+                   dm_bit(mda_to_unignore_bs, mda_free_count))) {
+                       mda_set_ignored(mda, 0);
+                       dm_list_move(&vg->fid->metadata_areas_in_use,
+                                    &mda->list);
+                       if (!--num_to_unignore)
+                               goto out;
+               }
+
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use)
+               if (mda_is_ignored(mda) && (--mda_free_count,
+                   dm_bit(mda_to_unignore_bs, mda_free_count))) {
+                       mda_set_ignored(mda, 0);
+                       if (!--num_to_unignore)
+                               goto out;
+               }
+
+       log_error(INTERNAL_ERROR "Unable to find %"PRIu32" metadata areas to unignore "
+                "on volume group %s", num_to_unignore, vg->name);
+
+       r = 0;
+
+out:
+       dm_pool_free(vg->vgmem, mda_to_unignore_bs);
+       return r;
+}
+
+static int _vg_adjust_ignored_mdas(struct volume_group *vg)
+{
+       uint32_t mda_copies_used = vg_mda_used_count(vg);
+
+       if (vg->mda_copies == VGMETADATACOPIES_UNMANAGED) {
+               /* Ensure at least one mda is in use. */
+               if (!mda_copies_used && vg_mda_count(vg) && !_vg_unignore_mdas(vg, 1))
+                       return_0;
+               else
+                       return 1;
+       }
+
+
+       /* Not an error to have vg_mda_count larger than total mdas. */
+       if (vg->mda_copies == VGMETADATACOPIES_ALL ||
+           vg->mda_copies >= vg_mda_count(vg)) {
+               /* Use all */
+               if (!_vg_unignore_mdas(vg, vg_mda_count(vg) - mda_copies_used))
+                       return_0;
+       } else if (mda_copies_used < vg->mda_copies) {
+               if (!_vg_unignore_mdas(vg, vg->mda_copies - mda_copies_used))
+                       return_0;
+       } else if (mda_copies_used > vg->mda_copies)
+               if (!_vg_ignore_mdas(vg, mda_copies_used - vg->mda_copies))
+                       return_0;
+
+       /*
+        * The VGMETADATACOPIES_ALL value will never be written disk.
+        * It is a special cmdline value that means 2 things:
+        * 1. clear all ignore bits in all mdas in this vg
+        * 2. set the "unmanaged" policy going forward for metadata balancing
+        */
+       if (vg->mda_copies == VGMETADATACOPIES_ALL)
+               vg->mda_copies = VGMETADATACOPIES_UNMANAGED;
+
+       return 1;
+}
+
+uint64_t find_min_mda_size(struct dm_list *mdas)
+{
+       uint64_t min_mda_size = UINT64_MAX, mda_size;
+       struct metadata_area *mda;
+
+       dm_list_iterate_items(mda, mdas) {
+               if (!mda->ops->mda_total_sectors)
+                       continue;
+               mda_size = mda->ops->mda_total_sectors(mda);
+               if (mda_size < min_mda_size)
+                       min_mda_size = mda_size;
+       }
+
+       if (min_mda_size == UINT64_MAX)
+               min_mda_size = UINT64_C(0);
+
+       return min_mda_size;
+}
+
+static int _move_mdas(struct volume_group *vg_from, struct volume_group *vg_to,
+                     struct dm_list *mdas_from, struct dm_list *mdas_to)
+{
+       struct metadata_area *mda, *mda2;
+       int common_mda = 0;
+
+       dm_list_iterate_items_safe(mda, mda2, mdas_from) {
+               if (!mda->ops->mda_in_vg) {
+                       common_mda = 1;
+                       continue;
+               }
+
+               if (!mda->ops->mda_in_vg(vg_from->fid, vg_from, mda)) {
+                       if (is_orphan_vg(vg_to->name))
+                               dm_list_del(&mda->list);
+                       else
+                               dm_list_move(mdas_to, &mda->list);
+               }
+       }
+       return common_mda;
+}
+
+/*
+ * Separate metadata areas after splitting a VG.
+ * Also accepts orphan VG as destination (for vgreduce).
+ */
+int vg_split_mdas(struct cmd_context *cmd __attribute__((unused)),
+                 struct volume_group *vg_from, struct volume_group *vg_to)
+{
+       struct dm_list *mdas_from_in_use, *mdas_to_in_use;
+       struct dm_list *mdas_from_ignored, *mdas_to_ignored;
+       int common_mda = 0;
+
+       mdas_from_in_use = &vg_from->fid->metadata_areas_in_use;
+       mdas_from_ignored = &vg_from->fid->metadata_areas_ignored;
+       mdas_to_in_use = &vg_to->fid->metadata_areas_in_use;
+       mdas_to_ignored = &vg_to->fid->metadata_areas_ignored;
+
+       common_mda = _move_mdas(vg_from, vg_to,
+                               mdas_from_in_use, mdas_to_in_use);
+       common_mda = _move_mdas(vg_from, vg_to,
+                               mdas_from_ignored, mdas_to_ignored);
+
+       if ((dm_list_empty(mdas_from_in_use) &&
+            dm_list_empty(mdas_from_ignored)) ||
+           ((!is_orphan_vg(vg_to->name) &&
+             dm_list_empty(mdas_to_in_use) &&
+             dm_list_empty(mdas_to_ignored))))
+               return common_mda;
+
+       return 1;
+}
+
+static int _wipe_sb(struct device *dev, const char *type, const char *name,
+                   int wipe_len, struct pvcreate_params *pp,
+                   int (*func)(struct device *dev, uint64_t *signature))
+{
+       int wipe;
+       uint64_t superblock;
+
+       wipe = func(dev, &superblock);
+       if (wipe == -1) {
+               log_error("Fatal error while trying to detect %s on %s.",
+                         type, name);
+               return 0;
+       }
+
+       if (wipe == 0)
+               return 1;
+
+       /* Specifying --yes => do not ask. */
+       if (!pp->yes && (pp->force == PROMPT) &&
+           yes_no_prompt("WARNING: %s detected on %s. Wipe it? [y/n] ",
+                         type, name) != 'y') {
+               log_error("Aborting pvcreate on %s.", name);
+               return 0;
+       }
+
+       log_print("Wiping %s on %s.", type, name);
+       if (!dev_set(dev, superblock, wipe_len, 0)) {
+               log_error("Failed to wipe %s on %s.", type, name);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * See if we may pvcreate on this device.
+ * 0 indicates we may not.
+ */
+static int pvcreate_check(struct cmd_context *cmd, const char *name,
+                         struct pvcreate_params *pp)
+{
+       struct physical_volume *pv;
+       struct device *dev;
+       struct dm_list mdas;
+
+       dm_list_init(&mdas);
+
+       /* FIXME Check partition type is LVM unless --force is given */
+
+       /* Is there a pv here already? */
+       pv = pv_read(cmd, name, &mdas, NULL, 0, 0);
+
+       /*
+        * If a PV has no MDAs it may appear to be an orphan until the
+        * metadata is read off another PV in the same VG.  Detecting
+        * this means checking every VG by scanning every PV on the
+        * system.
+        */
+       if (pv && is_orphan(pv) && mdas_empty_or_ignored(&mdas)) {
+               if (!scan_vgs_for_pvs(cmd, 0))
+                       return_0;
+               pv = pv_read(cmd, name, NULL, NULL, 0, 0);
+       }
+
+       /* Allow partial & exported VGs to be destroyed. */
+       /* We must have -ff to overwrite a non orphan */
+       if (pv && !is_orphan(pv) && pp->force != DONT_PROMPT_OVERRIDE) {
+               log_error("Can't initialize physical volume \"%s\" of "
+                         "volume group \"%s\" without -ff", name, pv_vg_name(pv));
+               return 0;
+       }
+
+       /* prompt */
+       if (pv && !is_orphan(pv) && !pp->yes &&
+           yes_no_prompt(_really_init, name, pv_vg_name(pv)) == 'n') {
+               log_error("%s: physical volume not initialized", name);
+               return 0;
+       }
+
+       if (sigint_caught())
+               return 0;
+
+       dev = dev_cache_get(name, cmd->filter);
+
+       /* Is there an md superblock here? */
+       /* FIXME: still possible issues here - rescan cache? */
+       if (!dev && md_filtering()) {
+               refresh_filters(cmd);
+               init_md_filtering(0);
+               dev = dev_cache_get(name, cmd->filter);
+               init_md_filtering(1);
+       }
+
+       if (!dev) {
+               log_error("Device %s not found (or ignored by filtering).", name);
+               return 0;
+       }
+
+       /*
+        * This test will fail if the device belongs to an MD array.
+        */
+       if (!dev_test_excl(dev)) {
+               /* FIXME Detect whether device-mapper itself is still using it */
+               log_error("Can't open %s exclusively.  Mounted filesystem?",
+                         name);
+               return 0;
+       }
+
+       if (!_wipe_sb(dev, "software RAID md superblock", name, 4, pp, dev_is_md))
+               return 0;
+
+       if (!_wipe_sb(dev, "swap signature", name, 10, pp, dev_is_swap))
+               return 0;
+
+       if (!_wipe_sb(dev, "LUKS signature", name, 8, pp, dev_is_luks))
+               return 0;
+
+       if (sigint_caught())
+               return 0;
+
+       if (pv && !is_orphan(pv) && pp->force) {
+               log_warn("WARNING: Forcing physical volume creation on "
+                         "%s%s%s%s", name,
+                         !is_orphan(pv) ? " of volume group \"" : "",
+                         !is_orphan(pv) ? pv_vg_name(pv) : "",
+                         !is_orphan(pv) ? "\"" : "");
+       }
+
+       return 1;
+}
+
+void pvcreate_params_set_defaults(struct pvcreate_params *pp)
+{
+       memset(pp, 0, sizeof(*pp));
+       pp->zero = 1;
+       pp->size = 0;
+       pp->data_alignment = UINT64_C(0);
+       pp->data_alignment_offset = UINT64_C(0);
+       pp->pvmetadatacopies = DEFAULT_PVMETADATACOPIES;
+       pp->pvmetadatasize = DEFAULT_PVMETADATASIZE;
+       pp->labelsector = DEFAULT_LABELSECTOR;
+       pp->idp = 0;
+       pp->pe_start = 0;
+       pp->extent_count = 0;
+       pp->extent_size = 0;
+       pp->restorefile = 0;
+       pp->force = PROMPT;
+       pp->yes = 0;
+       pp->metadataignore = DEFAULT_PVMETADATAIGNORE;
+}
+
+/*
+ * pvcreate_single() - initialize a device with PV label and metadata area
+ *
+ * Parameters:
+ * - pv_name: device path to initialize
+ * - pp: parameters to pass to pv_create; if NULL, use default values
+ *
+ * Returns:
+ * NULL: error
+ * struct physical_volume * (non-NULL): handle to physical volume created
+ */
+struct physical_volume * pvcreate_single(struct cmd_context *cmd,
+                                        const char *pv_name,
+                                        struct pvcreate_params *pp)
+{
+       struct physical_volume *pv;
+       struct device *dev;
+       struct dm_list mdas;
+       struct pvcreate_params default_pp;
+       char buffer[64] __attribute__((aligned(8)));
+
+       pvcreate_params_set_defaults(&default_pp);
+       if (!pp)
+               pp = &default_pp;
+
+       if (pp->idp) {
+               if ((dev = device_from_pvid(cmd, pp->idp, NULL)) &&
+                   (dev != dev_cache_get(pv_name, cmd->filter))) {
+                       if (!id_write_format((const struct id*)&pp->idp->uuid,
+                           buffer, sizeof(buffer)))
+                               return_NULL;
+                       log_error("uuid %s already in use on \"%s\"", buffer,
+                                 dev_name(dev));
+                       return NULL;
+               }
+       }
+
+       if (!pvcreate_check(cmd, pv_name, pp))
+               goto error;
+
+       if (sigint_caught())
+               goto error;
+
+       if (!(dev = dev_cache_get(pv_name, cmd->filter))) {
+               log_error("%s: Couldn't find device.  Check your filters?",
+                         pv_name);
+               goto error;
+       }
+
+       dm_list_init(&mdas);
+       if (!(pv = pv_create(cmd, dev, pp->idp, pp->size,
+                            pp->data_alignment, pp->data_alignment_offset,
+                            pp->pe_start, pp->extent_count, pp->extent_size,
+                            pp->pvmetadatacopies, pp->pvmetadatasize,
+                            pp->metadataignore, &mdas))) {
+               log_error("Failed to setup physical volume \"%s\"", pv_name);
+               goto error;
+       }
+
+       log_verbose("Set up physical volume for \"%s\" with %" PRIu64
+                   " available sectors", pv_name, pv_size(pv));
+
+       /* Wipe existing label first */
+       if (!label_remove(pv_dev(pv))) {
+               log_error("Failed to wipe existing label on %s", pv_name);
+               goto error;
+       }
+
+       if (pp->zero) {
+               log_verbose("Zeroing start of device %s", pv_name);
+               if (!dev_open_quiet(dev)) {
+                       log_error("%s not opened: device not zeroed", pv_name);
+                       goto error;
+               }
+
+               if (!dev_set(dev, UINT64_C(0), (size_t) 2048, 0)) {
+                       log_error("%s not wiped: aborting", pv_name);
+                       dev_close(dev);
+                       goto error;
+               }
+               dev_close(dev);
+       }
+
+       log_very_verbose("Writing physical volume data to disk \"%s\"",
+                        pv_name);
+
+       if (!(pv_write(cmd, pv, &mdas, pp->labelsector))) {
+               log_error("Failed to write physical volume \"%s\"", pv_name);
+               goto error;
+       }
+
+       log_print("Physical volume \"%s\" successfully created", pv_name);
+
+       return pv;
+
+      error:
+       return NULL;
+}
+
+static void _free_pv(struct dm_pool *mem, struct physical_volume *pv)
+{
+       dm_pool_free(mem, pv);
+}
+
+static struct physical_volume *_alloc_pv(struct dm_pool *mem, struct device *dev)
+{
+       struct physical_volume *pv = dm_pool_zalloc(mem, sizeof(*pv));
+
+       if (!pv)
+               return_NULL;
+
+       pv->pe_size = 0;
+       pv->pe_start = 0;
+       pv->pe_count = 0;
+       pv->pe_alloc_count = 0;
+       pv->pe_align = 0;
+       pv->pe_align_offset = 0;
+       pv->fmt = NULL;
+       pv->dev = dev;
+
+       pv->status = ALLOCATABLE_PV;
+
+       dm_list_init(&pv->tags);
+       dm_list_init(&pv->segments);
+
+       return pv;
+}
+
+/**
+ * pv_create - initialize a physical volume for use with a volume group
+ *
+ * @fmt: format type
+ * @dev: PV device to initialize
+ * @size: size of the PV in sectors
+ * @data_alignment: requested alignment of data
+ * @data_alignment_offset: requested offset to aligned data
+ * @pe_start: physical extent start
+ * @existing_extent_count
+ * @existing_extent_size
+ * @pvmetadatacopies
+ * @pvmetadatasize
+ * @mdas
+ *
+ * Returns:
+ *   PV handle - physical volume initialized successfully
+ *   NULL - invalid parameter or problem initializing the physical volume
+ *
+ * Note:
+ *   FIXME: shorten argument list and replace with explict 'set' functions
+ */
+struct physical_volume *pv_create(const struct cmd_context *cmd,
+                                 struct device *dev,
+                                 struct id *id, uint64_t size,
+                                 unsigned long data_alignment,
+                                 unsigned long data_alignment_offset,
+                                 uint64_t pe_start,
+                                 uint32_t existing_extent_count,
+                                 uint32_t existing_extent_size,
+                                 int pvmetadatacopies, uint64_t pvmetadatasize,
+                                 unsigned metadataignore, struct dm_list *mdas)
+{
+       const struct format_type *fmt = cmd->fmt;
+       struct dm_pool *mem = fmt->cmd->mem;
+       struct physical_volume *pv = _alloc_pv(mem, dev);
+
+       if (!pv)
+               return NULL;
+
+       if (id)
+               memcpy(&pv->id, id, sizeof(*id));
+       else if (!id_create(&pv->id)) {
+               log_error("Failed to create random uuid for %s.",
+                         dev_name(dev));
+               goto bad;
+       }
+
+       if (!dev_get_size(pv->dev, &pv->size)) {
+               log_error("%s: Couldn't get size.", pv_dev_name(pv));
+               goto bad;
+       }
+
+       if (size) {
+               if (size > pv->size)
+                       log_warn("WARNING: %s: Overriding real size. "
+                                 "You could lose data.", pv_dev_name(pv));
+               log_verbose("%s: Pretending size is %" PRIu64 " sectors.",
+                           pv_dev_name(pv), size);
+               pv->size = size;
+       }
+
+       if (pv->size < PV_MIN_SIZE) {
+               log_error("%s: Size must exceed minimum of %ld sectors.",
+                         pv_dev_name(pv), PV_MIN_SIZE);
+               goto bad;
+       }
+
+       if (pv->size < data_alignment) {
+               log_error("%s: Data alignment must not exceed device size.",
+                         pv_dev_name(pv));
+               goto bad;
+       }
+
+       pv->fmt = fmt;
+       pv->vg_name = fmt->orphan_vg_name;
+
+       if (!fmt->ops->pv_setup(fmt, pe_start, existing_extent_count,
+                               existing_extent_size, data_alignment,
+                               data_alignment_offset,
+                               pvmetadatacopies, pvmetadatasize,
+                               metadataignore, mdas, pv, NULL)) {
+               log_error("%s: Format-specific setup of physical volume "
+                         "failed.", pv_dev_name(pv));
+               goto bad;
+       }
+
+       return pv;
+
+      bad:
+       _free_pv(mem, pv);
+       return NULL;
+}
+
+/* FIXME: liblvm todo - make into function that returns handle */
+struct pv_list *find_pv_in_vg(const struct volume_group *vg,
+                             const char *pv_name)
+{
+       return _find_pv_in_vg(vg, pv_name);
+}
+
+static struct pv_list *_find_pv_in_vg(const struct volume_group *vg,
+                                     const char *pv_name)
+{
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs)
+               if (pvl->pv->dev == dev_cache_get(pv_name, vg->cmd->filter))
+                       return pvl;
+
+       return NULL;
+}
+
+struct pv_list *find_pv_in_pv_list(const struct dm_list *pl,
+                                  const struct physical_volume *pv)
+{
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, pl)
+               if (pvl->pv == pv)
+                       return pvl;
+
+       return NULL;
+}
+
+int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv)
+{
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs)
+               if (pv == pvl->pv)
+                        return 1;
+
+       return 0;
+}
+
+static struct pv_list *_find_pv_in_vg_by_uuid(const struct volume_group *vg,
+                                             const struct id *id)
+{
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs)
+               if (id_equal(&pvl->pv->id, id))
+                       return pvl;
+
+       return NULL;
+}
+
+/**
+ * find_pv_in_vg_by_uuid - Find PV in VG by PV UUID
+ * @vg: volume group to search
+ * @id: UUID of the PV to match
+ *
+ * Returns:
+ *   struct pv_list within owning struct volume_group - if UUID of PV found in VG
+ *   NULL - invalid parameter or UUID of PV not found in VG
+ *
+ * Note
+ *   FIXME - liblvm todo - make into function that takes VG handle
+ */
+struct pv_list *find_pv_in_vg_by_uuid(const struct volume_group *vg,
+                                     const struct id *id)
+{
+       return _find_pv_in_vg_by_uuid(vg, id);
+}
+
+struct lv_list *find_lv_in_vg(const struct volume_group *vg,
+                             const char *lv_name)
+{
+       struct lv_list *lvl;
+       const char *ptr;
+
+       /* Use last component */
+       if ((ptr = strrchr(lv_name, '/')))
+               ptr++;
+       else
+               ptr = lv_name;
+
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (!strcmp(lvl->lv->name, ptr))
+                       return lvl;
+
+       return NULL;
+}
+
+struct lv_list *find_lv_in_lv_list(const struct dm_list *ll,
+                                  const struct logical_volume *lv)
+{
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, ll)
+               if (lvl->lv == lv)
+                       return lvl;
+
+       return NULL;
+}
+
+struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg,
+                                     const union lvid *lvid)
+{
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (!strncmp(lvl->lv->lvid.s, lvid->s, sizeof(*lvid)))
+                       return lvl;
+
+       return NULL;
+}
+
+struct logical_volume *find_lv(const struct volume_group *vg,
+                              const char *lv_name)
+{
+       struct lv_list *lvl = find_lv_in_vg(vg, lv_name);
+       return lvl ? lvl->lv : NULL;
+}
+
+struct physical_volume *find_pv(struct volume_group *vg, struct device *dev)
+{
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs)
+               if (dev == pvl->pv->dev)
+                       return pvl->pv;
+
+       return NULL;
+}
+
+/* FIXME: liblvm todo - make into function that returns handle */
+struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
+                                       const char *pv_name)
+{
+       return _find_pv_by_name(cmd, pv_name);
+}
+
+
+static struct physical_volume *_find_pv_by_name(struct cmd_context *cmd,
+                                               const char *pv_name)
+{
+       struct dm_list mdas;
+       struct physical_volume *pv;
+
+       dm_list_init(&mdas);
+       if (!(pv = _pv_read(cmd, cmd->mem, pv_name, &mdas, NULL, 1, 0))) {
+               log_error("Physical volume %s not found", pv_name);
+               return NULL;
+       }
+
+       if (is_orphan_vg(pv->vg_name) && mdas_empty_or_ignored(&mdas)) {
+               /* If a PV has no MDAs - need to search all VGs for it */
+               if (!scan_vgs_for_pvs(cmd, 1))
+                       return_NULL;
+               if (!(pv = _pv_read(cmd, cmd->mem, pv_name, NULL, NULL, 1, 0))) {
+                       log_error("Physical volume %s not found", pv_name);
+                       return NULL;
+               }
+       }
+
+       if (is_orphan_vg(pv->vg_name)) {
+               log_error("Physical volume %s not in a volume group", pv_name);
+               return NULL;
+       }
+
+       return pv;
+}
+
+/* Find segment at a given logical extent in an LV */
+struct lv_segment *find_seg_by_le(const struct logical_volume *lv, uint32_t le)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv->segments)
+               if (le >= seg->le && le < seg->le + seg->len)
+                       return seg;
+
+       return NULL;
+}
+
+struct lv_segment *first_seg(const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv->segments)
+               return seg;
+
+       return NULL;
+}
+
+int vg_remove_mdas(struct volume_group *vg)
+{
+       struct metadata_area *mda;
+
+       /* FIXME Improve recovery situation? */
+       /* Remove each copy of the metadata */
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+               if (mda->ops->vg_remove &&
+                   !mda->ops->vg_remove(vg->fid, vg, mda))
+                       return_0;
+       }
+
+       return 1;
+}
+
+/*
+ * Determine whether two vgs are compatible for merging.
+ */
+int vgs_are_compatible(struct cmd_context *cmd __attribute__((unused)),
+                      struct volume_group *vg_from,
+                      struct volume_group *vg_to)
+{
+       struct lv_list *lvl1, *lvl2;
+       struct pv_list *pvl;
+       char *name1, *name2;
+
+       if (lvs_in_vg_activated(vg_from)) {
+               log_error("Logical volumes in \"%s\" must be inactive",
+                         vg_from->name);
+               return 0;
+       }
+
+       /* Check compatibility */
+       if (vg_to->extent_size != vg_from->extent_size) {
+               log_error("Extent sizes differ: %d (%s) and %d (%s)",
+                         vg_to->extent_size, vg_to->name,
+                         vg_from->extent_size, vg_from->name);
+               return 0;
+       }
+
+       if (vg_to->max_pv &&
+           (vg_to->max_pv < vg_to->pv_count + vg_from->pv_count)) {
+               log_error("Maximum number of physical volumes (%d) exceeded "
+                         " for \"%s\" and \"%s\"", vg_to->max_pv, vg_to->name,
+                         vg_from->name);
+               return 0;
+       }
+
+       if (vg_to->max_lv &&
+           (vg_to->max_lv < vg_visible_lvs(vg_to) + vg_visible_lvs(vg_from))) {
+               log_error("Maximum number of logical volumes (%d) exceeded "
+                         " for \"%s\" and \"%s\"", vg_to->max_lv, vg_to->name,
+                         vg_from->name);
+               return 0;
+       }
+
+       /* Metadata types must be the same */
+       if (vg_to->fid->fmt != vg_from->fid->fmt) {
+               log_error("Metadata types differ for \"%s\" and \"%s\"",
+                         vg_to->name, vg_from->name);
+               return 0;
+       }
+
+       /* Clustering attribute must be the same */
+       if (vg_is_clustered(vg_to) != vg_is_clustered(vg_from)) {
+               log_error("Clustered attribute differs for \"%s\" and \"%s\"",
+                         vg_to->name, vg_from->name);
+               return 0;
+       }
+
+       /* Check no conflicts with LV names */
+       dm_list_iterate_items(lvl1, &vg_to->lvs) {
+               name1 = lvl1->lv->name;
+
+               dm_list_iterate_items(lvl2, &vg_from->lvs) {
+                       name2 = lvl2->lv->name;
+
+                       if (!strcmp(name1, name2)) {
+                               log_error("Duplicate logical volume "
+                                         "name \"%s\" "
+                                         "in \"%s\" and \"%s\"",
+                                         name1, vg_to->name, vg_from->name);
+                               return 0;
+                       }
+               }
+       }
+
+       /* Check no PVs are constructed from either VG */
+       dm_list_iterate_items(pvl, &vg_to->pvs) {
+               if (pv_uses_vg(pvl->pv, vg_from)) {
+                       log_error("Physical volume %s might be constructed "
+                                 "from same volume group %s.",
+                                 pv_dev_name(pvl->pv), vg_from->name);
+                       return 0;
+               }
+       }
+
+       dm_list_iterate_items(pvl, &vg_from->pvs) {
+               if (pv_uses_vg(pvl->pv, vg_to)) {
+                       log_error("Physical volume %s might be constructed "
+                                 "from same volume group %s.",
+                                 pv_dev_name(pvl->pv), vg_to->name);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+struct _lv_postorder_baton {
+       int (*fn)(struct logical_volume *lv, void *data);
+       void *data;
+};
+
+static int _lv_postorder_visit(struct logical_volume *,
+                              int (*fn)(struct logical_volume *lv, void *data),
+                              void *data);
+
+static int _lv_postorder_level(struct logical_volume *lv, void *data)
+{
+       struct _lv_postorder_baton *baton = data;
+       if (lv->status & POSTORDER_OPEN_FLAG)
+               return 1; // a data structure loop has closed...
+       lv->status |= POSTORDER_OPEN_FLAG;
+       int r =_lv_postorder_visit(lv, baton->fn, baton->data);
+       lv->status &= ~POSTORDER_OPEN_FLAG;
+       lv->status |= POSTORDER_FLAG;
+       return r;
+};
+
+static int _lv_each_dependency(struct logical_volume *lv,
+                              int (*fn)(struct logical_volume *lv, void *data),
+                              void *data)
+{
+       int i, s;
+       struct lv_segment *lvseg;
+
+       struct logical_volume *deps[] = {
+               (lv->rdevice && lv != lv->rdevice->lv) ? lv->rdevice->lv : 0,
+               (lv->rdevice && lv != lv->rdevice->slog) ? lv->rdevice->slog : 0,
+               lv->snapshot ? lv->snapshot->origin : 0,
+               lv->snapshot ? lv->snapshot->cow : 0 };
+       for (i = 0; i < sizeof(deps) / sizeof(*deps); ++i) {
+               if (deps[i] && !fn(deps[i], data))
+                       return_0;
+       }
+
+       dm_list_iterate_items(lvseg, &lv->segments) {
+               if (lvseg->log_lv && !fn(lvseg->log_lv, data))
+                       return_0;
+               if (lvseg->rlog_lv && !fn(lvseg->rlog_lv, data))
+                       return_0;
+               for (s = 0; s < lvseg->area_count; ++s) {
+                       if (seg_type(lvseg, s) == AREA_LV && !fn(seg_lv(lvseg,s), data))
+                               return_0;
+               }
+       }
+       return 1;
+}
+
+static int _lv_postorder_cleanup(struct logical_volume *lv, void *data)
+{
+       if (!(lv->status & POSTORDER_FLAG))
+               return 1;
+       lv->status &= ~POSTORDER_FLAG;
+
+       if (!_lv_each_dependency(lv, _lv_postorder_cleanup, data))
+               return_0;
+       return 1;
+}
+
+static int _lv_postorder_visit(struct logical_volume *lv,
+                              int (*fn)(struct logical_volume *lv, void *data),
+                              void *data)
+{
+       struct _lv_postorder_baton baton;
+       int r;
+
+       if (lv->status & POSTORDER_FLAG)
+               return 1;
+
+       baton.fn = fn;
+       baton.data = data;
+       r = _lv_each_dependency(lv, _lv_postorder_level, &baton);
+       if (r)
+               r = fn(lv, data);
+
+       return r;
+}
+
+/*
+ * This will walk the LV dependency graph in depth-first order and in the
+ * postorder, call a callback function "fn". The void *data is passed along all
+ * the calls. The callback may return zero to indicate an error and terminate
+ * the depth-first walk. The error is propagated to return value of
+ * _lv_postorder.
+ */
+static int _lv_postorder(struct logical_volume *lv,
+                              int (*fn)(struct logical_volume *lv, void *data),
+                              void *data)
+{
+       int r;
+       r = _lv_postorder_visit(lv, fn, data);
+       _lv_postorder_cleanup(lv, 0);
+       return r;
+}
+
+struct _lv_mark_if_partial_baton {
+       int partial;
+};
+
+static int _lv_mark_if_partial_collect(struct logical_volume *lv, void *data)
+{
+       struct _lv_mark_if_partial_baton *baton = data;
+       if (lv->status & PARTIAL_LV)
+               baton->partial = 1;
+
+       return 1;
+}
+
+static int _lv_mark_if_partial_single(struct logical_volume *lv, void *data)
+{
+       int s;
+       struct _lv_mark_if_partial_baton baton;
+       struct lv_segment *lvseg;
+
+       dm_list_iterate_items(lvseg, &lv->segments) {
+               for (s = 0; s < lvseg->area_count; ++s) {
+                       if (seg_type(lvseg, s) == AREA_PV) {
+                               if (is_missing_pv(seg_pv(lvseg, s)))
+                                       lv->status |= PARTIAL_LV;
+                       }
+               }
+       }
+
+       baton.partial = 0;
+       _lv_each_dependency(lv, _lv_mark_if_partial_collect, &baton);
+
+       if (baton.partial)
+               lv->status |= PARTIAL_LV;
+
+       return 1;
+}
+
+static int _lv_mark_if_partial(struct logical_volume *lv)
+{
+       return _lv_postorder(lv, _lv_mark_if_partial_single, NULL);
+}
+
+/*
+ * Mark LVs with missing PVs using PARTIAL_LV status flag. The flag is
+ * propagated transitively, so LVs referencing other LVs are marked
+ * partial as well, if any of their referenced LVs are marked partial.
+ */
+int vg_mark_partial_lvs(struct volume_group *vg)
+{
+       struct logical_volume *lv;
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv = lvl->lv;
+               if (!_lv_mark_if_partial(lv))
+                       return_0;
+       }
+       return 1;
+}
+
+/*
+ * Be sure that all PV devices have cached read ahead in dev-cache
+ * Currently it takes read_ahead from first PV segment only
+ */
+static int _lv_read_ahead_single(struct logical_volume *lv, void *data)
+{
+       struct lv_segment *seg = first_seg(lv);
+       uint32_t seg_read_ahead = 0, *read_ahead = data;
+
+       if (seg && seg->area_count && seg_type(seg, 0) == AREA_PV)
+               dev_get_read_ahead(seg_pv(seg, 0)->dev, &seg_read_ahead);
+
+       if (seg_read_ahead > *read_ahead)
+               *read_ahead = seg_read_ahead;
+
+       return 1;
+}
+
+/*
+ * Calculate readahead for logical volume from underlying PV devices.
+ * If read_ahead is NULL, only ensure that readahead of PVs are preloaded
+ * into PV struct device in dev cache.
+ */
+void lv_calculate_readahead(const struct logical_volume *lv, uint32_t *read_ahead)
+{
+       uint32_t _read_ahead = 0;
+
+       if (lv->read_ahead == DM_READ_AHEAD_AUTO)
+               _lv_postorder((struct logical_volume *)lv, _lv_read_ahead_single, &_read_ahead);
+
+       if (read_ahead) {
+               log_debug("Calculated readahead of LV %s is %u", lv->name, _read_ahead);
+               *read_ahead = _read_ahead;
+       }
+}
+
+/*
+ * Check that an LV and all its PV references are correctly listed in vg->lvs
+ * and vg->pvs, respectively. This only looks at a single LV, but *not* at the
+ * LVs it is using. To do the latter, you should use _lv_postorder with this
+ * function. C.f. vg_validate.
+ */
+static int _lv_validate_references_single(struct logical_volume *lv, void *data)
+{
+       struct volume_group *vg = lv->vg;
+       struct lv_segment *lvseg;
+       struct pv_list *pvl;
+       struct lv_list *lvl;
+       int s;
+       int r = 1;
+       int ok = 0;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (lvl->lv == lv) {
+                       ok = 1;
+                       break;
+               }
+       }
+
+       if (!ok) {
+               log_error(INTERNAL_ERROR
+                         "Referenced LV %s not listed in VG %s.",
+                         lv->name, vg->name);
+               r = 0;
+       }
+
+       dm_list_iterate_items(lvseg, &lv->segments) {
+               for (s = 0; s < lvseg->area_count; ++s) {
+                       if (seg_type(lvseg, s) == AREA_PV) {
+                               ok = 0;
+                               /* look up the reference in vg->pvs */
+                               dm_list_iterate_items(pvl, &vg->pvs) {
+                                       if (pvl->pv == seg_pv(lvseg, s)) {
+                                               ok = 1;
+                                               break;
+                                       }
+                               }
+
+                               if (!ok) {
+                                       log_error(INTERNAL_ERROR
+                                                 "Referenced PV %s not listed in VG %s.",
+                                                 pv_dev_name(seg_pv(lvseg, s)), vg->name);
+                                       r = 0;
+                               }
+                       }
+               }
+       }
+
+       return r;
+}
+
+int vg_validate(struct volume_group *vg)
+{
+       struct pv_list *pvl, *pvl2;
+       struct lv_list *lvl, *lvl2;
+       struct lv_segment *seg;
+       char uuid[64] __attribute__((aligned(8)));
+       int r = 1;
+       uint32_t hidden_lv_count = 0, lv_count = 0, lv_visible_count = 0;
+       uint32_t pv_count = 0;
+       uint32_t num_snapshots = 0;
+       uint32_t loop_counter1, loop_counter2;
+
+       if (vg->alloc == ALLOC_CLING_BY_TAGS) {
+               log_error(INTERNAL_ERROR "VG %s allocation policy set to invalid cling_by_tags.",
+                         vg->name);
+               r = 0;
+       }
+
+       /* FIXME Also check there's no data/metadata overlap */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (++pv_count > vg->pv_count) {
+                       log_error(INTERNAL_ERROR "PV list corruption detected in VG %s.", vg->name);
+                       /* FIXME Dump list structure? */
+                       r = 0;
+               }
+               if (pvl->pv->vg != vg) {
+                       log_error(INTERNAL_ERROR "VG %s PV list entry points "
+                                 "to different VG %s", vg->name,
+                                 pvl->pv->vg ? pvl->pv->vg->name : "NULL");
+                       r = 0;
+               }
+       }
+
+       loop_counter1 = loop_counter2 = 0;
+       /* FIXME Use temp hash table instead? */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (++loop_counter1 > pv_count)
+                       break;
+               dm_list_iterate_items(pvl2, &vg->pvs) {
+                       if (++loop_counter2 > pv_count)
+                               break;
+                       if (pvl == pvl2)
+                               break;
+                       if (id_equal(&pvl->pv->id,
+                                    &pvl2->pv->id)) {
+                               if (!id_write_format(&pvl->pv->id, uuid,
+                                                    sizeof(uuid)))
+                                        stack;
+                               log_error(INTERNAL_ERROR "Duplicate PV id "
+                                         "%s detected for %s in %s.",
+                                         uuid, pv_dev_name(pvl->pv),
+                                         vg->name);
+                               r = 0;
+                       }
+               }
+
+               if (strcmp(pvl->pv->vg_name, vg->name)) {
+                       log_error(INTERNAL_ERROR "VG name for PV %s is corrupted.",
+                                 pv_dev_name(pvl->pv));
+                       r = 0;
+               }
+       }
+
+       if (!check_pv_segments(vg)) {
+               log_error(INTERNAL_ERROR "PV segments corrupted in %s.",
+                         vg->name);
+               r = 0;
+       }
+
+       /*
+        * Count all non-snapshot invisible LVs
+        */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv_count++;
+
+               if (lv_is_cow(lvl->lv))
+                       num_snapshots++;
+
+               if (lv_is_visible(lvl->lv))
+                       lv_visible_count++;
+
+               if (!check_lv_segments(lvl->lv, 0)) {
+                       log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+                                 lvl->lv->name);
+                       r = 0;
+               }
+
+               if (lvl->lv->alloc == ALLOC_CLING_BY_TAGS) {
+                       log_error(INTERNAL_ERROR "LV %s allocation policy set to invalid cling_by_tags.",
+                                 lvl->lv->name);
+                       r = 0;
+               }
+
+               if (lvl->lv->status & VISIBLE_LV)
+                       continue;
+
+               /* snapshots */
+               if (lv_is_cow(lvl->lv))
+                       continue;
+
+               /* virtual origins are always hidden */
+               if (lv_is_origin(lvl->lv) && !lv_is_virtual_origin(lvl->lv))
+                       continue;
+
+               /* count other non-snapshot invisible volumes */
+               hidden_lv_count++;
+
+               /*
+                *  FIXME: add check for unreferenced invisible LVs
+                *   - snapshot cow & origin
+                *   - mirror log & images
+                *   - mirror conversion volumes (_mimagetmp*)
+                */
+       }
+
+       /*
+        * all volumes = visible LVs + snapshot_cows + invisible LVs
+        */
+       if (lv_count != lv_visible_count + num_snapshots + hidden_lv_count) {
+               log_error(INTERNAL_ERROR "#internal LVs (%u) != #LVs (%"
+                         PRIu32 ") + #snapshots (%" PRIu32 ") + #internal LVs (%u) in VG %s",
+                         lv_count, lv_visible_count,
+                         num_snapshots, hidden_lv_count, vg->name);
+               r = 0;
+       }
+
+       /* Avoid endless loop if lv->segments list is corrupt */
+       if (!r)
+               return r;
+
+       loop_counter1 = loop_counter2 = 0;
+       /* FIXME Use temp hash table instead? */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (++loop_counter1 > lv_count)
+                       break;
+               dm_list_iterate_items(lvl2, &vg->lvs) {
+                       if (++loop_counter2 > lv_count)
+                               break;
+                       if (lvl == lvl2)
+                               break;
+                       if (!strcmp(lvl->lv->name, lvl2->lv->name)) {
+                               log_error(INTERNAL_ERROR "Duplicate LV name "
+                                         "%s detected in %s.", lvl->lv->name,
+                                         vg->name);
+                               r = 0;
+                       }
+                       if (id_equal(&lvl->lv->lvid.id[1],
+                                    &lvl2->lv->lvid.id[1])) {
+                               if (!id_write_format(&lvl->lv->lvid.id[1], uuid,
+                                                    sizeof(uuid)))
+                                        stack;
+                               log_error(INTERNAL_ERROR "Duplicate LV id "
+                                         "%s detected for %s and %s in %s.",
+                                         uuid, lvl->lv->name, lvl2->lv->name,
+                                         vg->name);
+                               r = 0;
+                       }
+               }
+
+               if (!check_lv_segments(lvl->lv, 1)) {
+                       log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+                                 lvl->lv->name);
+                       r = 0;
+               }
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (!_lv_postorder(lvl->lv, _lv_validate_references_single, NULL))
+                       r = 0;
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (!(lvl->lv->status & PVMOVE))
+                       continue;
+               dm_list_iterate_items(seg, &lvl->lv->segments) {
+                       if (seg_is_mirrored(seg)) {
+                               if (seg->area_count != 2) {
+                                       log_error(INTERNAL_ERROR
+                                                 "Segment %d in %s is not 2-way.",
+                                                 loop_counter1, lvl->lv->name);
+                                       r = 0;
+                               }
+                       } else if (seg->area_count != 1) {
+                               log_error(INTERNAL_ERROR
+                                         "Segment %d in %s has wrong number of areas: %d.",
+                                         loop_counter1, lvl->lv->name, seg->area_count);
+                               r = 0;
+                       }
+               }
+       }
+
+       if (!(vg->fid->fmt->features & FMT_UNLIMITED_VOLS) &&
+           (!vg->max_lv || !vg->max_pv)) {
+               log_error(INTERNAL_ERROR "Volume group %s has limited PV/LV count"
+                         " but limit is not set.", vg->name);
+               r = 0;
+       }
+
+       if (vg_max_lv_reached(vg))
+               stack;
+
+       return r;
+}
+
+/*
+ * After vg_write() returns success,
+ * caller MUST call either vg_commit() or vg_revert()
+ */
+int vg_write(struct volume_group *vg)
+{
+       struct dm_list *mdah;
+       struct metadata_area *mda;
+
+       if (!vg_validate(vg))
+               return_0;
+
+       if (vg->status & PARTIAL_VG) {
+               log_error("Cannot update partial volume group %s.", vg->name);
+               return 0;
+       }
+
+       if (vg_missing_pv_count(vg) && !vg->cmd->handles_missing_pvs) {
+               log_error("Cannot update volume group %s while physical "
+                         "volumes are missing.", vg->name);
+               return 0;
+       }
+
+       if (vg_has_unknown_segments(vg) && !vg->cmd->handles_unknown_segments) {
+               log_error("Cannot update volume group %s with unknown segments in it!",
+                         vg->name);
+               return 0;
+       }
+
+       if ((vg->fid->fmt->features & FMT_MDAS) && !_vg_adjust_ignored_mdas(vg))
+               return_0;
+
+       if (!vg_mda_used_count(vg)) {
+               log_error("Aborting vg_write: No metadata areas to write to!");
+               return 0;
+       }
+
+       if (!drop_cached_metadata(vg)) {
+               log_error("Unable to drop cached metadata for VG %s.", vg->name);
+               return 0;
+       }
+
+       vg->seqno++;
+
+       /* Write to each copy of the metadata area */
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+               if (!mda->ops->vg_write) {
+                       log_error("Format does not support writing volume"
+                                 "group metadata areas");
+                       /* Revert */
+                       dm_list_uniterate(mdah, &vg->fid->metadata_areas_in_use, &mda->list) {
+                               mda = dm_list_item(mdah, struct metadata_area);
+
+                               if (mda->ops->vg_revert &&
+                                   !mda->ops->vg_revert(vg->fid, vg, mda)) {
+                                       stack;
+                               }
+                       }
+                       return 0;
+               }
+               if (!mda->ops->vg_write(vg->fid, vg, mda)) {
+                       stack;
+                       /* Revert */
+                       dm_list_uniterate(mdah, &vg->fid->metadata_areas_in_use, &mda->list) {
+                               mda = dm_list_item(mdah, struct metadata_area);
+
+                               if (mda->ops->vg_revert &&
+                                   !mda->ops->vg_revert(vg->fid, vg, mda)) {
+                                       stack;
+                               }
+                       }
+                       return 0;
+               }
+       }
+
+       /* Now pre-commit each copy of the new metadata */
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+               if (mda->ops->vg_precommit &&
+                   !mda->ops->vg_precommit(vg->fid, vg, mda)) {
+                       stack;
+                       /* Revert */
+                       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+                               if (mda->ops->vg_revert &&
+                                   !mda->ops->vg_revert(vg->fid, vg, mda)) {
+                                       stack;
+                               }
+                       }
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _vg_commit_mdas(struct volume_group *vg)
+{
+       struct metadata_area *mda, *tmda;
+       struct dm_list ignored;
+       int failed = 0;
+       int cache_updated = 0;
+
+       /* Rearrange the metadata_areas_in_use so ignored mdas come first. */
+       dm_list_init(&ignored);
+       dm_list_iterate_items_safe(mda, tmda, &vg->fid->metadata_areas_in_use)
+               if (mda_is_ignored(mda))
+                       dm_list_move(&ignored, &mda->list);
+
+       dm_list_iterate_items_safe(mda, tmda, &ignored)
+               dm_list_move(&vg->fid->metadata_areas_in_use, &mda->list);
+
+       /* Commit to each copy of the metadata area */
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+               failed = 0;
+               if (mda->ops->vg_commit &&
+                   !mda->ops->vg_commit(vg->fid, vg, mda)) {
+                       stack;
+                       failed = 1;
+               }
+               /* Update cache first time we succeed */
+               if (!failed && !cache_updated) {
+                       lvmcache_update_vg(vg, 0);
+                       cache_updated = 1;
+               }
+       }
+       return cache_updated;
+}
+
+/* Commit pending changes */
+int vg_commit(struct volume_group *vg)
+{
+       int cache_updated = 0;
+
+       if (!vgname_is_locked(vg->name)) {
+               log_error(INTERNAL_ERROR "Attempt to write new VG metadata "
+                         "without locking %s", vg->name);
+               return cache_updated;
+       }
+
+       cache_updated = _vg_commit_mdas(vg);
+
+       if (cache_updated) {
+               /* Instruct remote nodes to upgrade cached metadata. */
+               remote_commit_cached_metadata(vg);
+               /*
+                * We need to clear old_name after a successful commit.
+                * The volume_group structure could be reused later.
+                */
+               vg->old_name = NULL;
+       }
+
+       /* If update failed, remove any cached precommitted metadata. */
+       if (!cache_updated && !drop_cached_metadata(vg))
+               log_error("Attempt to drop cached metadata failed "
+                         "after commit for VG %s.", vg->name);
+
+       /* If at least one mda commit succeeded, it was committed */
+       return cache_updated;
+}
+
+/* Don't commit any pending changes */
+int vg_revert(struct volume_group *vg)
+{
+       struct metadata_area *mda;
+
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+               if (mda->ops->vg_revert &&
+                   !mda->ops->vg_revert(vg->fid, vg, mda)) {
+                       stack;
+               }
+       }
+
+       if (!drop_cached_metadata(vg))
+               log_error("Attempt to drop cached metadata failed "
+                         "after reverted update for VG %s.", vg->name);
+
+       remote_revert_cached_metadata(vg);
+
+       return 1;
+}
+
+/* Make orphan PVs look like a VG */
+static struct volume_group *_vg_read_orphans(struct cmd_context *cmd,
+                                            int warnings,
+                                            const char *orphan_vgname)
+{
+       struct lvmcache_vginfo *vginfo;
+       struct lvmcache_info *info;
+       struct pv_list *pvl;
+       struct volume_group *vg;
+       struct physical_volume *pv;
+       struct dm_pool *mem;
+
+       lvmcache_label_scan(cmd, 0);
+
+       if (!(vginfo = vginfo_from_vgname(orphan_vgname, NULL)))
+               return_NULL;
+
+       if (!(mem = dm_pool_create("vg_read orphan", VG_MEMPOOL_CHUNK)))
+               return_NULL;
+
+       if (!(vg = dm_pool_zalloc(mem, sizeof(*vg)))) {
+               log_error("vg allocation failed");
+               goto bad;
+       }
+       dm_list_init(&vg->pvs);
+       dm_list_init(&vg->lvs);
+       dm_list_init(&vg->tags);
+       dm_list_init(&vg->removed_pvs);
+       vg->vgmem = mem;
+       vg->cmd = cmd;
+       if (!(vg->name = dm_pool_strdup(mem, orphan_vgname))) {
+               log_error("vg name allocation failed");
+               goto bad;
+       }
+
+       /* create format instance with appropriate metadata area */
+       if (!(vg->fid = vginfo->fmt->ops->create_instance(vginfo->fmt,
+                                                         orphan_vgname, NULL,
+                                                         NULL))) {
+               log_error("Failed to create format instance");
+               goto bad;
+       }
+
+       dm_list_iterate_items(info, &vginfo->infos) {
+               if (!(pv = _pv_read(cmd, mem, dev_name(info->dev), NULL, NULL, warnings, 0))) {
+                       continue;
+               }
+               if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
+                       log_error("pv_list allocation failed");
+                       goto bad;
+               }
+               pvl->pv = pv;
+               add_pvl_to_vgs(vg, pvl);
+       }
+
+       return vg;
+bad:
+       dm_pool_destroy(mem);
+       return NULL;
+}
+
+static int _update_pv_list(struct dm_pool *pvmem, struct dm_list *all_pvs, struct volume_group *vg)
+{
+       struct pv_list *pvl, *pvl2;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               dm_list_iterate_items(pvl2, all_pvs) {
+                       if (pvl->pv->dev == pvl2->pv->dev)
+                               goto next_pv;
+               }
+
+               /*
+                * PV is not on list so add it.
+                */
+               if (!(pvl2 = _copy_pvl(pvmem, pvl))) {
+                       log_error("pv_list allocation for '%s' failed",
+                                 pv_dev_name(pvl->pv));
+                       return 0;
+               }
+               dm_list_add(all_pvs, &pvl2->list);
+  next_pv:
+               ;
+       }
+
+       return 1;
+}
+
+int vg_missing_pv_count(const struct volume_group *vg)
+{
+       int ret = 0;
+       struct pv_list *pvl;
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (is_missing_pv(pvl->pv))
+                       ++ ret;
+       }
+       return ret;
+}
+
+static void check_reappeared_pv(struct volume_group *correct_vg,
+                               struct physical_volume *pv)
+{
+       struct pv_list *pvl;
+
+        /*
+         * Skip these checks in case the tool is going to deal with missing
+         * PVs, especially since the resulting messages can be pretty
+         * confusing.
+         */
+        if (correct_vg->cmd->handles_missing_pvs)
+            return;
+
+       dm_list_iterate_items(pvl, &correct_vg->pvs)
+               if (pv->dev == pvl->pv->dev && is_missing_pv(pvl->pv)) {
+                       log_warn("Missing device %s reappeared, updating "
+                                "metadata for VG %s to version %u.",
+                                pv_dev_name(pvl->pv),  pv_vg_name(pvl->pv), 
+                                correct_vg->seqno);
+                       if (pvl->pv->pe_alloc_count == 0) {
+                               pv->status &= ~MISSING_PV;
+                               pvl->pv->status &= ~MISSING_PV;
+                       } else
+                               log_warn("Device still marked missing because of allocated data "
+                                        "on it, remove volumes and consider vgreduce --removemissing.");
+               }
+}
+/* Caller sets consistent to 1 if it's safe for vg_read_internal to correct
+ * inconsistent metadata on disk (i.e. the VG write lock is held).
+ * This guarantees only consistent metadata is returned.
+ * If consistent is 0, caller must check whether consistent == 1 on return
+ * and take appropriate action if it isn't (e.g. abort; get write lock
+ * and call vg_read_internal again).
+ *
+ * If precommitted is set, use precommitted metadata if present.
+ *
+ * Either of vgname or vgid may be NULL.
+ */
+static struct volume_group *_vg_read(struct cmd_context *cmd,
+                                    const char *vgname,
+                                    const char *vgid,
+                                    int warnings, 
+                                    int *consistent, unsigned precommitted)
+{
+       struct format_instance *fid;
+       const struct format_type *fmt;
+       struct volume_group *vg, *correct_vg = NULL;
+       struct metadata_area *mda;
+       struct lvmcache_info *info;
+       int inconsistent = 0;
+       int inconsistent_vgid = 0;
+       int inconsistent_pvs = 0;
+       int inconsistent_seqno = 0;
+       int inconsistent_mdas = 0;
+       unsigned use_precommitted = precommitted;
+       unsigned saved_handles_missing_pvs = cmd->handles_missing_pvs;
+       struct dm_list *pvids;
+       struct pv_list *pvl, *pvl2;
+       struct dm_list all_pvs;
+       char uuid[64] __attribute__((aligned(8)));
+
+       if (is_orphan_vg(vgname)) {
+               if (use_precommitted) {
+                       log_error(INTERNAL_ERROR "vg_read_internal requires vgname "
+                                 "with pre-commit.");
+                       return NULL;
+               }
+               *consistent = 1;
+               return _vg_read_orphans(cmd, warnings, vgname);
+       }
+
+       /*
+        * If cached metadata was inconsistent and *consistent is set
+        * then repair it now.  Otherwise just return it.
+        * Also return if use_precommitted is set due to the FIXME in
+        * the missing PV logic below.
+        */
+       if ((correct_vg = lvmcache_get_vg(vgid, precommitted)) &&
+           (use_precommitted || !*consistent || !(correct_vg->status & INCONSISTENT_VG))) {
+               if (!(correct_vg->status & INCONSISTENT_VG))
+                       *consistent = 1;
+               else    /* Inconsistent but we can't repair it */
+                       correct_vg->status &= ~INCONSISTENT_VG;
+
+               if (vg_missing_pv_count(correct_vg)) {
+                       log_verbose("There are %d physical volumes missing.",
+                                   vg_missing_pv_count(correct_vg));
+                       vg_mark_partial_lvs(correct_vg);
+               }
+               return correct_vg;
+       } else {
+               free_vg(correct_vg);
+               correct_vg = NULL;
+       }
+
+       /* Find the vgname in the cache */
+       /* If it's not there we must do full scan to be completely sure */
+       if (!(fmt = fmt_from_vgname(vgname, vgid, 1))) {
+               lvmcache_label_scan(cmd, 0);
+               if (!(fmt = fmt_from_vgname(vgname, vgid, 1))) {
+                       /* Independent MDAs aren't supported under low memory */
+                       if (!cmd->independent_metadata_areas && memlock())
+                               return_NULL;
+                       lvmcache_label_scan(cmd, 2);
+                       if (!(fmt = fmt_from_vgname(vgname, vgid, 0)))
+                               return_NULL;
+               }
+       }
+
+       /* Now determine the correct vgname if none was supplied */
+       if (!vgname && !(vgname = vgname_from_vgid(cmd->mem, vgid)))
+               return_NULL;
+
+       if (use_precommitted && !(fmt->features & FMT_PRECOMMIT))
+               use_precommitted = 0;
+
+       /* create format instance with appropriate metadata area */
+       if (!(fid = fmt->ops->create_instance(fmt, vgname, vgid, NULL))) {
+               log_error("Failed to create format instance");
+               return NULL;
+       }
+
+       /* Store pvids for later so we can check if any are missing */
+       if (!(pvids = lvmcache_get_pvids(cmd, vgname, vgid)))
+               return_NULL;
+
+       /* Ensure contents of all metadata areas match - else do recovery */
+       dm_list_iterate_items(mda, &fid->metadata_areas_in_use) {
+               if ((use_precommitted &&
+                    !(vg = mda->ops->vg_read_precommit(fid, vgname, mda))) ||
+                   (!use_precommitted &&
+                    !(vg = mda->ops->vg_read(fid, vgname, mda)))) {
+                       inconsistent = 1;
+                       free_vg(vg);
+                       continue;
+               }
+               if (!correct_vg) {
+                       correct_vg = vg;
+                       continue;
+               }
+
+               /* FIXME Also ensure contents same - checksum compare? */
+               if (correct_vg->seqno != vg->seqno) {
+                       if (cmd->metadata_read_only)
+                               log_very_verbose("Not repairing VG %s metadata seqno (%d != %d) "
+                                                 "as global/metadata_read_only is set.",
+                                                 vgname, vg->seqno, correct_vg->seqno);
+                       else {
+                               inconsistent = 1;
+                               inconsistent_seqno = 1;
+                       }
+                       if (vg->seqno > correct_vg->seqno) {
+                               free_vg(correct_vg);
+                               correct_vg = vg;
+                       }
+               }
+
+               if (vg != correct_vg)
+                       free_vg(vg);
+       }
+
+       /* Ensure every PV in the VG was in the cache */
+       if (correct_vg) {
+               /*
+                * If the VG has PVs without mdas, or ignored mdas, they may
+                * still be orphans in the cache: update the cache state here,
+                * and update the metadata lists in the vg.
+                */
+               if (!inconsistent &&
+                   dm_list_size(&correct_vg->pvs) > dm_list_size(pvids)) {
+                       dm_list_iterate_items(pvl, &correct_vg->pvs) {
+                               if (!pvl->pv->dev) {
+                                       inconsistent_pvs = 1;
+                                       break;
+                               }
+
+                               if (str_list_match_item(pvids, pvl->pv->dev->pvid))
+                                       continue;
+
+                               /*
+                                * PV not marked as belonging to this VG in cache.
+                                * Check it's an orphan without metadata area
+                                * not ignored.
+                                */
+                               if (!(info = info_from_pvid(pvl->pv->dev->pvid, 1)) ||
+                                  !info->vginfo || !is_orphan_vg(info->vginfo->vgname)) {
+                                       inconsistent_pvs = 1;
+                                       break;
+                               }
+                               if (dm_list_size(&info->mdas)) {
+                                       if (!fid_add_mdas(fid, &info->mdas))
+                                               return_NULL;
+                                        
+                                       log_debug("Empty mda found for VG %s.", vgname);
+
+                                       if (inconsistent_mdas)
+                                               continue;
+
+                                       /*
+                                        * If any newly-added mdas are in-use then their
+                                        * metadata needs updating.
+                                        */
+                                       dm_list_iterate_items(mda, &info->mdas)
+                                               if (!mda_is_ignored(mda)) {
+                                                       inconsistent_mdas = 1;
+                                                       break;
+                                               }
+                               }
+                       }
+
+                       /* If the check passed, let's update VG and recalculate pvids */
+                       if (!inconsistent_pvs) {
+                               log_debug("Updating cache for PVs without mdas "
+                                         "in VG %s.", vgname);
+                               /*
+                                * If there is no precommitted metadata, committed metadata
+                                * is read and stored in the cache even if use_precommitted is set
+                                */
+                               lvmcache_update_vg(correct_vg, correct_vg->status & PRECOMMITTED);
+
+                               if (!(pvids = lvmcache_get_pvids(cmd, vgname, vgid)))
+                                       return_NULL;
+                       }
+               }
+
+               if (dm_list_size(&correct_vg->pvs) !=
+                   dm_list_size(pvids) + vg_missing_pv_count(correct_vg)) {
+                       log_debug("Cached VG %s had incorrect PV list",
+                                 vgname);
+
+                       if (memlock())
+                               inconsistent = 1;
+                       else {
+                               free_vg(correct_vg);
+                               correct_vg = NULL;
+                       }
+               } else dm_list_iterate_items(pvl, &correct_vg->pvs) {
+                       if (is_missing_pv(pvl->pv))
+                               continue;
+                       if (!str_list_match_item(pvids, pvl->pv->dev->pvid)) {
+                               log_debug("Cached VG %s had incorrect PV list",
+                                         vgname);
+                               free_vg(correct_vg);
+                               correct_vg = NULL;
+                               break;
+                       }
+               }
+
+               if (correct_vg && inconsistent_mdas) {
+                       free_vg(correct_vg);
+                       correct_vg = NULL;
+               }
+       }
+
+       dm_list_init(&all_pvs);
+
+       /* Failed to find VG where we expected it - full scan and retry */
+       if (!correct_vg) {
+               inconsistent = 0;
+
+               /* Independent MDAs aren't supported under low memory */
+               if (!cmd->independent_metadata_areas && memlock())
+                       return_NULL;
+               lvmcache_label_scan(cmd, 2);
+               if (!(fmt = fmt_from_vgname(vgname, vgid, 0)))
+                       return_NULL;
+
+               if (precommitted && !(fmt->features & FMT_PRECOMMIT))
+                       use_precommitted = 0;
+
+               /* create format instance with appropriate metadata area */
+               if (!(fid = fmt->ops->create_instance(fmt, vgname, vgid, NULL))) {
+                       log_error("Failed to create format instance");
+                       return NULL;
+               }
+
+               /* Ensure contents of all metadata areas match - else recover */
+               dm_list_iterate_items(mda, &fid->metadata_areas_in_use) {
+                       if ((use_precommitted &&
+                            !(vg = mda->ops->vg_read_precommit(fid, vgname,
+                                                               mda))) ||
+                           (!use_precommitted &&
+                            !(vg = mda->ops->vg_read(fid, vgname, mda)))) {
+                               inconsistent = 1;
+                               continue;
+                       }
+                       if (!correct_vg) {
+                               correct_vg = vg;
+                               if (!_update_pv_list(cmd->mem, &all_pvs, correct_vg)) {
+                                       free_vg(vg);
+                                       return_NULL;
+                               }
+                               continue;
+                       }
+
+                       if (strncmp((char *)vg->id.uuid,
+                           (char *)correct_vg->id.uuid, ID_LEN)) {
+                               inconsistent = 1;
+                               inconsistent_vgid = 1;
+                       }
+
+                       /* FIXME Also ensure contents same - checksums same? */
+                       if (correct_vg->seqno != vg->seqno) {
+                               /* Ignore inconsistent seqno if told to skip repair logic */
+                               if (cmd->metadata_read_only)
+                                       log_very_verbose("Not repairing VG %s metadata seqno (%d != %d) "
+                                                         "as global/metadata_read_only is set.",
+                                                         vgname, vg->seqno, correct_vg->seqno);
+                               else {
+                                       inconsistent = 1;
+                                       inconsistent_seqno = 1;
+                               }
+                               if (!_update_pv_list(cmd->mem, &all_pvs, vg)) {
+                                       free_vg(vg);
+                                       free_vg(correct_vg);
+                                       return_NULL;
+                               }
+                               if (vg->seqno > correct_vg->seqno) {
+                                       free_vg(correct_vg);
+                                       correct_vg = vg;
+                               }
+                       }
+
+                       if (vg != correct_vg)
+                               free_vg(vg);
+               }
+
+               /* Give up looking */
+               if (!correct_vg)
+                       return_NULL;
+       }
+
+       /*
+        * If there is no precommitted metadata, committed metadata
+        * is read and stored in the cache even if use_precommitted is set
+        */
+       lvmcache_update_vg(correct_vg, correct_vg->status & PRECOMMITTED &
+                          (inconsistent ? INCONSISTENT_VG : 0));
+
+       if (inconsistent) {
+               /* FIXME Test should be if we're *using* precommitted metadata not if we were searching for it */
+               if (use_precommitted) {
+                       log_error("Inconsistent pre-commit metadata copies "
+                                 "for volume group %s", vgname);
+                       /* FIXME: during repair, there is inconsistent flag set because some metadata areas
+                        * are missing (on missing PVs). Code should create list of missing PVs, compare it
+                        * with PV marked missing in metadata and if equals, use it as consistent vg.
+                        * For now, return precommited metadata if remainng seq match here to allow
+                        * preloading table in suspend call.
+                        */
+                       if (!inconsistent_seqno) {
+                               *consistent = 0;
+                               return correct_vg;
+                       }
+                       free_vg(correct_vg);
+                       return NULL;
+               }
+
+               if (!*consistent)
+                       return correct_vg;
+
+               /* Don't touch if vgids didn't match */
+               if (inconsistent_vgid) {
+                       log_error("Inconsistent metadata UUIDs found for "
+                                 "volume group %s", vgname);
+                       *consistent = 0;
+                       return correct_vg;
+               }
+
+               log_warn("WARNING: Inconsistent metadata found for VG %s - updating "
+                        "to use version %u", vgname, correct_vg->seqno);
+
+               /*
+                * If PV is marked missing but we found it,
+                * update metadata and remove MISSING flag
+                */
+               dm_list_iterate_items(pvl, &all_pvs)
+                       check_reappeared_pv(correct_vg, pvl->pv);
+
+               cmd->handles_missing_pvs = 1;
+               if (!vg_write(correct_vg)) {
+                       log_error("Automatic metadata correction failed");
+                       free_vg(correct_vg);
+                       cmd->handles_missing_pvs = saved_handles_missing_pvs;
+                       return NULL;
+               }
+               cmd->handles_missing_pvs = saved_handles_missing_pvs;
+
+               if (!vg_commit(correct_vg)) {
+                       log_error("Automatic metadata correction commit "
+                                 "failed");
+                       free_vg(correct_vg);
+                       return NULL;
+               }
+
+               dm_list_iterate_items(pvl, &all_pvs) {
+                       dm_list_iterate_items(pvl2, &correct_vg->pvs) {
+                               if (pvl->pv->dev == pvl2->pv->dev)
+                                       goto next_pv;
+                       }
+                       if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid))) {
+                               free_vg(correct_vg);
+                               return_NULL;
+                       }
+                       log_error("Removing PV %s (%s) that no longer belongs to VG %s",
+                                 pv_dev_name(pvl->pv), uuid, correct_vg->name);
+                       if (!pv_write_orphan(cmd, pvl->pv)) {
+                               free_vg(correct_vg);
+                               return_NULL;
+                       }
+
+                       /* Refresh metadata after orphan write */
+                       drop_cached_metadata(correct_vg);
+      next_pv:
+                       ;
+               }
+       }
+
+       if (vg_missing_pv_count(correct_vg)) {
+               log_verbose("There are %d physical volumes missing.",
+                           vg_missing_pv_count(correct_vg));
+               vg_mark_partial_lvs(correct_vg);
+       }
+
+       if ((correct_vg->status & PVMOVE) && !pvmove_mode()) {
+               log_error("WARNING: Interrupted pvmove detected in "
+                         "volume group %s", correct_vg->name);
+               log_error("Please restore the metadata by running "
+                         "vgcfgrestore.");
+               free_vg(correct_vg);
+               return NULL;
+       }
+
+       *consistent = 1;
+       return correct_vg;
+}
+
+struct volume_group *vg_read_internal(struct cmd_context *cmd, const char *vgname,
+                            const char *vgid, int warnings, int *consistent)
+{
+       struct volume_group *vg;
+       struct lv_list *lvl;
+
+       if (!(vg = _vg_read(cmd, vgname, vgid, warnings, consistent, 0)))
+               return NULL;
+
+       if (!check_pv_segments(vg)) {
+               log_error(INTERNAL_ERROR "PV segments corrupted in %s.",
+                         vg->name);
+               free_vg(vg);
+               return NULL;
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (!check_lv_segments(lvl->lv, 0)) {
+                       log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+                                 lvl->lv->name);
+                       free_vg(vg);
+                       return NULL;
+               }
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               /*
+                * Checks that cross-reference other LVs.
+                */
+               if (!check_lv_segments(lvl->lv, 1)) {
+                       log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+                                 lvl->lv->name);
+                       free_vg(vg);
+                       return NULL;
+               }
+       }
+
+       return vg;
+}
+
+void free_vg(struct volume_group *vg)
+{
+       if (!vg)
+               return;
+
+       if (vg->cmd && vg->vgmem == vg->cmd->mem) {
+               log_error(INTERNAL_ERROR "global memory pool used for VG %s",
+                         vg->name);
+               return;
+       }
+
+       dm_pool_destroy(vg->vgmem);
+}
+
+/* This is only called by lv_from_lvid, which is only called from
+ * activate.c so we know the appropriate VG lock is already held and
+ * the vg_read_internal is therefore safe.
+ */
+static struct volume_group *_vg_read_by_vgid(struct cmd_context *cmd,
+                                           const char *vgid,
+                                           unsigned precommitted)
+{
+       const char *vgname;
+       struct dm_list *vgnames;
+       struct volume_group *vg;
+       struct lvmcache_vginfo *vginfo;
+       struct str_list *strl;
+       int consistent = 0;
+
+       /* Is corresponding vgname already cached? */
+       if ((vginfo = vginfo_from_vgid(vgid)) &&
+           vginfo->vgname && !is_orphan_vg(vginfo->vgname)) {
+               if ((vg = _vg_read(cmd, NULL, vgid, 1,
+                                  &consistent, precommitted)) &&
+                   !strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+                       if (!consistent)
+                               log_error("Volume group %s metadata is "
+                                         "inconsistent", vg->name);
+                       return vg;
+               }
+               free_vg(vg);
+       }
+
+       /* Mustn't scan if memory locked: ensure cache gets pre-populated! */
+       if (memlock())
+               return_NULL;
+
+       /* FIXME Need a genuine read by ID here - don't vg_read_internal by name! */
+       /* FIXME Disabled vgrenames while active for now because we aren't
+        *       allowed to do a full scan here any more. */
+
+       // The slow way - full scan required to cope with vgrename
+       lvmcache_label_scan(cmd, 2);
+       if (!(vgnames = get_vgnames(cmd, 0))) {
+               log_error("vg_read_by_vgid: get_vgnames failed");
+               return NULL;
+       }
+
+       dm_list_iterate_items(strl, vgnames) {
+               vgname = strl->str;
+               if (!vgname)
+                       continue;       // FIXME Unnecessary?
+               consistent = 0;
+               if ((vg = _vg_read(cmd, vgname, vgid, 1, &consistent,
+                                  precommitted)) &&
+                   !strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+                       if (!consistent) {
+                               log_error("Volume group %s metadata is "
+                                         "inconsistent", vgname);
+                               free_vg(vg);
+                               return NULL;
+                       }
+                       return vg;
+               }
+               free_vg(vg);
+       }
+
+       return NULL;
+}
+
+/* Only called by activate.c */
+struct logical_volume *lv_from_lvid(struct cmd_context *cmd, const char *lvid_s,
+                                   unsigned precommitted)
+{
+       struct lv_list *lvl;
+       struct volume_group *vg;
+       const union lvid *lvid;
+
+       lvid = (const union lvid *) lvid_s;
+
+       log_very_verbose("Finding volume group for uuid %s", lvid_s);
+       if (!(vg = _vg_read_by_vgid(cmd, (const char *)lvid->id[0].uuid, precommitted))) {
+               log_error("Volume group for uuid not found: %s", lvid_s);
+               return NULL;
+       }
+
+       log_verbose("Found volume group \"%s\"", vg->name);
+       if (vg->status & EXPORTED_VG) {
+               log_error("Volume group \"%s\" is exported", vg->name);
+               goto out;
+       }
+       if (!(lvl = find_lv_in_vg_by_lvid(vg, lvid))) {
+               log_very_verbose("Can't find logical volume id %s", lvid_s);
+               goto out;
+       }
+
+       return lvl->lv;
+out:
+       free_vg(vg);
+       return NULL;
+}
+
+
+const char *find_vgname_from_pvid(struct cmd_context *cmd,
+                                 const char *pvid)
+{
+       char *vgname;
+       struct lvmcache_info *info;
+
+       vgname = lvmcache_vgname_from_pvid(cmd, pvid);
+
+       if (is_orphan_vg(vgname)) {
+               if (!(info = info_from_pvid(pvid, 0))) {
+                       return_NULL;
+               }
+               /*
+                * If an orphan PV has no MDAs, or it has MDAs but the
+                * MDA is ignored, it may appear to be an orphan until
+                * the metadata is read off another PV in the same VG.
+                * Detecting this means checking every VG by scanning
+                * every PV on the system.
+                */
+               if (mdas_empty_or_ignored(&info->mdas)) {
+                       if (!scan_vgs_for_pvs(cmd, 1)) {
+                               log_error("Rescan for PVs without "
+                                         "metadata areas failed.");
+                               return NULL;
+                       }
+                       /*
+                        * Ask lvmcache again - we may have a non-orphan
+                        * name now
+                        */
+                       vgname = lvmcache_vgname_from_pvid(cmd, pvid);
+               }
+       }
+       return vgname;
+}
+
+
+const char *find_vgname_from_pvname(struct cmd_context *cmd,
+                                   const char *pvname)
+{
+       const char *pvid;
+
+       pvid = pvid_from_devname(cmd, pvname);
+       if (!pvid)
+               /* Not a PV */
+               return NULL;
+
+       return find_vgname_from_pvid(cmd, pvid);
+}
+
+/**
+ * pv_read - read and return a handle to a physical volume
+ * @cmd: LVM command initiating the pv_read
+ * @pv_name: full device name of the PV, including the path
+ * @mdas: list of metadata areas of the PV
+ * @label_sector: sector number where the PV label is stored on @pv_name
+ * @warnings:
+ *
+ * Returns:
+ *   PV handle - valid pv_name and successful read of the PV, or
+ *   NULL - invalid parameter or error in reading the PV
+ *
+ * Note:
+ *   FIXME - liblvm todo - make into function that returns handle
+ */
+struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
+                               struct dm_list *mdas, uint64_t *label_sector,
+                               int warnings, int scan_label_only)
+{
+       return _pv_read(cmd, cmd->mem, pv_name, mdas, label_sector, warnings, scan_label_only);
+}
+
+/* FIXME Use label functions instead of PV functions */
+static struct physical_volume *_pv_read(struct cmd_context *cmd,
+                                       struct dm_pool *pvmem,
+                                       const char *pv_name,
+                                       struct dm_list *mdas,
+                                       uint64_t *label_sector,
+                                       int warnings, int scan_label_only)
+{
+       struct physical_volume *pv;
+       struct label *label;
+       struct lvmcache_info *info;
+       struct device *dev;
+
+       if (!(dev = dev_cache_get(pv_name, cmd->filter)))
+               return_NULL;
+
+       if (!(label_read(dev, &label, UINT64_C(0)))) {
+               if (warnings)
+                       log_error("No physical volume label read from %s",
+                                 pv_name);
+               return NULL;
+       }
+
+       info = (struct lvmcache_info *) label->info;
+       if (label_sector && *label_sector)
+               *label_sector = label->sector;
+
+       pv = _alloc_pv(pvmem, dev);
+       if (!pv) {
+               log_error("pv allocation for '%s' failed", pv_name);
+               return NULL;
+       }
+
+       /* FIXME Move more common code up here */
+       if (!(info->fmt->ops->pv_read(info->fmt, pv_name, pv, mdas,
+             scan_label_only))) {
+               log_error("Failed to read existing physical volume '%s'",
+                         pv_name);
+               goto bad;
+       }
+
+       if (!pv->size)
+               goto bad;
+
+       if (!alloc_pv_segment_whole_pv(pvmem, pv))
+               goto_bad;
+
+       return pv;
+bad:
+       _free_pv(pvmem, pv);
+       return NULL;
+}
+
+/* May return empty list */
+struct dm_list *get_vgnames(struct cmd_context *cmd, int include_internal)
+{
+       return lvmcache_get_vgnames(cmd, include_internal);
+}
+
+struct dm_list *get_vgids(struct cmd_context *cmd, int include_internal)
+{
+       return lvmcache_get_vgids(cmd, include_internal);
+}
+
+static int _get_pvs(struct cmd_context *cmd, int warnings, struct dm_list **pvslist)
+{
+       struct str_list *strl;
+       struct dm_list * uninitialized_var(results);
+       const char *vgname, *vgid;
+       struct pv_list *pvl, *pvl_copy;
+       struct dm_list *vgids;
+       struct volume_group *vg;
+       int consistent = 0;
+       int old_pvmove;
+
+       lvmcache_label_scan(cmd, 0);
+
+       if (pvslist) {
+               if (!(results = dm_pool_alloc(cmd->mem, sizeof(*results)))) {
+                       log_error("PV list allocation failed");
+                       return 0;
+               }
+
+               dm_list_init(results);
+       }
+
+       /* Get list of VGs */
+       if (!(vgids = get_vgids(cmd, 1))) {
+               log_error("get_pvs: get_vgids failed");
+               return 0;
+       }
+
+       /* Read every VG to ensure cache consistency */
+       /* Orphan VG is last on list */
+       old_pvmove = pvmove_mode();
+       init_pvmove(1);
+       dm_list_iterate_items(strl, vgids) {
+               vgid = strl->str;
+               if (!vgid)
+                       continue;       /* FIXME Unnecessary? */
+               consistent = 0;
+               if (!(vgname = vgname_from_vgid(NULL, vgid))) {
+                       stack;
+                       continue;
+               }
+               if (!(vg = vg_read_internal(cmd, vgname, vgid, warnings, &consistent))) {
+                       stack;
+                       continue;
+               }
+               if (!consistent)
+                       log_warn("WARNING: Volume Group %s is not consistent",
+                                vgname);
+
+               /* Move PVs onto results list */
+               if (pvslist)
+                       dm_list_iterate_items(pvl, &vg->pvs) {
+                               if (!(pvl_copy = _copy_pvl(cmd->mem, pvl))) {
+                                       log_error("PV list allocation failed");
+                                       free_vg(vg);
+                                       return 0;
+                               }
+                               dm_list_add(results, &pvl_copy->list);
+                       }
+               free_vg(vg);
+       }
+       init_pvmove(old_pvmove);
+
+       if (pvslist)
+               *pvslist = results;
+       else
+               dm_pool_free(cmd->mem, vgids);
+
+       return 1;
+}
+
+struct dm_list *get_pvs(struct cmd_context *cmd)
+{
+       struct dm_list *results;
+
+       if (!_get_pvs(cmd, 1, &results))
+               return NULL;
+
+       return results;
+}
+
+int scan_vgs_for_pvs(struct cmd_context *cmd, int warnings)
+{
+       return _get_pvs(cmd, warnings, NULL);
+}
+
+int pv_write(struct cmd_context *cmd __attribute__((unused)),
+            struct physical_volume *pv,
+            struct dm_list *mdas, int64_t label_sector)
+{
+       if (!pv->fmt->ops->pv_write) {
+               log_error("Format does not support writing physical volumes");
+               return 0;
+       }
+
+       if (!is_orphan_vg(pv->vg_name) || pv->pe_alloc_count) {
+               log_error("Assertion failed: can't _pv_write non-orphan PV "
+                         "(in VG %s)", pv->vg_name);
+               return 0;
+       }
+
+       if (!pv->fmt->ops->pv_write(pv->fmt, pv, mdas, label_sector))
+               return_0;
+
+       return 1;
+}
+
+int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv)
+{
+       const char *old_vg_name = pv->vg_name;
+
+       pv->vg_name = cmd->fmt->orphan_vg_name;
+       pv->status = ALLOCATABLE_PV;
+       pv->pe_alloc_count = 0;
+
+       if (!dev_get_size(pv->dev, &pv->size)) {
+               log_error("%s: Couldn't get size.", pv_dev_name(pv));
+               return 0;
+       }
+
+       if (!pv_write(cmd, pv, NULL, INT64_C(-1))) {
+               log_error("Failed to clear metadata from physical "
+                         "volume \"%s\" after removal from \"%s\"",
+                         pv_dev_name(pv), old_vg_name);
+               return 0;
+       }
+
+       return 1;
+}
+
+int is_global_vg(const char *vg_name)
+{
+       return (vg_name && !strcmp(vg_name, VG_GLOBAL)) ? 1 : 0;
+}
+
+/**
+ * is_orphan_vg - Determine whether a vg_name is an orphan
+ * @vg_name: pointer to the vg_name
+ */
+int is_orphan_vg(const char *vg_name)
+{
+       return (vg_name && !strncmp(vg_name, ORPHAN_PREFIX, sizeof(ORPHAN_PREFIX) - 1)) ? 1 : 0;
+}
+
+/*
+ * Returns:
+ *  0 - fail
+ *  1 - success
+ */
+int pv_analyze(struct cmd_context *cmd, const char *pv_name,
+              uint64_t label_sector)
+{
+       struct label *label;
+       struct device *dev;
+       struct metadata_area *mda;
+       struct lvmcache_info *info;
+
+       dev = dev_cache_get(pv_name, cmd->filter);
+       if (!dev) {
+               log_error("Device %s not found (or ignored by filtering).",
+                         pv_name);
+               return 0;
+       }
+
+       /*
+        * First, scan for LVM labels.
+        */
+       if (!label_read(dev, &label, label_sector)) {
+               log_error("Could not find LVM label on %s",
+                         pv_name);
+               return 0;
+       }
+
+       log_print("Found label on %s, sector %"PRIu64", type=%s",
+                 pv_name, label->sector, label->type);
+
+       /*
+        * Next, loop through metadata areas
+        */
+       info = label->info;
+       dm_list_iterate_items(mda, &info->mdas)
+               mda->ops->pv_analyze_mda(info->fmt, mda);
+
+       return 1;
+}
+
+/* FIXME: remove / combine this with locking? */
+int vg_check_write_mode(struct volume_group *vg)
+{
+       if (vg->open_mode != 'w') {
+               log_errno(EPERM, "Attempt to modify a read-only VG");
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * Performs a set of checks against a VG according to bits set in status
+ * and returns FAILED_* bits for those that aren't acceptable.
+ *
+ * FIXME Remove the unnecessary duplicate definitions and return bits directly.
+ */
+static uint32_t _vg_bad_status_bits(const struct volume_group *vg,
+                                   uint64_t status)
+{
+       uint32_t failure = 0;
+
+       if ((status & CLUSTERED) &&
+           (vg_is_clustered(vg)) && !locking_is_clustered()) {
+               log_error("Skipping clustered volume group %s", vg->name);
+               /* Return because other flags are considered undefined. */
+               return FAILED_CLUSTERED;
+       }
+
+       if ((status & EXPORTED_VG) &&
+           vg_is_exported(vg)) {
+               log_error("Volume group %s is exported", vg->name);
+               failure |= FAILED_EXPORTED;
+       }
+
+       if ((status & LVM_WRITE) &&
+           !(vg->status & LVM_WRITE)) {
+               log_error("Volume group %s is read-only", vg->name);
+               failure |= FAILED_READ_ONLY;
+       }
+
+       if ((status & RESIZEABLE_VG) &&
+           !vg_is_resizeable(vg)) {
+               log_error("Volume group %s is not resizeable.", vg->name);
+               failure |= FAILED_RESIZEABLE;
+       }
+
+       return failure;
+}
+
+/**
+ * vg_check_status - check volume group status flags and log error
+ * @vg - volume group to check status flags
+ * @status - specific status flags to check (e.g. EXPORTED_VG)
+ */
+int vg_check_status(const struct volume_group *vg, uint64_t status)
+{
+       return !_vg_bad_status_bits(vg, status);
+}
+
+static struct volume_group *_recover_vg(struct cmd_context *cmd,
+                        const char *vg_name, const char *vgid)
+{
+       int consistent = 1;
+       struct volume_group *vg;
+
+       unlock_vg(cmd, vg_name);
+
+       dev_close_all();
+
+       if (!lock_vol(cmd, vg_name, LCK_VG_WRITE))
+               return_NULL;
+
+       if (!(vg = vg_read_internal(cmd, vg_name, vgid, 1, &consistent)))
+               return_NULL;
+
+       if (!consistent) {
+               free_vg(vg);
+               return_NULL;
+       }
+
+       return (struct volume_group *)vg;
+}
+
+/*
+ * Consolidated locking, reading, and status flag checking.
+ *
+ * If the metadata is inconsistent, setting READ_ALLOW_INCONSISTENT in
+ * misc_flags will return it with FAILED_INCONSISTENT set instead of 
+ * giving you nothing.
+ *
+ * Use vg_read_error(vg) to determine the result.  Nonzero means there were
+ * problems reading the volume group.
+ * Zero value means that the VG is open and appropriate locks are held.
+ */
+static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const char *vg_name,
+                              const char *vgid, uint32_t lock_flags,
+                              uint64_t status_flags, uint32_t misc_flags)
+{
+       struct volume_group *vg = NULL;
+       int consistent = 1;
+       int consistent_in;
+       uint32_t failure = 0;
+       int already_locked;
+
+       if (misc_flags & READ_ALLOW_INCONSISTENT || lock_flags != LCK_VG_WRITE)
+               consistent = 0;
+
+       if (!validate_name(vg_name) && !is_orphan_vg(vg_name)) {
+               log_error("Volume group name %s has invalid characters",
+                         vg_name);
+               return NULL;
+       }
+
+       already_locked = vgname_is_locked(vg_name);
+
+       if (!already_locked && !(misc_flags & READ_WITHOUT_LOCK) &&
+           !lock_vol(cmd, vg_name, lock_flags)) {
+               log_error("Can't get lock for %s", vg_name);
+               return _vg_make_handle(cmd, vg, FAILED_LOCKING);
+       }
+
+       if (is_orphan_vg(vg_name))
+               status_flags &= ~LVM_WRITE;
+
+       consistent_in = consistent;
+
+       /* If consistent == 1, we get NULL here if correction fails. */
+       if (!(vg = vg_read_internal(cmd, vg_name, vgid, 1, &consistent))) {
+               if (consistent_in && !consistent) {
+                       log_error("Volume group \"%s\" inconsistent.", vg_name);
+                       failure |= FAILED_INCONSISTENT;
+                       goto_bad;
+               }
+
+               log_error("Volume group \"%s\" not found", vg_name);
+
+               failure |= FAILED_NOTFOUND;
+               goto_bad;
+       }
+
+       if (vg_is_clustered(vg) && !locking_is_clustered()) {
+               log_error("Skipping clustered volume group %s", vg->name);
+               failure |= FAILED_CLUSTERED;
+               goto_bad;
+       }
+
+       /* consistent == 0 when VG is not found, but failed == FAILED_NOTFOUND */
+       if (!consistent && !failure) {
+               free_vg(vg);
+               if (!(vg = _recover_vg(cmd, vg_name, vgid))) {
+                       log_error("Recovery of volume group \"%s\" failed.",
+                                 vg_name);
+                       failure |= FAILED_INCONSISTENT;
+                       goto_bad;
+               }
+       }
+
+       /*
+        * Check that the tool can handle tricky cases -- missing PVs and
+        * unknown segment types.
+        */
+
+       if (!cmd->handles_missing_pvs && vg_missing_pv_count(vg) &&
+           lock_flags == LCK_VG_WRITE) {
+               log_error("Cannot change VG %s while PVs are missing.", vg->name);
+               log_error("Consider vgreduce --removemissing.");
+               failure |= FAILED_INCONSISTENT; /* FIXME new failure code here? */
+               goto_bad;
+       }
+
+       if (!cmd->handles_unknown_segments && vg_has_unknown_segments(vg) &&
+           lock_flags == LCK_VG_WRITE) {
+               log_error("Cannot change VG %s with unknown segments in it!",
+                         vg->name);
+               failure |= FAILED_INCONSISTENT; /* FIXME new failure code here? */
+               goto_bad;
+       }
+
+       failure |= _vg_bad_status_bits(vg, status_flags);
+       if (failure)
+               goto_bad;
+
+       return _vg_make_handle(cmd, vg, failure);
+
+bad:
+       if (!already_locked && !(misc_flags & READ_WITHOUT_LOCK))
+               unlock_vg(cmd, vg_name);
+
+       return _vg_make_handle(cmd, vg, failure);
+}
+
+/*
+ * vg_read: High-level volume group metadata read function.
+ *
+ * vg_read_error() must be used on any handle returned to check for errors.
+ *
+ *  - metadata inconsistent and automatic correction failed: FAILED_INCONSISTENT
+ *  - VG is read-only: FAILED_READ_ONLY
+ *  - VG is EXPORTED, unless flags has READ_ALLOW_EXPORTED: FAILED_EXPORTED
+ *  - VG is not RESIZEABLE: FAILED_RESIZEABLE
+ *  - locking failed: FAILED_LOCKING
+ *
+ * On failures, all locks are released, unless one of the following applies:
+ *  - vgname_is_locked(lock_name) is true
+ * FIXME: remove the above 2 conditions if possible and make an error always
+ * release the lock.
+ *
+ * Volume groups are opened read-only unless flags contains READ_FOR_UPDATE.
+ *
+ * Checking for VG existence:
+ *
+ * FIXME: We want vg_read to attempt automatic recovery after acquiring a
+ * temporary write lock: if that fails, we bail out as usual, with failed &
+ * FAILED_INCONSISTENT. If it works, we are good to go. Code that's been in
+ * toollib just set lock_flags to LCK_VG_WRITE and called vg_read_internal with
+ * *consistent = 1.
+ */
+struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
+             const char *vgid, uint32_t flags)
+{
+       uint64_t status = UINT64_C(0);
+       uint32_t lock_flags = LCK_VG_READ;
+
+       if (flags & READ_FOR_UPDATE) {
+               status |= EXPORTED_VG | LVM_WRITE;
+               lock_flags = LCK_VG_WRITE;
+       }
+
+       if (flags & READ_ALLOW_EXPORTED)
+               status &= ~EXPORTED_VG;
+
+       return _vg_lock_and_read(cmd, vg_name, vgid, lock_flags, status, flags);
+}
+
+/*
+ * A high-level volume group metadata reading function. Open a volume group for
+ * later update (this means the user code can change the metadata and later
+ * request the new metadata to be written and committed).
+ */
+struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
+                        const char *vgid, uint32_t flags)
+{
+       return vg_read(cmd, vg_name, vgid, flags | READ_FOR_UPDATE);
+}
+
+/*
+ * Test the validity of a VG handle returned by vg_read() or vg_read_for_update().
+ */
+uint32_t vg_read_error(struct volume_group *vg_handle)
+{
+       if (!vg_handle)
+               return FAILED_ALLOCATION;
+
+       return vg_handle->read_status;
+}
+
+/*
+ * Lock a vgname and/or check for existence.
+ * Takes a WRITE lock on the vgname before scanning.
+ * If scanning fails or vgname found, release the lock.
+ * NOTE: If you find the return codes confusing, you might think of this
+ * function as similar to an open() call with O_CREAT and O_EXCL flags
+ * (open returns fail with -EEXIST if file already exists).
+ *
+ * Returns:
+ * FAILED_LOCKING - Cannot lock name
+ * FAILED_EXIST - VG name already exists - cannot reserve
+ * SUCCESS - VG name does not exist in system and WRITE lock held
+ */
+uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname)
+{
+       if (!lock_vol(cmd, vgname, LCK_VG_WRITE)) {
+               return FAILED_LOCKING;
+       }
+
+       /* Find the vgname in the cache */
+       /* If it's not there we must do full scan to be completely sure */
+       if (!fmt_from_vgname(vgname, NULL, 1)) {
+               lvmcache_label_scan(cmd, 0);
+               if (!fmt_from_vgname(vgname, NULL, 1)) {
+                       /* Independent MDAs aren't supported under low memory */
+                       if (!cmd->independent_metadata_areas && memlock()) {
+                               /*
+                                * FIXME: Disallow calling this function if
+                                * memlock() is true.
+                                */
+                               unlock_vg(cmd, vgname);
+                               return FAILED_LOCKING;
+                       }
+                       lvmcache_label_scan(cmd, 2);
+                       if (!fmt_from_vgname(vgname, NULL, 0)) {
+                               /* vgname not found after scanning */
+                               return SUCCESS;
+                       }
+               }
+       }
+
+       /* Found vgname so cannot reserve. */
+       unlock_vg(cmd, vgname);
+       return FAILED_EXIST;
+}
+
+void fid_add_mda(struct format_instance *fid, struct metadata_area *mda)
+{
+       dm_list_add(mda_is_ignored(mda) ? &fid->metadata_areas_ignored :
+                                         &fid->metadata_areas_in_use, &mda->list);
+}
+
+int fid_add_mdas(struct format_instance *fid, struct dm_list *mdas)
+{
+       struct metadata_area *mda, *mda_new;
+
+       dm_list_iterate_items(mda, mdas) {
+               mda_new = mda_copy(fid->fmt->cmd->mem, mda);
+               if (!mda_new)
+                       return_0;
+               fid_add_mda(fid, mda_new);
+       }
+       return 1;
+}
+
+/*
+ * Copy constructor for a metadata_area.
+ */
+struct metadata_area *mda_copy(struct dm_pool *mem,
+                              struct metadata_area *mda)
+{
+       struct metadata_area *mda_new;
+
+       if (!(mda_new = dm_pool_alloc(mem, sizeof(*mda_new)))) {
+               log_error("metadata_area allocation failed");
+               return NULL;
+       }
+       memcpy(mda_new, mda, sizeof(*mda));
+       if (mda->ops->mda_metadata_locn_copy && mda->metadata_locn) {
+               mda_new->metadata_locn =
+                       mda->ops->mda_metadata_locn_copy(mem, mda->metadata_locn);
+               if (!mda_new->metadata_locn) {
+                       dm_pool_free(mem, mda_new);
+                       return NULL;
+               }
+       }
+
+       dm_list_init(&mda_new->list);
+
+       return mda_new;
+}
+/*
+ * This function provides a way to answer the question on a format specific
+ * basis - does the format specfic context of these two metadata areas
+ * match?
+ *
+ * A metatdata_area is defined to be independent of the underlying context.
+ * This has the benefit that we can use the same abstraction to read disks
+ * (see _metadata_text_raw_ops) or files (see _metadata_text_file_ops).
+ * However, one downside is there is no format-independent way to determine
+ * whether a given metadata_area is attached to a specific device - in fact,
+ * it may not be attached to a device at all.
+ *
+ * Thus, LVM is structured such that an mda is not a member of struct
+ * physical_volume.  The location of the mda depends on whether
+ * the PV is in a volume group.  A PV not in a VG has an mda on the
+ * 'info->mda' list in lvmcache, while a PV in a VG has an mda on
+ * the vg->fid->metadata_areas_in_use list.  For further details, see _vg_read(),
+ * and the sequence of creating the format_instance with fid->metadata_areas_in_use
+ * list, as well as the construction of the VG, with list of PVs (comes
+ * after the construction of the fid and list of mdas).
+ */
+unsigned mda_locns_match(struct metadata_area *mda1, struct metadata_area *mda2)
+{
+       if (!mda1->ops->mda_locns_match || !mda2->ops->mda_locns_match ||
+           mda1->ops->mda_locns_match != mda2->ops->mda_locns_match)
+               return 0;
+
+       return mda1->ops->mda_locns_match(mda1, mda2);
+}
+
+unsigned mda_is_ignored(struct metadata_area *mda)
+{
+       return (mda->status & MDA_IGNORED);
+}
+
+void mda_set_ignored(struct metadata_area *mda, unsigned mda_ignored)
+{
+       void *locn = mda->metadata_locn;
+       unsigned old_mda_ignored = mda_is_ignored(mda);
+
+       if (mda_ignored && !old_mda_ignored)
+               mda->status |= MDA_IGNORED;
+       else if (!mda_ignored && old_mda_ignored)
+               mda->status &= ~MDA_IGNORED;
+       else
+               return; /* No change */
+
+       log_debug("%s ignored flag for mda %s at offset %" PRIu64 ".", 
+                 mda_ignored ? "Setting" : "Clearing",
+                 mda->ops->mda_metadata_locn_name ? mda->ops->mda_metadata_locn_name(locn) : "",
+                 mda->ops->mda_metadata_locn_offset ? mda->ops->mda_metadata_locn_offset(locn) : UINT64_C(0));
+}
+
+int mdas_empty_or_ignored(struct dm_list *mdas)
+{
+       struct metadata_area *mda;
+
+       if (!dm_list_size(mdas))
+               return 1;
+       dm_list_iterate_items(mda, mdas) {
+               if (mda_is_ignored(mda))
+                       return 1;
+       }
+       return 0;
+}
+
+int pv_change_metadataignore(struct physical_volume *pv, uint32_t mda_ignored)
+{
+       const char *pv_name = pv_dev_name(pv);
+
+       if (mda_ignored && !pv_mda_used_count(pv)) {
+               log_error("Metadata areas on physical volume \"%s\" already "
+                         "ignored.", pv_name);
+               return 0;
+       }
+
+       if (!mda_ignored && (pv_mda_used_count(pv) == pv_mda_count(pv))) {
+               log_error("Metadata areas on physical volume \"%s\" already "
+                         "marked as in-use.", pv_name);
+               return 0;
+       }
+
+       if (!pv_mda_count(pv)) {
+               log_error("Physical volume \"%s\" has no metadata "
+                         "areas.", pv_name);
+               return 0;
+       }
+
+       log_verbose("Marking metadata areas on physical volume \"%s\" "
+                   "as %s.", pv_name, mda_ignored ? "ignored" : "in-use");
+
+       if (!pv_mda_set_ignored(pv, mda_ignored))
+               return_0;
+
+       /*
+        * Update vg_mda_copies based on the mdas in this PV.
+        * This is most likely what the user would expect - if they
+        * specify a specific PV to be ignored/un-ignored, they will
+        * most likely not want LVM to turn around and change the
+        * ignore / un-ignore value when it writes the VG to disk.
+        * This does not guarantee this PV's ignore bits will be
+        * preserved in future operations.
+        */
+       if (!is_orphan(pv) &&
+           vg_mda_copies(pv->vg) != VGMETADATACOPIES_UNMANAGED) {
+               log_warn("WARNING: Changing preferred number of copies of VG %s "
+                        "metadata from %"PRIu32" to %"PRIu32, pv_vg_name(pv),
+                        vg_mda_copies(pv->vg), vg_mda_used_count(pv->vg));
+               vg_set_mda_copies(pv->vg, vg_mda_used_count(pv->vg));
+       }
+
+       return 1;
+}
+
+char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags)
+{
+       struct str_list *sl;
+
+       if (!dm_pool_begin_object(mem, 256)) {
+               log_error("dm_pool_begin_object failed");
+               return NULL;
+       }
+
+       dm_list_iterate_items(sl, tags) {
+               if (!dm_pool_grow_object(mem, sl->str, strlen(sl->str)) ||
+                   (sl->list.n != tags && !dm_pool_grow_object(mem, ",", 1))) {
+                       log_error("dm_pool_grow_object failed");
+                       return NULL;
+               }
+       }
+
+       if (!dm_pool_grow_object(mem, "\0", 1)) {
+               log_error("dm_pool_grow_object failed");
+               return NULL;
+       }
+       return dm_pool_end_object(mem);
+}
+
+/**
+ * pv_by_path - Given a device path return a PV handle if it is a PV
+ * @cmd - handle to the LVM command instance
+ * @pv_name - device path to read for the PV
+ *
+ * Returns:
+ *  NULL - device path does not contain a valid PV
+ *  non-NULL - PV handle corresponding to device path
+ *
+ * FIXME: merge with find_pv_by_name ?
+ */
+struct physical_volume *pv_by_path(struct cmd_context *cmd, const char *pv_name)
+{
+       struct dm_list mdas;
+
+       dm_list_init(&mdas);
+       return _pv_read(cmd, cmd->mem, pv_name, &mdas, NULL, 1, 0);
+}
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
new file mode 100644 (file)
index 0000000..08bff4a
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This is the in core representation of a volume group and its
+ * associated physical and logical volumes.
+ */
+
+#ifndef _LVM_METADATA_H
+#define _LVM_METADATA_H
+
+#include "ctype.h"
+#include "dev-cache.h"
+#include "lvm-string.h"
+#include "metadata-exported.h"
+
+//#define MAX_STRIPES 128U
+//#define SECTOR_SHIFT 9L
+//#define SECTOR_SIZE ( 1L << SECTOR_SHIFT )
+//#define STRIPE_SIZE_MIN ( (unsigned) lvm_getpagesize() >> SECTOR_SHIFT)      /* PAGESIZE in sectors */
+//#define STRIPE_SIZE_MAX ( 512L * 1024L >> SECTOR_SHIFT)      /* 512 KB in sectors */
+//#define STRIPE_SIZE_LIMIT ((UINT_MAX >> 2) + 1)
+//#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT)  /* 512 KB in sectors */
+//#define MAX_RESTRICTED_LVS 255       /* Used by FMT_RESTRICTED_LVIDS */
+#define MIRROR_LOG_OFFSET      2       /* sectors */
+#define VG_MEMPOOL_CHUNK       10240   /* in bytes, hint only */
+
+/*
+ * Ceiling(n / sz)
+ */
+#define dm_div_up(n, sz) (((n) + (sz) - 1) / (sz))
+
+/*
+ * Ceiling(n / size) * size
+ */
+#define dm_round_up(n, sz) (dm_div_up((n), (sz)) * (sz))
+
+
+/* Various flags */
+/* Note that the bits no longer necessarily correspond to LVM1 disk format */
+
+//#define PARTIAL_VG           0x00000001U     /* VG */
+//#define EXPORTED_VG                  0x00000002U     /* VG PV */
+//#define RESIZEABLE_VG                0x00000004U     /* VG */
+
+/* May any free extents on this PV be used or must they be left free? */
+//#define ALLOCATABLE_PV               0x00000008U     /* PV */
+
+#define SPINDOWN_LV            0x00000010U     /* LV */
+#define BADBLOCK_ON            0x00000020U     /* LV */
+//#define VISIBLE_LV           0x00000040U     /* LV */
+//#define FIXED_MINOR          0x00000080U     /* LV */
+/* FIXME Remove when metadata restructuring is completed */
+//#define SNAPSHOT             0x00001000U     /* LV - internal use only */
+//#define PVMOVE                       0x00002000U     /* VG LV SEG */
+//#define LOCKED                       0x00004000U     /* LV */
+//#define MIRRORED             0x00008000U     /* LV - internal use only */
+#define VIRTUAL                        0x00010000U     /* LV - internal use only */
+//#define MIRROR_LOG           0x00020000U     /* LV */
+//#define MIRROR_IMAGE         0x00040000U     /* LV */
+//#define MIRROR_NOTSYNCED     0x00080000U     /* LV */
+#define ACTIVATE_EXCL          0x00100000U     /* LV - internal use only */
+#define PRECOMMITTED           0x00200000U     /* VG - internal use only */
+//#define CONVERTING           0x00400000U     /* LV */
+
+//#define MISSING_PV           0x00800000U     /* PV */
+#define INCONSISTENT_VG                0x00800000U     /* VG - internal use only */
+//#define PARTIAL_LV           0x01000000U     /* LV - derived flag, not
+//                                                written out in metadata*/
+
+#define POSTORDER_FLAG         0x02000000U /* Not real flags, reserved for  */
+#define POSTORDER_OPEN_FLAG    0x04000000U /* temporary use inside vg_read_internal. */
+#define VIRTUAL_ORIGIN         0x08000000U     /* LV - internal use only */
+
+//#define LVM_READ                     0x00000100U     /* LV VG */
+//#define LVM_WRITE                    0x00000200U     /* LV VG */
+//#define CLUSTERED            0x00000400U     /* VG */
+#define SHARED                 0x00000800U     /* VG */
+
+/* Format features flags */
+//#define FMT_SEGMENTS         0x00000001U     /* Arbitrary segment params? */
+//#define FMT_MDAS             0x00000002U     /* Proper metadata areas? */
+//#define FMT_TAGS             0x00000004U     /* Tagging? */
+//#define FMT_UNLIMITED_VOLS   0x00000008U     /* Unlimited PVs/LVs? */
+//#define FMT_RESTRICTED_LVIDS 0x00000010U     /* LVID <= 255 */
+//#define FMT_ORPHAN_ALLOCATABLE       0x00000020U     /* Orphan PV allocatable? */
+#define FMT_PRECOMMIT          0x00000040U     /* Supports pre-commit? */
+//#define FMT_RESIZE_PV                0x00000080U     /* Supports pvresize? */
+//#define FMT_UNLIMITED_STRIPESIZE 0x00000100U /* Unlimited stripe size? */
+
+struct metadata_area;
+
+/* Per-format per-metadata area operations */
+struct metadata_area_ops {
+       struct volume_group *(*vg_read) (struct format_instance * fi,
+                                        const char *vg_name,
+                                        struct metadata_area * mda);
+       struct volume_group *(*vg_read_precommit) (struct format_instance * fi,
+                                        const char *vg_name,
+                                        struct metadata_area * mda);
+       /*
+        * Write out complete VG metadata.  You must ensure internal
+        * consistency before calling. eg. PEs can't refer to PVs not
+        * part of the VG.
+        *
+        * It is also the responsibility of the caller to ensure external
+        * consistency, eg by calling pv_write() if removing PVs from
+        * a VG or calling vg_write() a second time if splitting a VG
+        * into two.
+        *
+        * vg_write() should not read or write from any PVs not included
+        * in the volume_group structure it is handed.
+        * (format1 currently breaks this rule.)
+        */
+       int (*vg_write) (struct format_instance * fid, struct volume_group * vg,
+                        struct metadata_area * mda);
+       int (*vg_precommit) (struct format_instance * fid,
+                            struct volume_group * vg,
+                            struct metadata_area * mda);
+       int (*vg_commit) (struct format_instance * fid,
+                         struct volume_group * vg, struct metadata_area * mda);
+       int (*vg_revert) (struct format_instance * fid,
+                         struct volume_group * vg, struct metadata_area * mda);
+       int (*vg_remove) (struct format_instance * fi, struct volume_group * vg,
+                         struct metadata_area * mda);
+
+       /*
+        * Per location copy constructor.
+        */
+       void *(*mda_metadata_locn_copy) (struct dm_pool *mem, void *metadata_locn);
+
+       /*
+        * Per location description for logging.
+        */
+       const char *(*mda_metadata_locn_name) (void *metadata_locn);
+       uint64_t (*mda_metadata_locn_offset) (void *metadata_locn);
+
+       /*
+        * Returns number of free sectors in given metadata area.
+        */
+       uint64_t (*mda_free_sectors) (struct metadata_area *mda);
+
+       /*
+        * Returns number of total sectors in given metadata area.
+        */
+       uint64_t (*mda_total_sectors) (struct metadata_area *mda);
+
+       /*
+        * Check if metadata area belongs to vg
+        */
+       int (*mda_in_vg) (struct format_instance * fi,
+                           struct volume_group * vg, struct metadata_area *mda);
+       /*
+        * Analyze a metadata area on a PV.
+        */
+       int (*pv_analyze_mda) (const struct format_type * fmt,
+                              struct metadata_area *mda);
+
+       /*
+        * Do these two metadata_area structures match with respect to
+        * their underlying location?
+        */
+       unsigned (*mda_locns_match)(struct metadata_area *mda1,
+                                   struct metadata_area *mda2);
+};
+
+#define MDA_IGNORED 0x00000001
+
+struct metadata_area {
+       struct dm_list list;
+       struct metadata_area_ops *ops;
+       void *metadata_locn;
+       uint32_t status;
+};
+struct metadata_area *mda_copy(struct dm_pool *mem,
+                              struct metadata_area *mda);
+
+unsigned mda_is_ignored(struct metadata_area *mda);
+void mda_set_ignored(struct metadata_area *mda, unsigned ignored);
+unsigned mda_locns_match(struct metadata_area *mda1, struct metadata_area *mda2);
+void fid_add_mda(struct format_instance *fid, struct metadata_area *mda);
+int fid_add_mdas(struct format_instance *fid, struct dm_list *mdas);
+int mdas_empty_or_ignored(struct dm_list *mdas);
+
+#define seg_pvseg(seg, s)      (seg)->areas[(s)].u.pv.pvseg
+#define seg_dev(seg, s)                (seg)->areas[(s)].u.pv.pvseg->pv->dev
+#define seg_pe(seg, s)         (seg)->areas[(s)].u.pv.pvseg->pe
+#define seg_le(seg, s)         (seg)->areas[(s)].u.lv.le
+
+struct name_list {
+       struct dm_list list;
+       char *name;
+};
+
+struct mda_list {
+       struct dm_list list;
+       struct device_area mda;
+};
+
+struct peg_list {
+       struct dm_list list;
+       struct pv_segment *peg;
+};
+
+struct seg_list {
+       struct dm_list list;
+       unsigned count;
+       struct lv_segment *seg;
+};
+
+/*
+ * Ownership of objects passes to caller.
+ */
+struct format_handler {
+       /*
+        * Scan any metadata areas that aren't referenced in PV labels
+        */
+       int (*scan) (const struct format_type * fmt, const char *vgname);
+
+       /*
+        * Return PV with given path.
+        */
+       int (*pv_read) (const struct format_type * fmt, const char *pv_name,
+                       struct physical_volume * pv, struct dm_list *mdas,
+                       int scan_label_only);
+
+       /*
+        * Tweak an already filled out a pv ready for importing into a
+        * vg.  eg. pe_count is format specific.
+        */
+       int (*pv_setup) (const struct format_type * fmt,
+                        uint64_t pe_start, uint32_t extent_count,
+                        uint32_t extent_size, unsigned long data_alignment,
+                        unsigned long data_alignment_offset,
+                        int pvmetadatacopies, uint64_t pvmetadatasize,
+                        unsigned metadataignore, struct dm_list * mdas,
+                        struct physical_volume * pv, struct volume_group * vg);
+
+       /*
+        * Write a PV structure to disk. Fails if the PV is in a VG ie
+        * pv->vg_name must be a valid orphan VG name
+        */
+       int (*pv_write) (const struct format_type * fmt,
+                        struct physical_volume * pv, struct dm_list * mdas,
+                        int64_t label_sector);
+
+       /*
+        * Tweak an already filled out a lv eg, check there
+        * aren't too many extents.
+        */
+       int (*lv_setup) (struct format_instance * fi,
+                        struct logical_volume * lv);
+
+       /*
+        * Tweak an already filled out vg.  eg, max_pv is format
+        * specific.
+        */
+       int (*vg_setup) (struct format_instance * fi, struct volume_group * vg);
+
+       /*
+        * Check whether particular segment type is supported.
+        */
+       int (*segtype_supported) (struct format_instance *fid,
+                                 const struct segment_type *segtype);
+
+       /*
+        * Create format instance with a particular metadata area
+        */
+       struct format_instance *(*create_instance) (const struct format_type *
+                                                   fmt, const char *vgname,
+                                                   const char *vgid,
+                                                   void *context);
+
+       /*
+        * Destructor for format instance
+        */
+       void (*destroy_instance) (struct format_instance * fid);
+
+       /*
+        * Destructor for format type
+        */
+       void (*destroy) (struct format_type * fmt);
+};
+
+/*
+ * Utility functions
+ */
+unsigned long set_pe_align(struct physical_volume *pv, unsigned long data_alignment);
+unsigned long set_pe_align_offset(struct physical_volume *pv,
+                                 unsigned long data_alignment_offset);
+int vg_validate(struct volume_group *vg);
+
+int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv);
+
+/* Manipulate PV structures */
+int pv_add(struct volume_group *vg, struct physical_volume *pv);
+int pv_remove(struct volume_group *vg, struct physical_volume *pv);
+struct physical_volume *pv_find(struct volume_group *vg, const char *pv_name);
+
+/* Find a PV within a given VG */
+int get_pv_from_vg_by_id(const struct format_type *fmt, const char *vg_name,
+                        const char *vgid, const char *pvid,
+                        struct physical_volume *pv);
+
+struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg,
+                                     const union lvid *lvid);
+
+struct lv_list *find_lv_in_lv_list(const struct dm_list *ll,
+                                  const struct logical_volume *lv);
+
+/* Return the VG that contains a given LV (based on path given in lv_name) */
+/* or environment var */
+struct volume_group *find_vg_with_lv(const char *lv_name);
+
+/* Find LV with given lvid (used during activation) */
+struct logical_volume *lv_from_lvid(struct cmd_context *cmd,
+                                   const char *lvid_s,
+                                   unsigned precommitted);
+
+/* FIXME Merge these functions with ones above */
+struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);
+
+struct pv_list *find_pv_in_pv_list(const struct dm_list *pl,
+                                  const struct physical_volume *pv);
+
+/* Find LV segment containing given LE */
+struct lv_segment *find_seg_by_le(const struct logical_volume *lv, uint32_t le);
+
+/*
+ * Remove a dev_dir if present.
+ */
+const char *strip_dir(const char *vg_name, const char *dir);
+
+struct logical_volume *alloc_lv(struct dm_pool *mem);
+
+/*
+ * Checks that an lv has no gaps or overlapping segments.
+ * Set complete_vg to perform additional VG level checks.
+ */
+int check_lv_segments(struct logical_volume *lv, int complete_vg);
+
+
+/*
+ * Checks that a replicator segment is correct.
+ */
+int check_replicator_segment(const struct lv_segment *replicator_seg);
+
+/*
+ * Sometimes (eg, after an lvextend), it is possible to merge two
+ * adjacent segments into a single segment.  This function trys
+ * to merge as many segments as possible.
+ */
+int lv_merge_segments(struct logical_volume *lv);
+
+/*
+ * Ensure there's a segment boundary at a given LE, splitting if necessary
+ */
+int lv_split_segment(struct logical_volume *lv, uint32_t le);
+
+/*
+ * Add/remove upward link from underlying LV to the segment using it
+ * FIXME: ridiculously long name
+ */
+int add_seg_to_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
+int remove_seg_from_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
+struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv);
+
+/*
+ * Calculate readahead from underlying PV devices
+ */
+void lv_calculate_readahead(const struct logical_volume *lv, uint32_t *read_ahead);
+
+/*
+ * For internal metadata caching.
+ */
+int export_vg_to_buffer(struct volume_group *vg, char **buf);
+struct volume_group *import_vg_from_buffer(const char *buf,
+                                          struct format_instance *fid);
+
+/*
+ * Mirroring functions
+ */
+
+/*
+ * Given mirror image or mirror log segment, find corresponding mirror segment 
+ */
+int fixup_imported_mirrors(struct volume_group *vg);
+
+/*
+ * Begin skeleton for external LVM library
+ */
+struct id pv_id(const struct physical_volume *pv);
+const struct format_type *pv_format_type(const struct physical_volume *pv);
+struct id pv_vgid(const struct physical_volume *pv);
+
+struct physical_volume *pv_by_path(struct cmd_context *cmd, const char *pv_name);
+int add_pv_to_vg(struct volume_group *vg, const char *pv_name,
+                struct physical_volume *pv);
+int vg_mark_partial_lvs(struct volume_group *vg);
+int is_mirror_image_removable(struct logical_volume *mimage_lv, void *baton);
+
+uint64_t find_min_mda_size(struct dm_list *mdas);
+char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags);
+
+#endif
diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c
new file mode 100644 (file)
index 0000000..1be07cf
--- /dev/null
@@ -0,0 +1,2079 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "archiver.h"
+#include "activate.h"
+#include "lv_alloc.h"
+#include "lvm-string.h"
+#include "str_list.h"
+#include "locking.h"   /* FIXME Should not be used in this file */
+#include "memlock.h"
+
+#include "defaults.h" /* FIXME: should this be defaults.h? */
+
+/* These are necessary for _write_log_header() */
+#include "xlate.h"
+#define MIRROR_MAGIC 0x4D695272
+#define MIRROR_DISK_VERSION 2
+
+/* These are the flags that represent the mirror failure restoration policies */
+#define MIRROR_REMOVE           0
+#define MIRROR_ALLOCATE                 1
+#define MIRROR_ALLOCATE_ANYWHERE 2
+
+/*
+ * Returns true if the lv is temporary mirror layer for resync
+ */
+int is_temporary_mirror_layer(const struct logical_volume *lv)
+{
+       if (lv->status & MIRROR_IMAGE
+           && lv->status & MIRRORED
+           && !(lv->status & LOCKED))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Return a temporary LV for resyncing added mirror image.
+ * Add other mirror legs to lvs list.
+ */
+struct logical_volume *find_temporary_mirror(const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+
+       if (!(lv->status & MIRRORED))
+               return NULL;
+
+       seg = first_seg(lv);
+
+       /* Temporary mirror is always area_num == 0 */
+       if (seg_type(seg, 0) == AREA_LV &&
+           is_temporary_mirror_layer(seg_lv(seg, 0)))
+               return seg_lv(seg, 0);
+
+       return NULL;
+}
+
+int lv_is_mirrored(const struct logical_volume *lv)
+{
+       if (lv->status & MIRRORED)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * cluster_mirror_is_available
+ *
+ * Check if the proper kernel module and log daemon are running.
+ * Caller should check for 'vg_is_clustered(lv->vg)' before making
+ * this call.
+ *
+ * Returns: 1 if available, 0 otherwise
+ */
+static int cluster_mirror_is_available(struct logical_volume *lv)
+{
+       unsigned attr = 0;
+       struct cmd_context *cmd = lv->vg->cmd;
+       const struct segment_type *segtype;
+
+       if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+               return_0;
+
+       if (!segtype->ops->target_present)
+               return_0;
+
+       if (!segtype->ops->target_present(lv->vg->cmd, NULL, &attr))
+               return_0;
+
+       if (!(attr & MIRROR_LOG_CLUSTERED))
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Returns the number of mirrors of the LV
+ */
+uint32_t lv_mirror_count(const struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+       uint32_t s, mirrors;
+
+       if (!(lv->status & MIRRORED))
+               return 1;
+
+       seg = first_seg(lv);
+       mirrors = seg->area_count;
+
+       for (s = 0; s < seg->area_count; s++) {
+               if (seg_type(seg, s) != AREA_LV)
+                       continue;
+               if (is_temporary_mirror_layer(seg_lv(seg, s)))
+                       mirrors += lv_mirror_count(seg_lv(seg, s)) - 1;
+       }
+
+       return mirrors;
+}
+
+struct lv_segment *find_mirror_seg(struct lv_segment *seg)
+{
+       struct lv_segment *mirror_seg;
+
+       mirror_seg = get_only_segment_using_this_lv(seg->lv);
+
+       if (!mirror_seg) {
+               log_error("Failed to find mirror_seg for %s", seg->lv->name);
+               return NULL;
+       }
+
+       if (!seg_is_mirrored(mirror_seg)) {
+               log_error("%s on %s is not a mirror segments",
+                         mirror_seg->lv->name, seg->lv->name);
+               return NULL;
+       }
+
+       return mirror_seg;
+}
+
+/*
+ * Reduce the region size if necessary to ensure
+ * the volume size is a multiple of the region size.
+ */
+uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
+                                    uint32_t region_size)
+{
+       uint64_t region_max;
+
+       region_max = (1 << (ffs((int)extents) - 1)) * (uint64_t) extent_size;
+
+       if (region_max < UINT32_MAX && region_size > region_max) {
+               region_size = (uint32_t) region_max;
+               log_print("Using reduced mirror region size of %" PRIu32
+                         " sectors", region_size);
+       }
+
+       return region_size;
+}
+
+/*
+ * shift_mirror_images
+ * @mirrored_seg
+ * @mimage:  The position (index) of the image to move to the end
+ *
+ * When dealing with removal of legs, we often move a 'removable leg'
+ * to the back of the 'areas' array.  It is critically important not
+ * to simply swap it for the last area in the array.  This would have
+ * the affect of reordering the remaining legs - altering position of
+ * the primary.  So, we must shuffle all of the areas in the array
+ * to maintain their relative position before moving the 'removable
+ * leg' to the end.
+ *
+ * Short illustration of the problem:
+ *   - Mirror consists of legs A, B, C and we want to remove A
+ *   - We swap A and C and then remove A, leaving C, B
+ * This scenario is problematic in failure cases where A dies, because
+ * B becomes the primary.  If the above happens, we effectively throw
+ * away any changes made between the time of failure and the time of
+ * restructuring the mirror.
+ *
+ * So, any time we want to move areas to the end to be removed, use
+ * this function.
+ */
+int shift_mirror_images(struct lv_segment *mirrored_seg, unsigned mimage)
+{
+       int i;
+       struct lv_segment_area area;
+
+       if (mimage >= mirrored_seg->area_count) {
+               log_error("Invalid index (%u) of mirror image supplied "
+                         "to shift_mirror_images()", mimage);
+               return 0;
+       }
+
+       area = mirrored_seg->areas[mimage];
+
+       /* Shift remaining images down to fill the hole */
+       for (i = mimage + 1; i < mirrored_seg->area_count; i++)
+               mirrored_seg->areas[i-1] = mirrored_seg->areas[i];
+
+       /* Place this one at the end */
+       mirrored_seg->areas[i-1] = area;
+
+       return 1;
+}
+
+/*
+ * This function writes a new header to the mirror log header to the lv
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+static int _write_log_header(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       struct device *dev;
+       char *name;
+       struct { /* The mirror log header */
+               uint32_t magic;
+               uint32_t version;
+               uint64_t nr_regions;
+       } log_header;
+
+       log_header.magic = xlate32(MIRROR_MAGIC);
+       log_header.version = xlate32(MIRROR_DISK_VERSION);
+       log_header.nr_regions = xlate64((uint64_t)-1);
+
+       if (!(name = dm_pool_alloc(cmd->mem, PATH_MAX))) {
+               log_error("Name allocation failed - log header not written (%s)",
+                       lv->name);
+               return 0;
+       }
+
+       if (dm_snprintf(name, PATH_MAX, "%s%s/%s", cmd->dev_dir,
+                        lv->vg->name, lv->name) < 0) {
+               log_error("Name too long - log header not written (%s)", lv->name);
+               return 0;
+       }
+
+       log_verbose("Writing log header to device, %s", lv->name);
+
+       if (!(dev = dev_cache_get(name, NULL))) {
+               log_error("%s: not found: log header not written", name);
+               return 0;
+       }
+
+       if (!dev_open_quiet(dev))
+               return 0;
+
+       if (!dev_write(dev, UINT64_C(0), sizeof(log_header), &log_header)) {
+               log_error("Failed to write log header to %s", name);
+               dev_close_immediate(dev);
+               return 0;
+       }
+
+       dev_close_immediate(dev);
+
+       return 1;
+}
+
+/*
+ * Initialize mirror log contents
+ */
+static int _init_mirror_log(struct cmd_context *cmd,
+                           struct logical_volume *log_lv, int in_sync,
+                           struct dm_list *tags, int remove_on_failure)
+{
+       struct str_list *sl;
+       struct lvinfo info;
+       uint64_t orig_status = log_lv->status;
+       int was_active = 0;
+
+       if (!activation() && in_sync) {
+               log_error("Aborting. Unable to create in-sync mirror log "
+                         "while activation is disabled.");
+               return 0;
+       }
+
+       /* If the LV is active, deactivate it first. */
+       if (lv_info(cmd, log_lv, 0, &info, 0, 0) && info.exists) {
+               (void)deactivate_lv(cmd, log_lv);
+               /*
+                * FIXME: workaround to fail early
+                * Ensure that log is really deactivated because deactivate_lv
+                * on cluster do not fail if there is log_lv with different UUID.
+                */
+               if (lv_info(cmd, log_lv, 0, &info, 0, 0) && info.exists) {
+                       log_error("Aborting. Unable to deactivate mirror log.");
+                       goto revert_new_lv;
+               }
+               was_active = 1;
+       }
+
+       /* Temporary make it visible for set_lv() */
+       lv_set_visible(log_lv);
+
+       /* Temporary tag mirror log for activation */
+       dm_list_iterate_items(sl, tags)
+               if (!str_list_add(cmd->mem, &log_lv->tags, sl->str)) {
+                       log_error("Aborting. Unable to tag mirror log.");
+                       goto activate_lv;
+               }
+
+       /* store mirror log on disk(s) */
+       if (!vg_write(log_lv->vg) || !vg_commit(log_lv->vg))
+               goto activate_lv;
+
+       backup(log_lv->vg);
+
+       if (!activate_lv(cmd, log_lv)) {
+               log_error("Aborting. Failed to activate mirror log.");
+               goto revert_new_lv;
+       }
+
+       /* Remove the temporary tags */
+       dm_list_iterate_items(sl, tags)
+               if (!str_list_del(&log_lv->tags, sl->str))
+                       log_error("Failed to remove tag %s from mirror log.",
+                                 sl->str);
+
+       if (activation() && !set_lv(cmd, log_lv, log_lv->size,
+                                   in_sync ? -1 : 0)) {
+               log_error("Aborting. Failed to wipe mirror log.");
+               goto deactivate_and_revert_new_lv;
+       }
+
+       if (activation() && !_write_log_header(cmd, log_lv)) {
+               log_error("Aborting. Failed to write mirror log header.");
+               goto deactivate_and_revert_new_lv;
+       }
+
+       if (!deactivate_lv(cmd, log_lv)) {
+               log_error("Aborting. Failed to deactivate mirror log. "
+                         "Manual intervention required.");
+               return 0;
+       }
+
+       lv_set_hidden(log_lv);
+
+       if (was_active && !activate_lv(cmd, log_lv))
+               return_0;
+
+       return 1;
+
+deactivate_and_revert_new_lv:
+       if (!deactivate_lv(cmd, log_lv)) {
+               log_error("Unable to deactivate mirror log LV. "
+                         "Manual intervention required.");
+               return 0;
+       }
+
+revert_new_lv:
+       log_lv->status = orig_status;
+
+       dm_list_iterate_items(sl, tags)
+               if (!str_list_del(&log_lv->tags, sl->str))
+                       log_error("Failed to remove tag %s from mirror log.",
+                                 sl->str);
+
+       if (remove_on_failure && !lv_remove(log_lv)) {
+               log_error("Manual intervention may be required to remove "
+                         "abandoned log LV before retrying.");
+               return 0;
+       }
+
+       if (!vg_write(log_lv->vg) || !vg_commit(log_lv->vg))
+               log_error("Manual intervention may be required to "
+                         "remove/restore abandoned log LV before retrying.");
+       else
+               backup(log_lv->vg);
+
+activate_lv:
+       if (was_active && !remove_on_failure && !activate_lv(cmd, log_lv))
+               return_0;
+
+       return 0;
+}
+
+/*
+ * Delete independent/orphan LV, it must acquire lock.
+ */
+static int _delete_lv(struct logical_volume *mirror_lv, struct logical_volume *lv)
+{
+       struct cmd_context *cmd = mirror_lv->vg->cmd;
+       struct str_list *sl;
+
+       /* Inherit tags - maybe needed for activation */
+       if (!str_list_match_list(&mirror_lv->tags, &lv->tags, NULL)) {
+               dm_list_iterate_items(sl, &mirror_lv->tags)
+                       if (!str_list_add(cmd->mem, &lv->tags, sl->str)) {
+                               log_error("Aborting. Unable to tag.");
+                               return 0;
+                       }
+
+               if (!vg_write(mirror_lv->vg) ||
+                   !vg_commit(mirror_lv->vg)) {
+                       log_error("Intermediate VG commit for orphan volume failed.");
+                       return 0;
+               }
+       }
+
+       if (!activate_lv(cmd, lv))
+               return_0;
+
+       if (!deactivate_lv(cmd, lv))
+               return_0;
+
+       if (!lv_remove(lv))
+               return_0;
+
+       return 1;
+}
+
+static int _merge_mirror_images(struct logical_volume *lv,
+                               const struct dm_list *mimages)
+{
+       uint32_t addition = dm_list_size(mimages);
+       struct logical_volume **img_lvs;
+       struct lv_list *lvl;
+       int i = 0;
+
+       if (!addition)
+               return 1;
+
+       if (!(img_lvs = alloca(sizeof(*img_lvs) * addition)))
+               return_0;
+
+       dm_list_iterate_items(lvl, mimages)
+               img_lvs[i++] = lvl->lv;
+
+       return lv_add_mirror_lvs(lv, img_lvs, addition,
+                                MIRROR_IMAGE, first_seg(lv)->region_size);
+}
+
+/* Unlink the relationship between the segment and its log_lv */
+struct logical_volume *detach_mirror_log(struct lv_segment *mirrored_seg)
+{
+       struct logical_volume *log_lv;
+
+       if (!mirrored_seg->log_lv)
+               return NULL;
+
+       log_lv = mirrored_seg->log_lv;
+       mirrored_seg->log_lv = NULL;
+       lv_set_visible(log_lv);
+       log_lv->status &= ~MIRROR_LOG;
+       remove_seg_from_segs_using_this_lv(log_lv, mirrored_seg);
+
+       return log_lv;
+}
+
+/* Check if mirror image LV is removable with regard to given removable_pvs */
+int is_mirror_image_removable(struct logical_volume *mimage_lv, void *baton)
+{
+       struct physical_volume *pv;
+       struct lv_segment *seg;
+       int pv_found;
+       struct pv_list *pvl;
+       uint32_t s;
+       struct dm_list *removable_pvs = baton;
+
+       if (!baton || dm_list_empty(removable_pvs))
+               return 1;
+
+       dm_list_iterate_items(seg, &mimage_lv->segments) {
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_PV) {
+                               /* FIXME Recurse for AREA_LV? */
+                               /* Structure of seg_lv is unknown.
+                                * Not removing this LV for safety. */
+                               return 0;
+                       }
+
+                       pv = seg_pv(seg, s);
+
+                       pv_found = 0;
+                       dm_list_iterate_items(pvl, removable_pvs) {
+                               if (id_equal(&pv->id, &pvl->pv->id)) {
+                                       pv_found = 1;
+                                       break;
+                               }
+                               if (pvl->pv->dev && pv->dev &&
+                                   pv->dev->dev == pvl->pv->dev->dev) {
+                                       pv_found = 1;
+                                       break;
+                               }
+                       }
+                       if (!pv_found)
+                               return 0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * _move_removable_mimages_to_end
+ *
+ * We always detach mimage LVs from the end of the areas array.
+ * This function will push 'count' mimages to the end of the array
+ * based on if their PVs are removable.
+ *
+ * This is an all or nothing function.  Either the user specifies
+ * enough removable PVs to satisfy count, or they don't specify
+ * any removable_pvs at all (in which case all PVs in the mirror
+ * are considered removable).
+ */
+static int _move_removable_mimages_to_end(struct logical_volume *lv,
+                                         uint32_t count,
+                                         struct dm_list *removable_pvs)
+{
+       int i;
+       struct logical_volume *sub_lv;
+       struct lv_segment *mirrored_seg = first_seg(lv);
+
+       if (!removable_pvs)
+               return 1;
+
+       for (i = mirrored_seg->area_count - 1; (i >= 0) && count; i--) {
+               sub_lv = seg_lv(mirrored_seg, i);
+
+               if (!is_temporary_mirror_layer(sub_lv) &&
+                   is_mirror_image_removable(sub_lv, removable_pvs)) {
+                       if (!shift_mirror_images(mirrored_seg, i))
+                               return_0;
+                       count--;
+               }
+       }
+
+       return !count;
+}
+
+static int _mirrored_lv_in_sync(struct logical_volume *lv)
+{
+       percent_t sync_percent;
+
+       if (!lv_mirror_percent(lv->vg->cmd, lv, 0, &sync_percent,
+                              NULL)) {
+               log_error("Unable to determine mirror sync status of %s/%s.",
+                         lv->vg->name, lv->name);
+               return 0;
+       }
+
+       return (sync_percent == PERCENT_100) ? 1 : 0;
+}
+
+/*
+ * Split off 'split_count' legs from a mirror
+ *
+ * Returns: 0 on error, 1 on success
+ */
+static int _split_mirror_images(struct logical_volume *lv,
+                               const char *split_name,
+                               uint32_t split_count,
+                               struct dm_list *removable_pvs)
+{
+       uint32_t i;
+       struct logical_volume *sub_lv = NULL;
+       struct logical_volume *new_lv = NULL;
+       struct logical_volume *detached_log_lv = NULL;
+       struct lv_segment *mirrored_seg = first_seg(lv);
+       struct dm_list split_images;
+       struct lv_list *lvl;
+
+       if (!(lv->status & MIRRORED)) {
+               log_error("Unable to split non-mirrored LV, %s",
+                         lv->name);
+               return 0;
+       }
+
+       if (!split_count) {
+               log_error("split_count is zero!");
+               return 0;
+       }
+
+       log_verbose("Detaching %d images from mirror, %s",
+                   split_count, lv->name);
+
+       if (!_move_removable_mimages_to_end(lv, split_count, removable_pvs)) {
+               /*
+                * FIXME: Allow incomplete specification of removable PVs?
+                *
+                * I am forcing the user to either specify no
+                * removable PVs or all of them.  Should we allow
+                * them to just specify some - making us pick the rest?
+                */
+               log_error("Insufficient removable PVs given"
+                         " to satisfy request");
+               return 0;
+       }
+
+       dm_list_init(&split_images);
+       for (i = 0; i < split_count; i++) {
+               mirrored_seg->area_count--;
+               sub_lv = seg_lv(mirrored_seg, mirrored_seg->area_count);
+
+               sub_lv->status &= ~MIRROR_IMAGE;
+               lv_set_visible(sub_lv);
+               release_lv_segment_area(mirrored_seg, mirrored_seg->area_count,
+                                       mirrored_seg->area_len);
+
+               log_very_verbose("%s assigned to be split", sub_lv->name);
+
+               if (!new_lv) {
+                       new_lv = sub_lv;
+                       new_lv->name = dm_pool_strdup(lv->vg->cmd->mem,
+                                                     split_name);
+                       if (!new_lv->name) {
+                               log_error("Unable to rename newly split LV");
+                               return 0;
+                       }
+               } else {
+                       lvl = dm_pool_alloc(lv->vg->cmd->mem, sizeof(*lvl));
+                       if (!lvl) {
+                               log_error("lv_list alloc failed");
+                               return 0;
+                       }
+                       lvl->lv = sub_lv;
+                       dm_list_add(&split_images, &lvl->list);
+               }
+       }
+
+       if (!dm_list_empty(&split_images)) {
+               size_t len = strlen(new_lv->name) + 32;
+               char *layer_name, format[len];
+
+               if (!insert_layer_for_lv(lv->vg->cmd, new_lv,
+                                        0, "_mimage_%d")) {
+                       log_error("Failed to build new mirror, %s",
+                                 new_lv->name);
+                       return 0;
+               }
+
+               first_seg(new_lv)->region_size = mirrored_seg->region_size;
+
+               dm_list_iterate_items(lvl, &split_images) {
+                       sub_lv = lvl->lv;
+
+                       dm_snprintf(format, len, "%s_mimage_%%d",
+                                   new_lv->name);
+
+                       layer_name = dm_pool_alloc(lv->vg->cmd->mem, len);
+                       if (!layer_name) {
+                               log_error("Unable to allocate memory");
+                               return 0;
+                       }
+                       if (!generate_lv_name(lv->vg, format, layer_name, len)||
+                           sscanf(layer_name, format, &i) != 1) {
+                               log_error("Failed to generate new image names");
+                               return 0;
+                       }
+                       sub_lv->name = layer_name;
+               }
+
+               if (!_merge_mirror_images(new_lv, &split_images)) {
+                       log_error("Failed to group split "
+                                 "images into new mirror");
+                       return 0;
+               }
+
+               /*
+                * We don't allow splitting a mirror that is not in-sync,
+                * so we can bring the newly split mirror up without a
+                * resync.  (It will be a 'core' log mirror after all.)
+                */
+               init_mirror_in_sync(1);
+       }
+
+       sub_lv = NULL;
+
+       /*
+        * If no more mirrors, remove mirror layer.
+        * The sub_lv is removed entirely later - leaving
+        * only the top-level (now linear) LV.
+        */
+       if (mirrored_seg->area_count == 1) {
+               sub_lv = seg_lv(mirrored_seg, 0);
+               sub_lv->status &= ~MIRROR_IMAGE;
+               lv_set_visible(sub_lv);
+               detached_log_lv = detach_mirror_log(mirrored_seg);
+               if (!remove_layer_from_lv(lv, sub_lv))
+                       return_0;
+               lv->status &= ~MIRRORED;
+               lv->status &= ~MIRROR_NOTSYNCED;
+       }
+
+       if (!vg_write(mirrored_seg->lv->vg)) {
+               log_error("Intermediate VG metadata write failed.");
+               return 0;
+       }
+
+       /*
+        * Suspend the original device and all its sub devices
+        */
+       if (!suspend_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+               log_error("Failed to lock %s", mirrored_seg->lv->name);
+               vg_revert(mirrored_seg->lv->vg);
+               return 0;
+       }
+
+       if (!vg_commit(mirrored_seg->lv->vg)) {
+               resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv);
+               return 0;
+       }
+
+       /* Bring newly split-off LV into existence */
+       if (!activate_lv(lv->vg->cmd, new_lv)) {
+               log_error("Failed to activate newly split LV, %s",
+                         new_lv->name);
+               return 0;
+       }
+
+       /* Resume altered original LV */
+       log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name);
+       if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+               log_error("Problem reactivating %s", mirrored_seg->lv->name);
+               return 0;
+       }
+
+       if (sub_lv && !_delete_lv(lv, sub_lv))
+               return_0;
+
+       if (detached_log_lv && !_delete_lv(lv, detached_log_lv))
+               return_0;
+
+       log_very_verbose("%" PRIu32 " image(s) detached from %s",
+                        split_count, lv->name);
+
+       return 1;
+}
+
+/*
+ * Remove num_removed images from mirrored_seg
+ *
+ * Arguments:
+ *   num_removed:   the requested (maximum) number of mirrors to be removed
+ *   removable_pvs: if not NULL and list not empty, only mirrors using PVs
+ *                  in this list will be removed
+ *   remove_log:    if non-zero, log_lv will be removed
+ *                  (even if it's 0, log_lv will be removed if there is no
+ *                   mirror remaining after the removal)
+ *   collapse:      if non-zero, instead of removing, remove the temporary
+ *                  mirror layer and merge mirrors to the original LV.
+ *                  removable_pvs should be NULL and num_removed should be
+ *                  seg->area_count - 1.
+ *   removed:       if non NULL, the number of removed mirror images is set
+ *                  as a result
+ *
+ * If collapse is non-zero, <removed> is guaranteed to be equal to num_removed.
+ *
+ * Return values:
+ *   Failure (0) means something unexpected has happend and
+ *   the caller should abort.
+ *   Even if no mirror was removed (e.g. no LV matches to 'removable_pvs'),
+ *   returns success (1).
+ */
+static int _remove_mirror_images(struct logical_volume *lv,
+                                uint32_t num_removed,
+                                int (*is_removable)(struct logical_volume *, void *),
+                                void *removable_baton,
+                                unsigned remove_log, unsigned collapse,
+                                uint32_t *removed)
+{
+       uint32_t m;
+       int32_t s;
+       struct logical_volume *sub_lv;
+       struct logical_volume *detached_log_lv = NULL;
+       struct logical_volume *temp_layer_lv = NULL;
+       struct lv_segment *mirrored_seg = first_seg(lv);
+       uint32_t old_area_count = mirrored_seg->area_count;
+       uint32_t new_area_count = mirrored_seg->area_count;
+       struct lv_list *lvl;
+       struct dm_list tmp_orphan_lvs;
+
+       if (removed)
+               *removed = 0;
+
+       log_very_verbose("Reducing mirror set from %" PRIu32 " to %"
+                        PRIu32 " image(s)%s.",
+                        old_area_count, old_area_count - num_removed,
+                        remove_log ? " and no log volume" : "");
+
+       if (collapse && (old_area_count - num_removed != 1)) {
+               log_error("Incompatible parameters to _remove_mirror_images");
+               return 0;
+       }
+
+       /* Move removable_pvs to end of array */
+       for (s = mirrored_seg->area_count - 1;
+            s >= 0 && old_area_count - new_area_count < num_removed;
+            s--) {
+               sub_lv = seg_lv(mirrored_seg, s);
+               if (!is_temporary_mirror_layer(sub_lv) &&
+                   is_removable(sub_lv, removable_baton)) {
+                       /*
+                        * Check if the user is trying to pull the
+                        * primary mirror image when the mirror is
+                        * not in-sync.
+                        */
+                       if ((s == 0) && !_mirrored_lv_in_sync(lv) &&
+                           !(lv->status & PARTIAL_LV)) {
+                               log_error("Unable to remove primary mirror image while mirror is not in-sync");
+                               return_0;
+                       }
+                       if (!shift_mirror_images(mirrored_seg, s))
+                               return_0;
+                       new_area_count--;
+               }
+       }
+
+       /*
+        * If removable_pvs were specified, then they have been shifted
+        * to the end to ensure they are removed.  The remaining balance
+        * of images left to remove will be taken from the unspecified.
+        */
+       new_area_count = old_area_count - num_removed;
+
+       if (num_removed && old_area_count == new_area_count)
+               return 1;
+
+       /* Remove mimage LVs from the segment */
+       dm_list_init(&tmp_orphan_lvs);
+       for (m = new_area_count; m < mirrored_seg->area_count; m++) {
+               seg_lv(mirrored_seg, m)->status &= ~MIRROR_IMAGE;
+               lv_set_visible(seg_lv(mirrored_seg, m));
+               if (!(lvl = dm_pool_alloc(lv->vg->cmd->mem, sizeof(*lvl)))) {
+                       log_error("lv_list alloc failed");
+                       return 0;
+               }
+               lvl->lv = seg_lv(mirrored_seg, m);
+               dm_list_add(&tmp_orphan_lvs, &lvl->list);
+               release_lv_segment_area(mirrored_seg, m, mirrored_seg->area_len);
+       }
+       mirrored_seg->area_count = new_area_count;
+
+       /* If no more mirrors, remove mirror layer */
+       /* As an exceptional case, if the lv is temporary layer,
+        * leave the LV as mirrored and let the lvconvert completion
+        * to remove the layer. */
+       if (new_area_count == 1 && !is_temporary_mirror_layer(lv)) {
+               temp_layer_lv = seg_lv(mirrored_seg, 0);
+               temp_layer_lv->status &= ~MIRROR_IMAGE;
+               lv_set_visible(temp_layer_lv);
+               detached_log_lv = detach_mirror_log(mirrored_seg);
+               if (!remove_layer_from_lv(lv, temp_layer_lv))
+                       return_0;
+               lv->status &= ~MIRRORED;
+               lv->status &= ~MIRROR_NOTSYNCED;
+               if (collapse && !_merge_mirror_images(lv, &tmp_orphan_lvs)) {
+                       log_error("Failed to add mirror images");
+                       return 0;
+               }
+               mirrored_seg = first_seg(lv);
+               if (remove_log && !detached_log_lv)
+                       detached_log_lv = detach_mirror_log(mirrored_seg);
+       } else if (new_area_count == 0) {
+               log_very_verbose("All mimages of %s are gone", lv->name);
+
+               /* All mirror images are gone.
+                * It can happen for vgreduce --removemissing. */
+               detached_log_lv = detach_mirror_log(mirrored_seg);
+               lv->status &= ~MIRRORED;
+               lv->status &= ~MIRROR_NOTSYNCED;
+               if (!replace_lv_with_error_segment(lv))
+                       return_0;
+       } else if (remove_log)
+               detached_log_lv = detach_mirror_log(mirrored_seg);
+
+       /*
+        * The log may be removed due to repair.  If the log
+        * happens to be a mirrored log, then there is a special
+        * case we need to consider.  One of the images of a
+        * mirrored log can fail followed shortly afterwards by
+        * a failure of the second.  This means that the top-level
+        * mirror is waiting for writes to the log to finish, but
+        * they never will unless the mirrored log can be repaired
+        * or replaced with an error target.  Since both the devices
+        * have failed, we must replace with error target - it is
+        * the only way to release the pending writes.
+        */
+       if (detached_log_lv && lv_is_mirrored(detached_log_lv) &&
+           (detached_log_lv->status & PARTIAL_LV)) {
+               struct lv_segment *seg = first_seg(detached_log_lv);
+
+               log_very_verbose("%s being removed due to failures",
+                                detached_log_lv->name);
+
+               /*
+                * We are going to replace the mirror with an
+                * error segment, but before we do, we must remember
+                * all of the LVs that must be deleted later (i.e.
+                * the sub-lv's)
+                */
+               for (m = 0; m < seg->area_count; m++) {
+                       seg_lv(seg, m)->status &= ~MIRROR_IMAGE;
+                       lv_set_visible(seg_lv(seg, m));
+                       if (!(lvl = dm_pool_alloc(lv->vg->cmd->mem,
+                                                 sizeof(*lvl)))) {
+                               log_error("dm_pool_alloc failed");
+                               return 0;
+                       }
+                       lvl->lv = seg_lv(seg, m);
+                       dm_list_add(&tmp_orphan_lvs, &lvl->list);
+               }
+
+               if (!replace_lv_with_error_segment(detached_log_lv)) {
+                       log_error("Failed error target substitution for %s",
+                                 detached_log_lv->name);
+                       return 0;
+               }
+
+               if (!vg_write(detached_log_lv->vg)) {
+                       log_error("intermediate VG write failed.");
+                       return 0;
+               }
+
+               if (!suspend_lv(detached_log_lv->vg->cmd,
+                               detached_log_lv)) {
+                       log_error("Failed to suspend %s",
+                                 detached_log_lv->name);
+                       return 0;
+               }
+
+               if (!vg_commit(detached_log_lv->vg)) {
+                       if (!resume_lv(detached_log_lv->vg->cmd,
+                                      detached_log_lv))
+                               stack;
+                       return_0;
+               }
+
+               if (!resume_lv(detached_log_lv->vg->cmd, detached_log_lv)) {
+                       log_error("Failed to resume %s",
+                                 detached_log_lv->name);
+                       return_0;
+               }
+       }
+
+       /*
+        * To successfully remove these unwanted LVs we need to
+        * remove the LVs from the mirror set, commit that metadata
+        * then deactivate and remove them fully.
+        */
+
+       if (!vg_write(mirrored_seg->lv->vg)) {
+               log_error("intermediate VG write failed.");
+               return 0;
+       }
+
+       if (!suspend_lv_origin(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+               log_error("Failed to lock %s", mirrored_seg->lv->name);
+               vg_revert(mirrored_seg->lv->vg);
+               return 0;
+       }
+
+       /* FIXME: second suspend should not be needed
+        * Explicitly suspend temporary LV
+        * This balance memlock_inc() calls with memlock_dec() in resume
+        * (both localy and in cluster) and also properly propagates precommited
+        * metadata into dm table on other nodes.
+        * (visible flag set causes the suspend is not properly propagated?)
+        */
+       if (temp_layer_lv && !suspend_lv(temp_layer_lv->vg->cmd, temp_layer_lv))
+               log_error("Problem suspending temporary LV %s", temp_layer_lv->name);
+
+       if (!vg_commit(mirrored_seg->lv->vg)) {
+               if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv))
+                       stack;
+               return_0;
+       }
+
+       log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name);
+
+       /*
+        * Avoid having same mirror target loaded twice simultaneously by first
+        * resuming the removed LV which now contains an error segment.
+        * As it's now detached from mirrored_seg->lv we must resume it
+        * explicitly.
+        */
+       if (temp_layer_lv && !resume_lv(temp_layer_lv->vg->cmd, temp_layer_lv)) {
+               log_error("Problem resuming temporary LV, %s", temp_layer_lv->name);
+               return 0;
+       }
+
+       if (!resume_lv_origin(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+               log_error("Problem reactivating %s", mirrored_seg->lv->name);
+               return 0;
+       }
+
+       /* Save or delete the 'orphan' LVs */
+       if (!collapse) {
+               dm_list_iterate_items(lvl, &tmp_orphan_lvs)
+                       if (!_delete_lv(lv, lvl->lv))
+                               return_0;
+       }
+
+       if (temp_layer_lv && !_delete_lv(lv, temp_layer_lv))
+               return_0;
+
+       if (detached_log_lv && !_delete_lv(lv, detached_log_lv))
+               return_0;
+
+       /* Mirror with only 1 area is 'in sync'. */
+       if (new_area_count == 1 && is_temporary_mirror_layer(lv)) {
+               if (first_seg(lv)->log_lv &&
+                   !_init_mirror_log(lv->vg->cmd, first_seg(lv)->log_lv,
+                                     1, &lv->tags, 0)) {
+                       /* As a result, unnecessary sync may run after
+                        * collapsing. But safe.*/
+                       log_error("Failed to initialize log device");
+                       return_0;
+               }
+       }
+
+       if (removed)
+               *removed = old_area_count - new_area_count;
+
+       log_very_verbose("%" PRIu32 " image(s) removed from %s",
+                        old_area_count - num_removed, lv->name);
+
+       return 1;
+}
+
+/*
+ * Remove the number of mirror images from the LV
+ */
+int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors,
+                        int (*is_removable)(struct logical_volume *, void *),
+                        void *removable_baton, unsigned remove_log)
+{
+       uint32_t num_removed, removed_once, r;
+       uint32_t existing_mirrors = lv_mirror_count(lv);
+       struct logical_volume *next_lv = lv;
+
+       num_removed = existing_mirrors - num_mirrors;
+
+       /* num_removed can be 0 if the function is called just to remove log */
+       do {
+               if (num_removed < first_seg(next_lv)->area_count)
+                       removed_once = num_removed;
+               else
+                       removed_once = first_seg(next_lv)->area_count - 1;
+
+               if (!_remove_mirror_images(next_lv, removed_once,
+                                          is_removable, removable_baton,
+                                          remove_log, 0, &r))
+                       return_0;
+
+               if (r < removed_once) {
+                       /* Some mirrors are removed from the temporary mirror,
+                        * but the temporary layer still exists.
+                        * Down the stack and retry for remainder. */
+                       next_lv = find_temporary_mirror(next_lv);
+               }
+
+               num_removed -= r;
+       } while (next_lv && num_removed);
+
+       if (num_removed) {
+               if (num_removed == existing_mirrors - num_mirrors)
+                       log_error("No mirror images found using specified PVs.");
+               else {
+                       log_error("%u images are removed out of requested %u.",
+                                 existing_mirrors - lv_mirror_count(lv),
+                                 existing_mirrors - num_mirrors);
+               }
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _no_removable_images(struct logical_volume *lv __attribute__((unused)),
+                               void *baton __attribute__((unused))) {
+       return 0;
+}
+
+/*
+ * Collapsing temporary mirror layers.
+ *
+ * When mirrors are added to already-mirrored LV, a temporary mirror layer
+ * is inserted at the top of the stack to reduce resync work.
+ * The function will remove the intermediate layer and collapse the stack
+ * as far as mirrors are in-sync.
+ *
+ * The function is destructive: to remove intermediate mirror layers,
+ * VG metadata commits and suspend/resume are necessary.
+ */
+int collapse_mirrored_lv(struct logical_volume *lv)
+{
+       struct logical_volume *tmp_lv;
+       struct lv_segment *mirror_seg;
+
+       while ((tmp_lv = find_temporary_mirror(lv))) {
+               mirror_seg = find_mirror_seg(first_seg(tmp_lv));
+               if (!mirror_seg) {
+                       log_error("Failed to find mirrored LV for %s",
+                                 tmp_lv->name);
+                       return 0;
+               }
+
+               if (!_mirrored_lv_in_sync(mirror_seg->lv)) {
+                       log_verbose("Not collapsing %s: out-of-sync",
+                                   mirror_seg->lv->name);
+                       return 1;
+               }
+
+               if (!_remove_mirror_images(mirror_seg->lv,
+                                          mirror_seg->area_count - 1,
+                                          _no_removable_images, NULL, 0, 1, NULL)) {
+                       log_error("Failed to release mirror images");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int get_mirror_fault_policy(struct cmd_context *cmd __attribute__((unused)),
+                                  int log_policy)
+{
+       const char *policy;
+
+       if (log_policy)
+               policy = find_config_str(NULL, "activation/mirror_log_fault_policy",
+                                        DEFAULT_MIRROR_LOG_FAULT_POLICY);
+       else {
+               policy = find_config_str(NULL, "activation/mirror_image_fault_policy",
+                                        NULL);
+               if (!policy)
+                       policy = find_config_str(NULL, "activation/mirror_device_fault_policy",
+                                                DEFAULT_MIRROR_IMAGE_FAULT_POLICY);
+       }
+
+       if (!strcmp(policy, "remove"))
+               return MIRROR_REMOVE;
+       else if (!strcmp(policy, "allocate"))
+               return MIRROR_ALLOCATE;
+       else if (!strcmp(policy, "allocate_anywhere"))
+               return MIRROR_ALLOCATE_ANYWHERE;
+
+       if (log_policy)
+               log_error("Bad activation/mirror_log_fault_policy");
+       else
+               log_error("Bad activation/mirror_device_fault_policy");
+
+       return MIRROR_REMOVE;
+}
+
+static int get_mirror_log_fault_policy(struct cmd_context *cmd)
+{
+       return get_mirror_fault_policy(cmd, 1);
+}
+
+static int get_mirror_device_fault_policy(struct cmd_context *cmd)
+{
+       return get_mirror_fault_policy(cmd, 0);
+}
+
+/*
+ * replace_mirror_images
+ * @mirrored_seg: segment (which may be linear now) to restore
+ * @num_mirrors: number of copies we should end up with
+ * @replace_log: replace log if not present
+ * @in_sync: was the original mirror in-sync?
+ *
+ * in_sync will be set to 0 if new mirror devices are being added
+ * In other words, it is only useful if the log (and only the log)
+ * is being restored.
+ *
+ * Returns: 0 on failure, 1 on reconfig, -1 if no reconfig done
+ */
+static int replace_mirror_images(struct lv_segment *mirrored_seg,
+                                uint32_t num_mirrors,
+                                int log_policy, int in_sync)
+{
+       int r = -1;
+       struct logical_volume *lv = mirrored_seg->lv;
+
+       /* FIXME: Use lvconvert rather than duplicating its code */
+
+       if (mirrored_seg->area_count < num_mirrors) {
+               log_warn("WARNING: Failed to replace mirror device in %s/%s",
+                        mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+
+               if ((mirrored_seg->area_count > 1) && !mirrored_seg->log_lv)
+                       log_warn("WARNING: Use 'lvconvert -m %d %s/%s --corelog' to replace failed devices",
+                                num_mirrors - 1, lv->vg->name, lv->name);
+               else
+                       log_warn("WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices",
+                                num_mirrors - 1, lv->vg->name, lv->name);
+               r = 0;
+
+               /* REMEMBER/FIXME: set in_sync to 0 if a new mirror device was added */
+               in_sync = 0;
+       }
+
+       /*
+        * FIXME: right now, we ignore the allocation policy specified to
+        * allocate the new log.
+        */
+       if ((mirrored_seg->area_count > 1) && !mirrored_seg->log_lv &&
+           (log_policy != MIRROR_REMOVE)) {
+               log_warn("WARNING: Failed to replace mirror log device in %s/%s",
+                        lv->vg->name, lv->name);
+
+               log_warn("WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices",
+                        mirrored_seg->area_count - 1 , lv->vg->name, lv->name);
+               r = 0;
+       }
+
+       return r;
+}
+
+int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
+                             struct dm_list *removable_pvs, unsigned remove_log)
+{
+       int r;
+       int in_sync;
+       int log_policy, dev_policy;
+       uint32_t old_num_mirrors = mirrored_seg->area_count;
+       int had_log = (mirrored_seg->log_lv) ? 1 : 0;
+
+       /* was the mirror in-sync before problems? */
+       in_sync = _mirrored_lv_in_sync(mirrored_seg->lv);
+
+       /*
+        * While we are only removing devices, we can have sync set.
+        * Setting this is only useful if we are moving to core log
+        * otherwise the disk log will contain the sync information
+        */
+       init_mirror_in_sync(in_sync);
+
+       r = _remove_mirror_images(mirrored_seg->lv, old_num_mirrors - num_mirrors,
+                                 is_mirror_image_removable, removable_pvs,
+                                 remove_log, 0, NULL);
+       if (!r)
+               /* Unable to remove bad devices */
+               return 0;
+
+       log_warn("WARNING: Bad device removed from mirror volume, %s/%s",
+                 mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+
+       log_policy = get_mirror_log_fault_policy(mirrored_seg->lv->vg->cmd);
+       dev_policy = get_mirror_device_fault_policy(mirrored_seg->lv->vg->cmd);
+
+       r = replace_mirror_images(mirrored_seg,
+                                 (dev_policy != MIRROR_REMOVE) ?
+                                 old_num_mirrors : num_mirrors,
+                                 log_policy, in_sync);
+
+       if (!r)
+               /* Failed to replace device(s) */
+               log_warn("WARNING: Unable to find substitute device for mirror volume, %s/%s",
+                        mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+       else if (r > 0)
+               /* Success in replacing device(s) */
+               log_warn("WARNING: Mirror volume, %s/%s restored - substitute for failed device found.",
+                         mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+       else
+               /* Bad device removed, but not replaced because of policy */
+               if (mirrored_seg->area_count == 1) {
+                       log_warn("WARNING: Mirror volume, %s/%s converted to linear due to device failure.",
+                                 mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+               } else if (had_log && !mirrored_seg->log_lv) {
+                       log_warn("WARNING: Mirror volume, %s/%s disk log removed due to device failure.",
+                                 mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+               }
+       /*
+        * If we made it here, we at least removed the bad device.
+        * Consider this success.
+        */
+       return 1;
+}
+
+static int _create_mimage_lvs(struct alloc_handle *ah,
+                             uint32_t num_mirrors,
+                             uint32_t stripes,
+                             uint32_t stripe_size,
+                             struct logical_volume *lv,
+                             struct logical_volume **img_lvs,
+                             int log)
+{
+       uint32_t m;
+       char *img_name;
+       size_t len;
+       
+       len = strlen(lv->name) + 32;
+       if (!(img_name = alloca(len))) {
+               log_error("img_name allocation failed. "
+                         "Remove new LV and retry.");
+               return 0;
+       }
+
+       if (dm_snprintf(img_name, len, "%s_mimage_%%d", lv->name) < 0) {
+               log_error("img_name allocation failed. "
+                         "Remove new LV and retry.");
+               return 0;
+       }
+
+       for (m = 0; m < num_mirrors; m++) {
+               if (!(img_lvs[m] = lv_create_empty(img_name,
+                                            NULL, LVM_READ | LVM_WRITE,
+                                            ALLOC_INHERIT, lv->vg))) {
+                       log_error("Aborting. Failed to create mirror image LV. "
+                                 "Remove new LV and retry.");
+                       return 0;
+               }
+
+               if (log) {
+                       if (!lv_add_log_segment(ah, m * stripes + 1, img_lvs[m], 0)) {
+                               log_error("Aborting. Failed to add mirror image segment "
+                                         "to %s. Remove new LV and retry.",
+                                         img_lvs[m]->name);
+                               return 0;
+                       }
+               } else {
+                       if (!lv_add_segment(ah, m * stripes, stripes, img_lvs[m],
+                                           get_segtype_from_string(lv->vg->cmd,
+                                                                   "striped"),
+                                           stripe_size, 0, 0)) {
+                               log_error("Aborting. Failed to add mirror image segment "
+                                         "to %s. Remove new LV and retry.",
+                                         img_lvs[m]->name);
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Remove mirrors from each segment.
+ * 'new_mirrors' is the number of mirrors after the removal. '0' for linear.
+ * If 'status_mask' is non-zero, the removal happens only when all segments
+ * has the status bits on.
+ */
+int remove_mirrors_from_segments(struct logical_volume *lv,
+                                uint32_t new_mirrors, uint64_t status_mask)
+{
+       struct lv_segment *seg;
+       uint32_t s;
+
+       /* Check the segment params are compatible */
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (!seg_is_mirrored(seg)) {
+                       log_error("Segment is not mirrored: %s:%" PRIu32,
+                                 lv->name, seg->le);
+                       return 0;
+               } if ((seg->status & status_mask) != status_mask) {
+                       log_error("Segment status does not match: %s:%" PRIu32
+                                 " status:0x%" PRIx64 "/0x%" PRIx64, lv->name, seg->le,
+                                 seg->status, status_mask);
+                       return 0;
+               }
+       }
+
+       /* Convert the segments */
+       dm_list_iterate_items(seg, &lv->segments) {
+               if (!new_mirrors && seg->extents_copied == seg->area_len) {
+                       if (!move_lv_segment_area(seg, 0, seg, 1))
+                               return_0;
+               }
+
+               for (s = new_mirrors + 1; s < seg->area_count; s++)
+                       release_lv_segment_area(seg, s, seg->area_len);
+
+               seg->area_count = new_mirrors + 1;
+
+               if (!new_mirrors)
+                       seg->segtype = get_segtype_from_string(lv->vg->cmd,
+                                                              "striped");
+       }
+
+       return 1;
+}
+
+const char *get_pvmove_pvname_from_lv_mirr(struct logical_volume *lv_mirr)
+{
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv_mirr->segments) {
+               if (!seg_is_mirrored(seg))
+                       continue;
+               if (seg_type(seg, 0) != AREA_PV)
+                       continue;
+               return dev_name(seg_dev(seg, 0));
+       }
+
+       return NULL;
+}
+
+const char *get_pvmove_pvname_from_lv(struct logical_volume *lv)
+{
+       struct lv_segment *seg;
+       uint32_t s;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               for (s = 0; s < seg->area_count; s++) {
+                       if (seg_type(seg, s) != AREA_LV)
+                               continue;
+                       return get_pvmove_pvname_from_lv_mirr(seg_lv(seg, s));
+               }
+       }
+
+       return NULL;
+}
+
+struct logical_volume *find_pvmove_lv(struct volume_group *vg,
+                                     struct device *dev,
+                                     uint32_t lv_type)
+{
+       struct lv_list *lvl;
+       struct logical_volume *lv;
+       struct lv_segment *seg;
+
+       /* Loop through all LVs */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv = lvl->lv;
+
+               if (!(lv->status & lv_type))
+                       continue;
+
+               /* Check segment origins point to pvname */
+               dm_list_iterate_items(seg, &lv->segments) {
+                       if (seg_type(seg, 0) != AREA_PV)
+                               continue;
+                       if (seg_dev(seg, 0) != dev)
+                               continue;
+                       return lv;
+               }
+       }
+
+       return NULL;
+}
+
+struct logical_volume *find_pvmove_lv_from_pvname(struct cmd_context *cmd,
+                                                 struct volume_group *vg,
+                                                 const char *name,
+                                                 const char *uuid __attribute__((unused)),
+                                                 uint32_t lv_type)
+{
+       struct physical_volume *pv;
+
+       if (!(pv = find_pv_by_name(cmd, name)))
+               return_NULL;
+
+       return find_pvmove_lv(vg, pv->dev, lv_type);
+}
+
+struct dm_list *lvs_using_lv(struct cmd_context *cmd, struct volume_group *vg,
+                         struct logical_volume *lv)
+{
+       struct dm_list *lvs;
+       struct logical_volume *lv1;
+       struct lv_list *lvl, *lvl1;
+       struct lv_segment *seg;
+       uint32_t s;
+
+       if (!(lvs = dm_pool_alloc(cmd->mem, sizeof(*lvs)))) {
+               log_error("lvs list alloc failed");
+               return NULL;
+       }
+
+       dm_list_init(lvs);
+
+       /* Loop through all LVs except the one supplied */
+       dm_list_iterate_items(lvl1, &vg->lvs) {
+               lv1 = lvl1->lv;
+               if (lv1 == lv)
+                       continue;
+
+               /* Find whether any segment points at the supplied LV */
+               dm_list_iterate_items(seg, &lv1->segments) {
+                       for (s = 0; s < seg->area_count; s++) {
+                               if (seg_type(seg, s) != AREA_LV ||
+                                   seg_lv(seg, s) != lv)
+                                       continue;
+                               if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+                                       log_error("lv_list alloc failed");
+                                       return NULL;
+                               }
+                               lvl->lv = lv1;
+                               dm_list_add(lvs, &lvl->list);
+                               goto next_lv;
+                       }
+               }
+             next_lv:
+               ;
+       }
+
+       return lvs;
+}
+
+percent_t copy_percent(struct logical_volume *lv_mirr)
+{
+       uint32_t numerator = 0u, denominator = 0u;
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(seg, &lv_mirr->segments) {
+               denominator += seg->area_len;
+
+               if (seg_is_mirrored(seg) && seg->area_count > 1)
+                       numerator += seg->extents_copied;
+               else
+                       numerator += seg->area_len;
+       }
+
+       return denominator ? make_percent( numerator, denominator ) : 100.0;
+}
+
+/*
+ * Fixup mirror pointers after single-pass segment import
+ */
+int fixup_imported_mirrors(struct volume_group *vg)
+{
+       struct lv_list *lvl;
+       struct lv_segment *seg;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               dm_list_iterate_items(seg, &lvl->lv->segments) {
+                       if (seg->segtype !=
+                           get_segtype_from_string(vg->cmd, "mirror"))
+                               continue;
+
+                       if (seg->log_lv && !add_seg_to_segs_using_this_lv(seg->log_lv, seg))
+                               return_0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Add mirrors to "linear" or "mirror" segments
+ */
+int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv,
+                           uint32_t mirrors, uint32_t region_size,
+                           struct dm_list *allocatable_pvs, alloc_policy_t alloc)
+{
+       struct alloc_handle *ah;
+       const struct segment_type *segtype;
+       struct dm_list *parallel_areas;
+       uint32_t adjusted_region_size;
+       int r = 1;
+
+       if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv, 1)))
+               return_0;
+
+       if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+               return_0;
+
+       adjusted_region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+                                                          lv->le_count,
+                                                          region_size);
+
+       if (!(ah = allocate_extents(lv->vg, NULL, segtype, 1, mirrors, 0, 0,
+                                   lv->le_count, allocatable_pvs, alloc,
+                                   parallel_areas))) {
+               log_error("Unable to allocate mirror extents for %s.", lv->name);
+               return 0;
+       }
+
+       if (!lv_add_mirror_areas(ah, lv, 0, adjusted_region_size)) {
+               log_error("Failed to add mirror areas to %s", lv->name);
+               r = 0;
+       }
+
+       alloc_destroy(ah);
+       return r;
+}
+
+/*
+ * Convert mirror log
+ *
+ * FIXME: Can't handle segment-by-segment mirror (like pvmove)
+ */
+int remove_mirror_log(struct cmd_context *cmd,
+                     struct logical_volume *lv,
+                     struct dm_list *removable_pvs,
+                     int force)
+{
+       percent_t sync_percent;
+       struct lvinfo info;
+       struct volume_group *vg = lv->vg;
+
+       /* Unimplemented features */
+       if (dm_list_size(&lv->segments) != 1) {
+               log_error("Multiple-segment mirror is not supported");
+               return 0;
+       }
+
+       /* Had disk log, switch to core. */
+       if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+               if (!lv_mirror_percent(cmd, lv, 0, &sync_percent,
+                                      NULL)) {
+                       log_error("Unable to determine mirror sync status.");
+                       return 0;
+               }
+       } else if (vg_is_clustered(vg)) {
+               log_error("Unable to convert the log of an inactive "
+                         "cluster mirror, %s", lv->name);
+               return 0;
+       } else if (force || yes_no_prompt("Full resync required to convert "
+                                "inactive mirror %s to core log. "
+                                "Proceed? [y/n]: ", lv->name) == 'y')
+               sync_percent = 0;
+       else
+               return 0;
+
+       if (sync_percent == PERCENT_100)
+               init_mirror_in_sync(1);
+       else {
+               /* A full resync will take place */
+               lv->status &= ~MIRROR_NOTSYNCED;
+               init_mirror_in_sync(0);
+       }
+
+       if (!remove_mirror_images(lv, lv_mirror_count(lv),
+                                 is_mirror_image_removable, removable_pvs, 1U))
+               return_0;
+
+       return 1;
+}
+
+static struct logical_volume *_create_mirror_log(struct logical_volume *lv,
+                                                struct alloc_handle *ah,
+                                                alloc_policy_t alloc,
+                                                const char *lv_name,
+                                                const char *suffix)
+{
+       struct logical_volume *log_lv;
+       char *log_name;
+       size_t len;
+
+       len = strlen(lv_name) + 32;
+       if (!(log_name = alloca(len))) {
+               log_error("log_name allocation failed.");
+               return NULL;
+       }
+
+       if (dm_snprintf(log_name, len, "%s%s", lv_name, suffix) < 0) {
+               log_error("log_name allocation failed.");
+               return NULL;
+       }
+
+       if (!(log_lv = lv_create_empty(log_name, NULL,
+                                      VISIBLE_LV | LVM_READ | LVM_WRITE,
+                                      alloc, lv->vg)))
+               return_NULL;
+
+       if (!lv_add_log_segment(ah, 0, log_lv, MIRROR_LOG))
+               return_NULL;
+
+       return log_lv;
+}
+
+/*
+ * Returns: 1 on success, 0 on error
+ */
+static int _form_mirror(struct cmd_context *cmd, struct alloc_handle *ah,
+                       struct logical_volume *lv,
+                       uint32_t mirrors, uint32_t stripes,
+                       uint32_t stripe_size, uint32_t region_size, int log)
+{
+       struct logical_volume **img_lvs;
+
+       /*
+        * insert a mirror layer
+        */
+       if (dm_list_size(&lv->segments) != 1 ||
+           seg_type(first_seg(lv), 0) != AREA_LV)
+               if (!insert_layer_for_lv(cmd, lv, 0, "_mimage_%d"))
+                       return 0;
+
+       /*
+        * create mirror image LVs
+        */
+       if (!(img_lvs = alloca(sizeof(*img_lvs) * mirrors))) {
+               log_error("img_lvs allocation failed. "
+                         "Remove new LV and retry.");
+               return 0;
+       }
+
+       if (!_create_mimage_lvs(ah, mirrors, stripes, stripe_size, lv, img_lvs, log))
+               return 0;
+
+       if (!lv_add_mirror_lvs(lv, img_lvs, mirrors,
+                              MIRROR_IMAGE | (lv->status & LOCKED),
+                              region_size)) {
+               log_error("Aborting. Failed to add mirror segment. "
+                         "Remove new LV and retry.");
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct logical_volume *_set_up_mirror_log(struct cmd_context *cmd,
+                                                struct alloc_handle *ah,
+                                                struct logical_volume *lv,
+                                                uint32_t log_count,
+                                                uint32_t region_size,
+                                                alloc_policy_t alloc,
+                                                int in_sync)
+{
+       struct logical_volume *log_lv;
+       const char *suffix, *c;
+       char *lv_name;
+       size_t len;
+       struct lv_segment *seg;
+
+       init_mirror_in_sync(in_sync);
+
+       /* Mirror log name is lv_name + suffix, determined as the following:
+        *   1. suffix is:
+        *        o "_mlog" for the original mirror LV.
+        *        o "_mlogtmp_%d" for temporary mirror LV,
+        *   2. lv_name is:
+        *        o lv->name, if the log is temporary
+        *        o otherwise, the top-level LV name
+        */
+       seg = first_seg(lv);
+       if (seg_type(seg, 0) == AREA_LV &&
+           strstr(seg_lv(seg, 0)->name, MIRROR_SYNC_LAYER)) {
+               lv_name = lv->name;
+               suffix = "_mlogtmp_%d";
+       } else if ((c = strstr(lv->name, MIRROR_SYNC_LAYER))) {
+               len = c - lv->name + 1;
+               if (!(lv_name = alloca(len)) ||
+                   !dm_snprintf(lv_name, len, "%s", lv->name)) {
+                       log_error("mirror log name allocation failed");
+                       return 0;
+               }
+               suffix = "_mlog";
+       } else {
+               lv_name = lv->name;
+               suffix = "_mlog";
+       }
+
+       if (!(log_lv = _create_mirror_log(lv, ah, alloc,
+                                         (const char *) lv_name, suffix))) {
+               log_error("Failed to create mirror log.");
+               return NULL;
+       }
+
+       if ((log_count > 1) &&
+           !_form_mirror(cmd, ah, log_lv, log_count-1, 1, 0, region_size, 1)) {
+               log_error("Failed to form mirrored log.");
+               return NULL;
+       }
+
+       if (!_init_mirror_log(cmd, log_lv, in_sync, &lv->tags, 1)) {
+               log_error("Failed to initialise mirror log.");
+               return NULL;
+       }
+
+       return log_lv;
+}
+
+int attach_mirror_log(struct lv_segment *seg, struct logical_volume *log_lv)
+{
+       seg->log_lv = log_lv;
+       log_lv->status |= MIRROR_LOG;
+       lv_set_hidden(log_lv);
+       return add_seg_to_segs_using_this_lv(log_lv, seg);
+}
+
+int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+                  uint32_t log_count, uint32_t region_size,
+                  struct dm_list *allocatable_pvs, alloc_policy_t alloc)
+{
+       struct alloc_handle *ah;
+       const struct segment_type *segtype;
+       struct dm_list *parallel_areas;
+       percent_t sync_percent;
+       int in_sync;
+       struct logical_volume *log_lv;
+       struct lvinfo info;
+       int r = 0;
+
+       if (dm_list_size(&lv->segments) != 1) {
+               log_error("Multiple-segment mirror is not supported");
+               return 0;
+       }
+
+       /*
+        * We are unable to convert the log of inactive cluster mirrors
+        * due to the inability to detect whether the mirror is active
+        * on remote nodes (even though it is inactive on this node)
+        */
+       if (vg_is_clustered(lv->vg) &&
+           !(lv_info(cmd, lv, 0, &info, 0, 0) && info.exists)) {
+               log_error("Unable to convert the log of inactive "
+                         "cluster mirror %s", lv->name);
+               return 0;
+       }
+
+       if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv, 0)))
+               return_0;
+
+       if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+               return_0;
+
+       if (activation() && segtype->ops->target_present &&
+           !segtype->ops->target_present(cmd, NULL, NULL)) {
+               log_error("%s: Required device-mapper target(s) not "
+                         "detected in your kernel", segtype->name);
+               return 0;
+       }
+
+       /* allocate destination extents */
+       ah = allocate_extents(lv->vg, NULL, segtype,
+                             0, 0, log_count, region_size, 0,
+                             allocatable_pvs, alloc, parallel_areas);
+       if (!ah) {
+               log_error("Unable to allocate extents for mirror log.");
+               return 0;
+       }
+
+       /* check sync status */
+       if (lv_mirror_percent(cmd, lv, 0, &sync_percent, NULL) &&
+           (sync_percent == PERCENT_100))
+               in_sync = 1;
+       else
+               in_sync = 0;
+
+       if (!(log_lv = _set_up_mirror_log(cmd, ah, lv, log_count,
+                                         region_size, alloc, in_sync)))
+               goto_out;
+
+       if (!attach_mirror_log(first_seg(lv), log_lv))
+               goto_out;
+
+       r = 1;
+out:
+       alloc_destroy(ah);
+       return r;
+}
+
+/*
+ * Convert "linear" LV to "mirror".
+ */
+int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
+                     uint32_t mirrors, uint32_t stripes,
+                     uint32_t stripe_size, uint32_t region_size,
+                     struct dm_list *allocatable_pvs, alloc_policy_t alloc,
+                     uint32_t log_count)
+{
+       struct alloc_handle *ah;
+       const struct segment_type *segtype;
+       struct dm_list *parallel_areas;
+       struct logical_volume *log_lv = NULL;
+
+       /*
+        * allocate destination extents
+        */
+
+       if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv, 0)))
+               return_0;
+
+       if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+               return_0;
+
+       ah = allocate_extents(lv->vg, NULL, segtype,
+                             stripes, mirrors, log_count, region_size, lv->le_count,
+                             allocatable_pvs, alloc, parallel_areas);
+       if (!ah) {
+               log_error("Unable to allocate extents for mirror(s).");
+               return 0;
+       }
+
+       /*
+        * create and initialize mirror log
+        */
+       if (log_count &&
+           !(log_lv = _set_up_mirror_log(cmd, ah, lv, log_count,
+                                         (region_size > lv->vg->extent_size) ?
+                                         lv->vg->extent_size : region_size,
+                                         alloc, mirror_in_sync()))) {
+               stack;
+               goto out_remove_images;
+       }
+
+       /* The log initialization involves vg metadata commit.
+          So from here on, if failure occurs, the log must be explicitly
+          removed and the updated vg metadata should be committed. */
+
+       if (!_form_mirror(cmd, ah, lv, mirrors, stripes, stripe_size, region_size, 0))
+               goto out_remove_log;
+
+       if (log_count && !attach_mirror_log(first_seg(lv), log_lv))
+               stack;
+
+       alloc_destroy(ah);
+       return 1;
+
+  out_remove_log:
+       if (log_lv) {
+               if (!lv_remove(log_lv) ||
+                   !vg_write(log_lv->vg) ||
+                   !vg_commit(log_lv->vg))
+                       log_error("Manual intervention may be required to remove "
+                                 "abandoned log LV before retrying.");
+               else
+                       backup(log_lv->vg);
+       }
+  out_remove_images:
+       alloc_destroy(ah);
+       return 0;
+}
+
+/*
+ * Generic interface for adding mirror and/or mirror log.
+ * 'mirror' is the number of mirrors to be added.
+ * 'pvs' is either allocatable pvs.
+ */
+int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
+                  uint32_t mirrors, uint32_t stripes, uint32_t stripe_size,
+                  uint32_t region_size, uint32_t log_count,
+                  struct dm_list *pvs, alloc_policy_t alloc, uint32_t flags)
+{
+       if (!mirrors && !log_count) {
+               log_error("No conversion is requested");
+               return 0;
+       }
+
+       if (vg_is_clustered(lv->vg)) {
+               if (!(lv->status & ACTIVATE_EXCL) &&
+                   !cluster_mirror_is_available(lv)) {
+                       log_error("Shared cluster mirrors are not available.");
+                       return 0;
+               }
+
+               /*
+                * No mirrored logs for cluster mirrors until
+                * log daemon is multi-threaded.
+                */
+               if (log_count > 1) {
+                       log_error("Log type, \"mirrored\", is unavailable to cluster mirrors");
+                       return 0;
+               }
+       }
+
+       /* For corelog mirror, activation code depends on
+        * the global mirror_in_sync status. As we are adding
+        * a new mirror, it should be set as 'out-of-sync'
+        * so that the sync starts. */
+       /* However, MIRROR_SKIP_INIT_SYNC even overrides it. */
+       if (flags & MIRROR_SKIP_INIT_SYNC)
+               init_mirror_in_sync(1);
+       else if (!log_count)
+               init_mirror_in_sync(0);
+
+       if (flags & MIRROR_BY_SEG) {
+               if (log_count) {
+                       log_error("Persistent log is not supported on "
+                                 "segment-by-segment mirroring");
+                       return 0;
+               }
+               if (stripes > 1) {
+                       log_error("Striped-mirroring is not supported on "
+                                 "segment-by-segment mirroring");
+                       return 0;
+               }
+
+               return add_mirrors_to_segments(cmd, lv, mirrors,
+                                              region_size, pvs, alloc);
+       } else if (flags & MIRROR_BY_LV) {
+               if (!mirrors)
+                       return add_mirror_log(cmd, lv, log_count,
+                                             region_size, pvs, alloc);
+               return add_mirror_images(cmd, lv, mirrors,
+                                        stripes, stripe_size, region_size,
+                                        pvs, alloc, log_count);
+       }
+
+       log_error("Unsupported mirror conversion type");
+       return 0;
+}
+
+int lv_split_mirror_images(struct logical_volume *lv, const char *split_name,
+                          uint32_t split_count, struct dm_list *removable_pvs)
+{
+       int r;
+
+       if (find_lv_in_vg(lv->vg, split_name)) {
+               log_error("Logical Volume \"%s\" already exists in "
+                         "volume group \"%s\"", split_name, lv->vg->name);
+               return 0;
+       }
+
+       /* Can't split a mirror that is not in-sync... unless force? */
+       if (!_mirrored_lv_in_sync(lv)) {
+               log_error("Unable to split mirror that is not in-sync.");
+               return_0;
+       }
+
+       /*
+        * FIXME: Generate default name when not supplied.
+        *
+        * If we were going to generate a default name, we would
+        * do it here.  Better to wait for a decision on the form
+        * of the default name when '--track_deltas' (the ability
+        * to merge a split leg back in and only copy the changes)
+        * is being implemented.  For now, we force the user to
+        * come up with a name for their LV.
+        */
+       r = _split_mirror_images(lv, split_name, split_count, removable_pvs);
+       if (!r)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Generic interface for removing mirror and/or mirror log.
+ * 'mirror' is the number of mirrors to be removed.
+ * 'pvs' is removable pvs.
+ */
+int lv_remove_mirrors(struct cmd_context *cmd __attribute__((unused)),
+                     struct logical_volume *lv,
+                     uint32_t mirrors, uint32_t log_count,
+                     int (*is_removable)(struct logical_volume *, void *),
+                     void *removable_baton,
+                     uint64_t status_mask)
+{
+       uint32_t new_mirrors;
+       struct lv_segment *seg;
+
+       if (!mirrors && !log_count) {
+               log_error("No conversion is requested");
+               return 0;
+       }
+
+       seg = first_seg(lv);
+       if (!seg_is_mirrored(seg)) {
+               log_error("Not a mirror segment");
+               return 0;
+       }
+
+       if (lv_mirror_count(lv) <= mirrors) {
+               log_error("Removing more than existing: %d <= %d",
+                         seg->area_count, mirrors);
+               return 0;
+       }
+       new_mirrors = lv_mirror_count(lv) - mirrors - 1;
+
+       /* MIRROR_BY_LV */
+       if (seg_type(seg, 0) == AREA_LV &&
+           seg_lv(seg, 0)->status & MIRROR_IMAGE)
+               return remove_mirror_images(lv, new_mirrors + 1,
+                                           is_removable, removable_baton,
+                                           log_count ? 1U : 0);
+
+       /* MIRROR_BY_SEG */
+       if (log_count) {
+               log_error("Persistent log is not supported on "
+                         "segment-by-segment mirroring");
+               return 0;
+       }
+       return remove_mirrors_from_segments(lv, new_mirrors, status_mask);
+}
+
diff --git a/lib/metadata/pv.c b/lib/metadata/pv.c
new file mode 100644 (file)
index 0000000..e5bb9f0
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "lvmcache.h"
+
+/*
+ * FIXME: Check for valid handle before dereferencing field or log error?
+ */
+#define pv_field(handle, field)        ((handle)->field)
+
+char *pv_fmt_dup(const struct physical_volume *pv)
+{
+       if (!pv->fmt)
+               return NULL;
+       return dm_pool_strdup(pv->vg->vgmem, pv->fmt->name);
+}
+
+char *pv_name_dup(const struct physical_volume *pv)
+{
+       return dm_pool_strdup(pv->vg->vgmem, dev_name(pv->dev));
+}
+
+/*
+ * Gets/Sets for external LVM library
+ */
+struct id pv_id(const struct physical_volume *pv)
+{
+       return pv_field(pv, id);
+}
+
+char *pv_uuid_dup(const struct physical_volume *pv)
+{
+       return id_format_and_copy(pv->vg->vgmem, &pv->id);
+}
+
+char *pv_tags_dup(const struct physical_volume *pv)
+{
+       return tags_format_and_copy(pv->vg->vgmem, &pv->tags);
+}
+
+const struct format_type *pv_format_type(const struct physical_volume *pv)
+{
+       return pv_field(pv, fmt);
+}
+
+struct id pv_vgid(const struct physical_volume *pv)
+{
+       return pv_field(pv, vgid);
+}
+
+struct device *pv_dev(const struct physical_volume *pv)
+{
+       return pv_field(pv, dev);
+}
+
+const char *pv_vg_name(const struct physical_volume *pv)
+{
+       return pv_field(pv, vg_name);
+}
+
+const char *pv_dev_name(const struct physical_volume *pv)
+{
+       return dev_name(pv_dev(pv));
+}
+
+uint64_t pv_size(const struct physical_volume *pv)
+{
+       return pv_field(pv, size);
+}
+
+uint64_t pv_dev_size(const struct physical_volume *pv)
+{
+       uint64_t size;
+
+       if (!dev_get_size(pv->dev, &size))
+               size = 0;
+       return size;
+}
+
+uint64_t pv_size_field(const struct physical_volume *pv)
+{
+       uint64_t size;
+
+       if (!pv->pe_count)
+               size = pv->size;
+       else
+               size = (uint64_t) pv->pe_count * pv->pe_size;
+       return size;
+}
+
+uint64_t pv_free(const struct physical_volume *pv)
+{
+       uint64_t freespace;
+
+       if (!pv->pe_count)
+               freespace = pv->size;
+       else
+               freespace = (uint64_t)
+                       (pv->pe_count - pv->pe_alloc_count) * pv->pe_size;
+       return freespace;
+}
+
+uint64_t pv_status(const struct physical_volume *pv)
+{
+       return pv_field(pv, status);
+}
+
+uint32_t pv_pe_size(const struct physical_volume *pv)
+{
+       return pv_field(pv, pe_size);
+}
+
+uint64_t pv_pe_start(const struct physical_volume *pv)
+{
+       return pv_field(pv, pe_start);
+}
+
+uint32_t pv_pe_count(const struct physical_volume *pv)
+{
+       return pv_field(pv, pe_count);
+}
+
+uint32_t pv_pe_alloc_count(const struct physical_volume *pv)
+{
+       return pv_field(pv, pe_alloc_count);
+}
+
+uint32_t pv_mda_count(const struct physical_volume *pv)
+{
+       struct lvmcache_info *info;
+
+       info = info_from_pvid((const char *)&pv->id.uuid, 0);
+       return info ? dm_list_size(&info->mdas) : UINT64_C(0);
+}
+
+uint32_t pv_mda_used_count(const struct physical_volume *pv)
+{
+       struct lvmcache_info *info;
+       struct metadata_area *mda;
+       uint32_t used_count=0;
+
+       info = info_from_pvid((const char *)&pv->id.uuid, 0);
+       if (!info)
+               return 0;
+       dm_list_iterate_items(mda, &info->mdas) {
+               if (!mda_is_ignored(mda))
+                       used_count++;
+       }
+       return used_count;
+}
+
+/**
+ * is_orphan - Determine whether a pv is an orphan based on its vg_name
+ * @pv: handle to the physical volume
+ */
+int is_orphan(const struct physical_volume *pv)
+{
+       return is_orphan_vg(pv_field(pv, vg_name));
+}
+
+/**
+ * is_pv - Determine whether a pv is a real pv or dummy one
+ * @pv: handle to device
+ */
+int is_pv(const struct physical_volume *pv)
+{
+       return (pv_field(pv, vg_name) ? 1 : 0);
+}
+
+int is_missing_pv(const struct physical_volume *pv)
+{
+       return pv_field(pv, status) & MISSING_PV ? 1 : 0;
+}
+
+char *pv_attr_dup(struct dm_pool *mem, const struct physical_volume *pv)
+{
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(mem, 3))) {
+               log_error("dm_pool_alloc failed");
+               return NULL;
+       }
+
+       repstr[0] = (pv->status & ALLOCATABLE_PV) ? 'a' : '-';
+       repstr[1] = (pv->status & EXPORTED_VG) ? 'x' : '-';
+       return repstr;
+}
+
+uint64_t pv_mda_size(const struct physical_volume *pv)
+{
+       struct lvmcache_info *info;
+       uint64_t min_mda_size = 0;
+       const char *pvid = (const char *)(&pv->id.uuid);
+
+       /* PVs could have 2 mdas of different sizes (rounding effect) */
+       if ((info = info_from_pvid(pvid, 0)))
+               min_mda_size = find_min_mda_size(&info->mdas);
+       return min_mda_size;
+}
+
+uint64_t pv_mda_free(const struct physical_volume *pv)
+{
+       struct lvmcache_info *info;
+       uint64_t freespace = UINT64_MAX, mda_free;
+       const char *pvid = (const char *)&pv->id.uuid;
+       struct metadata_area *mda;
+
+       if ((info = info_from_pvid(pvid, 0)))
+               dm_list_iterate_items(mda, &info->mdas) {
+                       if (!mda->ops->mda_free_sectors)
+                               continue;
+                       mda_free = mda->ops->mda_free_sectors(mda);
+                       if (mda_free < freespace)
+                               freespace = mda_free;
+               }
+
+       if (freespace == UINT64_MAX)
+               freespace = UINT64_C(0);
+       return freespace;
+}
+
+uint64_t pv_used(const struct physical_volume *pv)
+{
+       uint64_t used;
+
+       if (!pv->pe_count)
+               used = 0LL;
+       else
+               used = (uint64_t) pv->pe_alloc_count * pv->pe_size;
+       return used;
+}
+
+unsigned pv_mda_set_ignored(const struct physical_volume *pv, unsigned mda_ignored)
+{
+       struct lvmcache_info *info;
+       struct metadata_area *mda, *vg_mda, *tmda;
+       struct dm_list *vg_mdas_in_use, *vg_mdas_ignored;
+
+       if (!(info = info_from_pvid((const char *)&pv->id.uuid, 0)))
+               return_0;
+
+       if (is_orphan(pv)) {
+               dm_list_iterate_items(mda, &info->mdas)
+                       mda_set_ignored(mda, mda_ignored);
+               return 1;
+       }
+
+       /*
+        * Do not allow disabling of the the last PV in a VG.
+        */
+       if (pv_mda_used_count(pv) == vg_mda_used_count(pv->vg)) {
+               log_error("Cannot disable all metadata areas in volume group %s.",
+                         pv->vg->name);
+               return 0;
+       }
+
+       /*
+        * Non-orphan case is more complex.
+        * If the PV's mdas are ignored, and we wish to un-ignore,
+        * we clear the bit and move them from the ignored mda list to the
+        * in_use list, ensuring the new state will get written to disk
+        * in the vg_write() path.
+        * If the PV's mdas are not ignored, and we are setting
+        * them to ignored, we set the bit but leave them on the in_use
+        * list, ensuring the new state will get written to disk in the
+        * vg_write() path.
+        */
+       vg_mdas_in_use = &pv->vg->fid->metadata_areas_in_use;
+       vg_mdas_ignored = &pv->vg->fid->metadata_areas_ignored;
+
+       dm_list_iterate_items(mda, &info->mdas) {
+               if (mda_is_ignored(mda) && !mda_ignored)
+                       /* Changing an ignored mda to one in_use requires moving it */
+                       dm_list_iterate_items_safe(vg_mda, tmda, vg_mdas_ignored)
+                               if (mda_locns_match(mda, vg_mda)) {
+                                       mda_set_ignored(vg_mda, mda_ignored);
+                                       dm_list_move(vg_mdas_in_use, &vg_mda->list);
+                               }
+
+               dm_list_iterate_items_safe(vg_mda, tmda, vg_mdas_in_use)
+                       if (mda_locns_match(mda, vg_mda))
+                               /* Don't move mda: needs writing to disk. */
+                               mda_set_ignored(vg_mda, mda_ignored);
+
+               mda_set_ignored(mda, mda_ignored);
+       }
+
+       return 1;
+}
+
diff --git a/lib/metadata/pv.h b/lib/metadata/pv.h
new file mode 100644 (file)
index 0000000..a02f6f8
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_PV_H
+#define _LVM_PV_H
+
+struct id;
+struct device;
+struct format_type;
+struct volume_group;
+
+struct physical_volume {
+       struct id id;
+       struct device *dev;
+       const struct format_type *fmt;
+
+       /*
+        * vg_name and vgid are used before the parent VG struct exists.
+        * FIXME: Investigate removal/substitution with 'vg' fields.
+        */
+       const char *vg_name;
+       struct id vgid;
+
+       /*
+        * 'vg' is set and maintained when the PV belongs to a 'pvs'
+        * list in a parent VG struct.
+        */
+       struct volume_group *vg;
+
+       uint64_t status;
+       uint64_t size;
+
+       /* physical extents */
+       uint32_t pe_size;
+       uint64_t pe_start;
+       uint32_t pe_count;
+       uint32_t pe_alloc_count;
+       unsigned long pe_align;
+       unsigned long pe_align_offset;
+
+       struct dm_list segments;        /* Ordered pv_segments covering complete PV */
+       struct dm_list tags;
+};
+
+char *pv_fmt_dup(const struct physical_volume *pv);
+char *pv_name_dup(const struct physical_volume *pv);
+struct device *pv_dev(const struct physical_volume *pv);
+const char *pv_vg_name(const struct physical_volume *pv);
+char *pv_attr_dup(struct dm_pool *mem, const struct physical_volume *pv);
+const char *pv_dev_name(const struct physical_volume *pv);
+char *pv_uuid_dup(const struct physical_volume *pv);
+char *pv_tags_dup(const struct physical_volume *pv);
+uint64_t pv_size(const struct physical_volume *pv);
+uint64_t pv_size_field(const struct physical_volume *pv);
+uint64_t pv_dev_size(const struct physical_volume *pv);
+uint64_t pv_free(const struct physical_volume *pv);
+uint64_t pv_status(const struct physical_volume *pv);
+uint32_t pv_pe_size(const struct physical_volume *pv);
+uint64_t pv_pe_start(const struct physical_volume *pv);
+uint32_t pv_pe_count(const struct physical_volume *pv);
+uint32_t pv_pe_alloc_count(const struct physical_volume *pv);
+uint64_t pv_mda_size(const struct physical_volume *pv);
+uint64_t pv_mda_free(const struct physical_volume *pv);
+uint64_t pv_used(const struct physical_volume *pv);
+uint32_t pv_mda_count(const struct physical_volume *pv);
+uint32_t pv_mda_used_count(const struct physical_volume *pv);
+unsigned pv_mda_set_ignored(const struct physical_volume *pv, unsigned ignored);
+int is_orphan(const struct physical_volume *pv);
+int is_missing_pv(const struct physical_volume *pv);
+int is_pv(const struct physical_volume *pv);
+
+#endif /* _LVM_PV_H */
diff --git a/lib/metadata/pv_alloc.h b/lib/metadata/pv_alloc.h
new file mode 100644 (file)
index 0000000..d894f31
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_PV_ALLOC_H
+
+int alloc_pv_segment_whole_pv(struct dm_pool *mem, struct physical_volume *pv);
+int peg_dup(struct dm_pool *mem, struct dm_list *peg_new, struct dm_list *peg_old);
+struct pv_segment *assign_peg_to_lvseg(struct physical_volume *pv, uint32_t pe,
+                                      uint32_t area_len,
+                                      struct lv_segment *seg,
+                                      uint32_t area_num);
+int pv_split_segment(struct dm_pool *mem,
+                    struct physical_volume *pv, uint32_t pe,
+                    struct pv_segment **pvseg_allocated);
+int release_pv_segment(struct pv_segment *peg, uint32_t area_reduction);
+int check_pv_segments(struct volume_group *vg);
+void merge_pv_segments(struct pv_segment *peg1, struct pv_segment *peg2);
+
+#endif
diff --git a/lib/metadata/pv_manip.c b/lib/metadata/pv_manip.c
new file mode 100644 (file)
index 0000000..e6fe6c2
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2003 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "pv_alloc.h"
+#include "toolcontext.h"
+#include "archiver.h"
+#include "locking.h"
+#include "lvmcache.h"
+
+static struct pv_segment *_alloc_pv_segment(struct dm_pool *mem,
+                                           struct physical_volume *pv,
+                                           uint32_t pe, uint32_t len,
+                                           struct lv_segment *lvseg,
+                                           uint32_t lv_area)
+{
+       struct pv_segment *peg;
+
+       if (!(peg = dm_pool_zalloc(mem, sizeof(*peg)))) {
+               log_error("pv_segment allocation failed");
+               return NULL;
+       }
+
+       peg->pv = pv;
+       peg->pe = pe;
+       peg->len = len;
+       peg->lvseg = lvseg;
+       peg->lv_area = lv_area;
+
+       dm_list_init(&peg->list);
+
+       return peg;
+}
+
+int alloc_pv_segment_whole_pv(struct dm_pool *mem, struct physical_volume *pv)
+{
+       struct pv_segment *peg;
+
+       if (!pv->pe_count)
+               return 1;
+
+       /* FIXME Cope with holes in PVs */
+       if (!(peg = _alloc_pv_segment(mem, pv, 0, pv->pe_count, NULL, 0)))
+               return_0;
+
+       dm_list_add(&pv->segments, &peg->list);
+
+       return 1;
+}
+
+int peg_dup(struct dm_pool *mem, struct dm_list *peg_new, struct dm_list *peg_old)
+{
+       struct pv_segment *peg, *pego;
+
+       dm_list_init(peg_new);
+
+       dm_list_iterate_items(pego, peg_old) {
+               if (!(peg = _alloc_pv_segment(mem, pego->pv, pego->pe,
+                                             pego->len, pego->lvseg,
+                                             pego->lv_area)))
+                       return_0;
+               dm_list_add(peg_new, &peg->list);
+       }
+
+       return 1;
+}
+
+/* Find segment at a given physical extent in a PV */
+static struct pv_segment *find_peg_by_pe(const struct physical_volume *pv,
+                                        uint32_t pe)
+{
+       struct pv_segment *pvseg;
+
+       /* search backwards to optimise mostly used last segment split */
+       dm_list_iterate_back_items(pvseg, &pv->segments)
+               if (pe >= pvseg->pe && pe < pvseg->pe + pvseg->len)
+                       return pvseg;
+
+       return NULL;
+}
+
+/*
+ * Split peg at given extent.
+ * Second part is always not allocated to a LV and returned.
+ */
+static struct pv_segment *_pv_split_segment(struct dm_pool *mem,
+                                           struct physical_volume *pv,
+                                           struct pv_segment *peg,
+                                           uint32_t pe)
+{
+       struct pv_segment *peg_new;
+
+       if (!(peg_new = _alloc_pv_segment(mem, peg->pv, pe,
+                                         peg->len + peg->pe - pe,
+                                         NULL, 0)))
+               return_NULL;
+
+       peg->len = peg->len - peg_new->len;
+
+       dm_list_add_h(&peg->list, &peg_new->list);
+
+       if (peg->lvseg) {
+               peg->pv->pe_alloc_count -= peg_new->len;
+               peg->lvseg->lv->vg->free_count += peg_new->len;
+       }
+
+       return peg_new;
+}
+
+/*
+ * Ensure there is a PV segment boundary at the given extent.
+ */
+int pv_split_segment(struct dm_pool *mem,
+                    struct physical_volume *pv, uint32_t pe,
+                    struct pv_segment **pvseg_allocated)
+{
+       struct pv_segment *pvseg, *pvseg_new = NULL;
+
+       if (pe == pv->pe_count)
+               goto out;
+
+       if (!(pvseg = find_peg_by_pe(pv, pe))) {
+               log_error("Segment with extent %" PRIu32 " in PV %s not found",
+                         pe, pv_dev_name(pv));
+               return 0;
+       }
+
+       /* This is a peg start already */
+       if (pe == pvseg->pe) {
+               pvseg_new = pvseg;
+               goto out;
+       }
+
+       if (!(pvseg_new = _pv_split_segment(mem, pv, pvseg, pe)))
+               return_0;
+out:
+       if (pvseg_allocated)
+               *pvseg_allocated = pvseg_new;
+
+       return 1;
+}
+
+static struct pv_segment null_pv_segment = {
+       .pv = NULL,
+       .pe = 0,
+};
+
+struct pv_segment *assign_peg_to_lvseg(struct physical_volume *pv,
+                                      uint32_t pe, uint32_t area_len,
+                                      struct lv_segment *seg,
+                                      uint32_t area_num)
+{
+       struct pv_segment *peg = NULL;
+
+       /* Missing format1 PV */
+       if (!pv)
+               return &null_pv_segment;
+
+       if (!pv_split_segment(seg->lv->vg->vgmem, pv, pe, &peg) ||
+           !pv_split_segment(seg->lv->vg->vgmem, pv, pe + area_len, NULL))
+               return_NULL;
+
+       if (!peg) {
+               log_error("Missing PV segment on %s at %u.",
+                         pv_dev_name(pv), pe);
+               return NULL;
+       }
+
+       peg->lvseg = seg;
+       peg->lv_area = area_num;
+
+       peg->pv->pe_alloc_count += area_len;
+       peg->lvseg->lv->vg->free_count -= area_len;
+
+       return peg;
+}
+
+int release_pv_segment(struct pv_segment *peg, uint32_t area_reduction)
+{
+       if (!peg->lvseg) {
+               log_error("release_pv_segment with unallocated segment: "
+                         "%s PE %" PRIu32, pv_dev_name(peg->pv), peg->pe);
+               return 0;
+       }
+
+       if (peg->lvseg->area_len == area_reduction) {
+               peg->pv->pe_alloc_count -= area_reduction;
+               peg->lvseg->lv->vg->free_count += area_reduction;
+
+               peg->lvseg = NULL;
+               peg->lv_area = 0;
+
+               /* FIXME merge free space */
+
+               return 1;
+       }
+
+       if (!pv_split_segment(peg->lvseg->lv->vg->vgmem,
+                             peg->pv, peg->pe + peg->lvseg->area_len -
+                             area_reduction, NULL))
+               return_0;
+
+       return 1;
+}
+
+/*
+ * Only for use by lv_segment merging routines.
+ */
+void merge_pv_segments(struct pv_segment *peg1, struct pv_segment *peg2)
+{
+       peg1->len += peg2->len;
+
+       dm_list_del(&peg2->list);
+}
+
+/*
+ * Calculate the overlap, in extents, between a struct pv_segment and
+ * a struct pe_range.
+ */
+static uint32_t _overlap_pe(const struct pv_segment *pvseg,
+                           const struct pe_range *per)
+{
+       uint32_t start;
+       uint32_t end;
+
+       start = max(pvseg->pe, per->start);
+       end = min(pvseg->pe + pvseg->len, per->start + per->count);
+       if (end < start)
+               return 0;
+       else
+               return end - start;
+}
+
+/*
+ * Returns: number of free PEs in a struct pv_list
+ */
+uint32_t pv_list_extents_free(const struct dm_list *pvh)
+{
+       struct pv_list *pvl;
+       struct pe_range *per;
+       uint32_t extents = 0;
+       struct pv_segment *pvseg;
+
+       dm_list_iterate_items(pvl, pvh) {
+               dm_list_iterate_items(per, pvl->pe_ranges) {
+                       dm_list_iterate_items(pvseg, &pvl->pv->segments) {
+                               if (!pvseg_is_allocated(pvseg))
+                                       extents += _overlap_pe(pvseg, per);
+                       }
+               }
+       }
+
+       return extents;
+}
+
+/*
+ * Check all pv_segments in VG for consistency
+ */
+int check_pv_segments(struct volume_group *vg)
+{
+       struct physical_volume *pv;
+       struct pv_list *pvl;
+       struct pv_segment *peg;
+       unsigned s, segno;
+       uint32_t start_pe, alloced;
+       uint32_t pv_count = 0, free_count = 0, extent_count = 0;
+       int ret = 1;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+               segno = 0;
+               start_pe = 0;
+               alloced = 0;
+               pv_count++;
+
+               dm_list_iterate_items(peg, &pv->segments) {
+                       s = peg->lv_area;
+
+                       /* FIXME Remove this next line eventually */
+                       log_debug("%s %u: %6u %6u: %s(%u:%u)",
+                                 pv_dev_name(pv), segno++, peg->pe, peg->len,
+                                 peg->lvseg ? peg->lvseg->lv->name : "NULL",
+                                 peg->lvseg ? peg->lvseg->le : 0, s);
+                       /* FIXME Add details here on failure instead */
+                       if (start_pe != peg->pe) {
+                               log_error("Gap in pvsegs: %u, %u",
+                                         start_pe, peg->pe);
+                               ret = 0;
+                       }
+                       if (peg->lvseg) {
+                               if (seg_type(peg->lvseg, s) != AREA_PV) {
+                                       log_error("Wrong lvseg area type");
+                                       ret = 0;
+                               }
+                               if (seg_pvseg(peg->lvseg, s) != peg) {
+                                       log_error("Inconsistent pvseg pointers");
+                                       ret = 0;
+                               }
+                               if (peg->lvseg->area_len != peg->len) {
+                                       log_error("Inconsistent length: %u %u",
+                                                 peg->len,
+                                                 peg->lvseg->area_len);
+                                       ret = 0;
+                               }
+                               alloced += peg->len;
+                       }
+                       start_pe += peg->len;
+               }
+
+               if (start_pe != pv->pe_count) {
+                       log_error("PV segment pe_count mismatch: %u != %u",
+                                 start_pe, pv->pe_count);
+                       ret = 0;
+               }
+
+               if (alloced != pv->pe_alloc_count) {
+                       log_error("PV segment pe_alloc_count mismatch: "
+                                 "%u != %u", alloced, pv->pe_alloc_count);
+                       ret = 0;
+               }
+
+               extent_count += start_pe;
+               free_count += (start_pe - alloced);
+       }
+
+       if (pv_count != vg->pv_count) {
+               log_error("PV segment VG pv_count mismatch: %u != %u",
+                         pv_count, vg->pv_count);
+               ret = 0;
+       }
+
+       if (free_count != vg->free_count) {
+               log_error("PV segment VG free_count mismatch: %u != %u",
+                         free_count, vg->free_count);
+               ret = 0;
+       }
+
+       if (extent_count != vg->extent_count) {
+               log_error("PV segment VG extent_count mismatch: %u != %u",
+                         extent_count, vg->extent_count);
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static int _reduce_pv(struct physical_volume *pv, struct volume_group *vg, uint32_t new_pe_count)
+{
+       struct pv_segment *peg, *pegt;
+       uint32_t old_pe_count = pv->pe_count;
+
+       if (new_pe_count < pv->pe_alloc_count) {
+               log_error("%s: cannot resize to %" PRIu32 " extents "
+                         "as %" PRIu32 " are allocated.",
+                         pv_dev_name(pv), new_pe_count,
+                         pv->pe_alloc_count);
+               return 0;
+       }
+
+       /* Check PEs to be removed are not already allocated */
+       dm_list_iterate_items(peg, &pv->segments) {
+               if (peg->pe + peg->len <= new_pe_count)
+                       continue;
+
+               if (peg->lvseg) {
+                       log_error("%s: cannot resize to %" PRIu32 " extents as "
+                                 "later ones are allocated.",
+                                 pv_dev_name(pv), new_pe_count);
+                       return 0;
+               }
+       }
+
+       if (!pv_split_segment(vg->vgmem, pv, new_pe_count, NULL))
+               return_0;
+
+       dm_list_iterate_items_safe(peg, pegt, &pv->segments) {
+               if (peg->pe + peg->len > new_pe_count)
+                       dm_list_del(&peg->list);
+       }
+
+       pv->pe_count = new_pe_count;
+
+       vg->extent_count -= (old_pe_count - new_pe_count);
+       vg->free_count -= (old_pe_count - new_pe_count);
+
+       return 1;
+}
+
+static int _extend_pv(struct physical_volume *pv, struct volume_group *vg,
+                     uint32_t new_pe_count)
+{
+       struct pv_segment *peg;
+       uint32_t old_pe_count = pv->pe_count;
+
+       if ((uint64_t) new_pe_count * pv->pe_size > pv->size ) {
+               log_error("%s: cannot resize to %" PRIu32 " extents as there "
+                         "is only room for %" PRIu64 ".", pv_dev_name(pv),
+                         new_pe_count, pv->size / pv->pe_size);
+               return 0;
+       }
+
+       peg = _alloc_pv_segment(pv->fmt->cmd->mem, pv,
+                               old_pe_count,
+                               new_pe_count - old_pe_count,
+                               NULL, 0);
+       dm_list_add(&pv->segments, &peg->list);
+
+       pv->pe_count = new_pe_count;
+
+       vg->extent_count += (new_pe_count - old_pe_count);
+       vg->free_count += (new_pe_count - old_pe_count);
+
+       return 1;
+}
+
+/*
+ * Resize a PV in a VG, adding or removing segments as needed.
+ * New size must fit within pv->size.
+ */
+int pv_resize(struct physical_volume *pv,
+             struct volume_group *vg,
+             uint32_t new_pe_count)
+{
+       if ((new_pe_count == pv->pe_count)) {
+               log_verbose("No change to size of physical volume %s.",
+                           pv_dev_name(pv));
+               return 1;
+       }
+
+       log_verbose("Resizing physical volume %s from %" PRIu32
+                   " to %" PRIu32 " extents.",
+                   pv_dev_name(pv), pv->pe_count, new_pe_count);
+
+       if (new_pe_count > pv->pe_count)
+               return _extend_pv(pv, vg, new_pe_count);
+       else
+               return _reduce_pv(pv, vg, new_pe_count);
+}
diff --git a/lib/metadata/pv_map.c b/lib/metadata/pv_map.c
new file mode 100644 (file)
index 0000000..8ddb698
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "pv_map.h"
+#include "pv_alloc.h"
+
+#include <assert.h>
+
+/*
+ * Areas are maintained in size order, largest first.
+ *
+ * FIXME Cope with overlap.
+ */
+static void _insert_area(struct dm_list *head, struct pv_area *a, unsigned reduced)
+{
+       struct pv_area *pva;
+       uint32_t count = reduced ? a->unreserved : a->count;
+               
+       dm_list_iterate_items(pva, head)
+               if (count > pva->count)
+                       break;
+
+       dm_list_add(&pva->list, &a->list);
+       a->map->pe_count += a->count;
+}
+
+static void _remove_area(struct pv_area *a)
+{
+       dm_list_del(&a->list);
+       a->map->pe_count -= a->count;
+}
+
+static int _create_single_area(struct dm_pool *mem, struct pv_map *pvm,
+                              uint32_t start, uint32_t length)
+{
+       struct pv_area *pva;
+
+       if (!(pva = dm_pool_zalloc(mem, sizeof(*pva))))
+               return_0;
+
+       log_debug("Allowing allocation on %s start PE %" PRIu32 " length %"
+                 PRIu32, pv_dev_name(pvm->pv), start, length);
+       pva->map = pvm;
+       pva->start = start;
+       pva->count = length;
+       pva->unreserved = pva->count;
+       _insert_area(&pvm->areas, pva, 0);
+
+       return 1;
+}
+
+static int _create_alloc_areas_for_pv(struct dm_pool *mem, struct pv_map *pvm,
+                                     uint32_t start, uint32_t count)
+{
+       struct pv_segment *peg;
+       uint32_t pe, end, area_len;
+
+       /* Only select extents from start to end inclusive */
+       end = start + count - 1;
+       if (end > pvm->pv->pe_count - 1)
+               end = pvm->pv->pe_count - 1;
+
+       pe = start;
+
+       /* Walk through complete ordered list of device segments */
+       dm_list_iterate_items(peg, &pvm->pv->segments) {
+               /* pe holds the next extent we want to check */
+
+               /* Beyond the range we're interested in? */
+               if (pe > end)
+                       break;
+
+               /* Skip if we haven't reached the first seg we want yet */
+               if (pe > peg->pe + peg->len - 1)
+                       continue;
+
+               /* Free? */
+               if (peg->lvseg)
+                       goto next;
+
+               /* How much of this peg do we need? */
+               area_len = (end >= peg->pe + peg->len - 1) ?
+                          peg->len - (pe - peg->pe) : end - pe + 1;
+
+               if (!_create_single_area(mem, pvm, pe, area_len))
+                       return_0;
+
+      next:
+               pe = peg->pe + peg->len;
+       }
+
+       return 1;
+}
+
+static int _create_all_areas_for_pv(struct dm_pool *mem, struct pv_map *pvm,
+                                   struct dm_list *pe_ranges)
+{
+       struct pe_range *aa;
+
+       if (!pe_ranges) {
+               /* Use whole PV */
+               if (!_create_alloc_areas_for_pv(mem, pvm, UINT32_C(0),
+                                               pvm->pv->pe_count))
+                       return_0;
+
+               return 1;
+       }
+
+       dm_list_iterate_items(aa, pe_ranges) {
+               if (!_create_alloc_areas_for_pv(mem, pvm, aa->start,
+                                               aa->count))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _create_maps(struct dm_pool *mem, struct dm_list *pvs, struct dm_list *pvms)
+{
+       struct pv_map *pvm, *pvm2;
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, pvs) {
+               if (!(pvl->pv->status & ALLOCATABLE_PV))
+                       continue;
+               if (is_missing_pv(pvl->pv))
+                       continue;
+               assert(pvl->pv->dev);
+
+               pvm = NULL;
+
+               dm_list_iterate_items(pvm2, pvms)
+                       if (pvm2->pv->dev == pvl->pv->dev) {
+                               pvm = pvm2;
+                               break;
+                       }
+
+               if (!pvm) {
+                       if (!(pvm = dm_pool_zalloc(mem, sizeof(*pvm))))
+                               return_0;
+
+                       pvm->pv = pvl->pv;
+                       dm_list_init(&pvm->areas);
+                       dm_list_add(pvms, &pvm->list);
+               }
+
+               if (!_create_all_areas_for_pv(mem, pvm, pvl->pe_ranges))
+                       return_0;
+       }
+
+       return 1;
+}
+
+/*
+ * Create list of PV areas available for this particular allocation
+ */
+struct dm_list *create_pv_maps(struct dm_pool *mem, struct volume_group *vg,
+                           struct dm_list *allocatable_pvs)
+{
+       struct dm_list *pvms;
+
+       if (!(pvms = dm_pool_zalloc(mem, sizeof(*pvms)))) {
+               log_error("create_pv_maps alloc failed");
+               return NULL;
+       }
+
+       dm_list_init(pvms);
+
+       if (!_create_maps(mem, allocatable_pvs, pvms)) {
+               log_error("Couldn't create physical volume maps in %s",
+                         vg->name);
+               dm_pool_free(mem, pvms);
+               return NULL;
+       }
+
+       return pvms;
+}
+
+void consume_pv_area(struct pv_area *pva, uint32_t to_go)
+{
+       _remove_area(pva);
+
+       assert(to_go <= pva->count);
+
+       if (to_go < pva->count) {
+               /* split the area */
+               pva->start += to_go;
+               pva->count -= to_go;
+               pva->unreserved = pva->count;
+               _insert_area(&pva->map->areas, pva, 0);
+       }
+}
+
+/*
+ * Remove an area from list and reinsert it based on its new smaller size
+ * after a provisional allocation.
+ */
+void reinsert_reduced_pv_area(struct pv_area *pva)
+{
+       _remove_area(pva);
+       _insert_area(&pva->map->areas, pva, 1);
+}
+
+uint32_t pv_maps_size(struct dm_list *pvms)
+{
+       struct pv_map *pvm;
+       uint32_t pe_count = 0;
+
+       dm_list_iterate_items(pvm, pvms)
+               pe_count += pvm->pe_count;
+
+       return pe_count;
+}
diff --git a/lib/metadata/pv_map.h b/lib/metadata/pv_map.h
new file mode 100644 (file)
index 0000000..4ebfd4a
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_PV_MAP_H
+#define _LVM_PV_MAP_H
+
+#include "metadata.h"
+
+/*
+ * The in core rep. only stores a mapping from
+ * logical extents to physical extents against an
+ * lv.  Sometimes, when allocating a new lv for
+ * instance, it is useful to have the inverse
+ * mapping available.
+ */
+
+struct pv_area {
+       struct pv_map *map;
+       uint32_t start;
+       uint32_t count;
+
+       /* Number of extents unreserved during ALLOC_ANYWHERE allocation. */
+       uint32_t unreserved;
+
+       struct dm_list list;            /* pv_map.areas */
+};
+
+/*
+ * When building up a potential group of "parallel" extent ranges during
+ * an allocation attempt, track the maximum number of extents that may
+ * need to be used as a particular parallel area.  Several of these
+ * structs may reference the same pv_area, but 'used' may differ between
+ * them.  The sum of all the 'used' variables referring to the same
+ * pv_area may not exceed that area's count, so we cannot allocate the
+ * same extents twice.
+ */
+struct pv_area_used {
+       struct pv_area *pva;
+       uint32_t used;
+};
+
+struct pv_map {
+       struct physical_volume *pv;
+       struct dm_list areas;           /* struct pv_areas */
+       uint32_t pe_count;              /* Total number of PEs */
+
+       struct dm_list list;
+};
+
+/*
+ * Find intersection between available_pvs and free space in VG
+ */
+struct dm_list *create_pv_maps(struct dm_pool *mem, struct volume_group *vg,
+                           struct dm_list *allocatable_pvs);
+
+void consume_pv_area(struct pv_area *area, uint32_t to_go);
+void reinsert_reduced_pv_area(struct pv_area *pva);
+
+uint32_t pv_maps_size(struct dm_list *pvms);
+
+#endif
diff --git a/lib/metadata/replicator_manip.c b/lib/metadata/replicator_manip.c
new file mode 100644 (file)
index 0000000..79abca0
--- /dev/null
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "toolcontext.h"
+
+/* Add lv as replicator_dev device */
+int replicator_dev_add_rimage(struct replicator_device *rdev,
+                             struct logical_volume *lv)
+{
+       if (!lv || !rdev)
+               return_0;
+
+       if (lv_is_rimage(lv)) {
+               log_error("Logical volume %s is already part of other "
+                         "replicator.", lv->name);
+               return 0;
+       }
+
+       if (rdev->lv) {
+               log_error("Logical volume %s can not be attached to an "
+                         "already defined replicator device", lv->name);
+               return 0;
+       }
+
+       lv_set_hidden(lv);
+       lv->rdevice = rdev;
+       rdev->lv = lv;
+
+       return add_seg_to_segs_using_this_lv(lv, rdev->replicator_dev);
+}
+
+/* Remove lv from replicator_dev device */
+struct logical_volume *replicator_dev_remove_rimage(struct replicator_device *rdev)
+{
+       struct logical_volume *lv;
+
+       if (!rdev || !rdev->lv)
+               return_NULL;
+
+       lv = rdev->lv;
+       if (!remove_seg_from_segs_using_this_lv(lv, rdev->replicator_dev))
+               return_NULL;
+
+       /* FIXME: - check for site references */
+       rdev->lv = NULL;
+       lv->rdevice = NULL;
+       lv_set_visible(lv);
+
+       return lv;
+}
+
+int replicator_dev_add_slog(struct replicator_device *rdev,
+                           struct logical_volume *slog)
+{
+       if (!slog || !rdev)
+               return_0;
+
+       if (rdev->slog) {
+               log_error("Replicator device in site %s already has sync log.",
+                         rdev->rsite->name);
+               return 0;
+       }
+
+       if (slog->rdevice) {
+               log_error("Sync log %s is already used by replicator %s.",
+                         slog->name, slog->rdevice->rsite->replicator->name);
+               return 0;
+       }
+
+       lv_set_hidden(slog);
+       slog->rdevice = rdev;
+       rdev->slog = slog;
+
+       return add_seg_to_segs_using_this_lv(slog, rdev->replicator_dev);
+}
+
+struct logical_volume *replicator_dev_remove_slog(struct replicator_device *rdev)
+{
+       struct logical_volume *lv;
+
+       if (!rdev)
+               return_NULL;
+
+       lv = rdev->slog;
+       if (!lv) {
+               log_error("Replicator device in site %s does not have sync log.",
+                         rdev->rsite->name);
+               return NULL;
+       }
+
+       if (!remove_seg_from_segs_using_this_lv(lv, rdev->replicator_dev))
+               return_NULL;
+
+       rdev->slog = NULL;
+       lv->rdevice = NULL;
+       lv_set_visible(lv);
+
+       return lv;
+}
+
+int replicator_add_replicator_dev(struct logical_volume *replicator_lv,
+                                 struct lv_segment *replicator_dev_seg)
+{
+       if (!replicator_lv)
+               return_0;
+
+       if (!(replicator_lv->status & REPLICATOR)) {
+               dm_list_init(&replicator_lv->rsites);
+               lv_set_hidden(replicator_lv);
+               replicator_lv->status |= REPLICATOR;
+       }
+
+       if (!replicator_dev_seg)
+               return 1;
+
+       if (replicator_dev_seg->replicator) {
+               log_error("Replicator device %s is already part of replicator.",
+                         replicator_dev_seg->lv->name);
+               return 0;
+       }
+
+       replicator_dev_seg->replicator = replicator_lv;
+
+       return add_seg_to_segs_using_this_lv(replicator_lv, replicator_dev_seg);
+}
+
+/**
+ * Returns rimage ?? lv upon succeful detach of device
+ * entire LV entry should be removed by this crootall ??
+ */
+struct logical_volume *replicator_remove_replicator_dev(struct lv_segment *replicator_dev_seg)
+{
+       struct logical_volume *lv = NULL;
+
+       log_error("FIXME: not implemented.");
+#if 0
+       /* FIXME: - this is going to be complex.... */
+       if (!replicator_dev_seg)
+               return_NULL;
+
+       /* if slog or rimage - exit */
+
+       if (!remove_seg_from_segs_using_this_lv(lv, replicator_seg))
+               return_NULL;
+
+       replicator_seg->rlog_lv = NULL;
+       lv->status &= ~REPLICATOR_LOG;
+       lv_set_visible(lv);
+#endif
+
+       return lv;
+}
+
+int replicator_add_rlog(struct lv_segment *replicator_seg,
+                       struct logical_volume *rlog_lv)
+{
+       if (!rlog_lv)
+               return_0;
+
+       if (rlog_lv->status & REPLICATOR_LOG) {
+               log_error("Rlog device %s is already used.", rlog_lv->name);
+               return 0;
+       }
+
+       lv_set_hidden(rlog_lv);
+       rlog_lv->status |= REPLICATOR_LOG;
+       replicator_seg->rlog_lv = rlog_lv;
+
+       return add_seg_to_segs_using_this_lv(rlog_lv, replicator_seg);
+}
+
+struct logical_volume *replicator_remove_rlog(struct lv_segment *replicator_seg)
+{
+       struct logical_volume *lv;
+
+       if (!replicator_seg)
+               return_0;
+
+       if (!(lv = replicator_seg->rlog_lv)) {
+               log_error("Replog segment %s does not have rlog.",
+                         replicator_seg->lv->name);
+               return NULL;
+       }
+
+       if (!remove_seg_from_segs_using_this_lv(lv, replicator_seg))
+               return_NULL;
+
+       replicator_seg->rlog_lv = NULL;
+       lv->status &= ~REPLICATOR_LOG;
+       lv_set_visible(lv);
+
+       return lv;
+}
+
+
+#if 0
+/*
+ * Create new LV to pretend the original LV
+ * this target will have a 'replicator' segment
+ */
+int lv_add_replicator(struct logical_volume *origin, const char *rep_suffix)
+{
+       struct logical_volume *rep_lv;
+       char *name;
+       size_t slen;
+
+       if (!(name = strstr(origin->name, rep_suffix))) {
+               log_error("Failed to find replicator suffix %s in LV name %s",
+                         rep_suffix, origin->name);
+               return 0;
+       }
+       slen = (size_t)(name - origin->name);
+       name = alloca(slen + 1);
+       memcpy(name, origin->name, slen);
+       name[slen] = 0;
+
+       if ((rep_lv = find_lv(origin->vg, name))) {
+               rep_lv->status |= VIRTUAL;
+               return 1;
+       }
+
+       if (!(rep_lv = lv_create_empty(name, &origin->lvid,
+                                      LVM_READ | LVM_WRITE | VISIBLE_LV,
+                                      ALLOC_INHERIT, origin->vg)))
+               return_0;
+
+       if (!lv_add_virtual_segment(rep_lv, 0, origin->le_count,
+                                   get_segtype_from_string(origin->vg->cmd,
+                                                           "error")))
+               return_0;
+
+       rep_lv->status |= VIRTUAL;
+       return 1;
+}
+
+int lv_remove_replicator(struct logical_volume *lv)
+{
+       return 1;
+}
+#endif
+
+/*
+ * Check all replicator structures:
+ *  only non-clustered VG for Replicator
+ *  only one segment in replicator LV
+ *  site has correct combination of operation_mode parameters
+ *  site and related devices have correct index numbers
+ *  duplicate site names, site indexes, device names, device indexes
+ */
+int check_replicator_segment(const struct lv_segment *rseg)
+{
+       struct replicator_site *rsite, *rsiteb;
+       struct replicator_device *rdev, *rdevb;
+        struct logical_volume *lv = rseg->lv;
+       int r = 1;
+
+       if (vg_is_clustered(lv->vg)) {
+               log_error("Volume Group %s of replicator %s is clustered",
+                         lv->vg->name, lv->name);
+               return 0;
+       }
+
+       if (dm_list_size(&lv->segments) != 1) {
+               log_error("Replicator %s segment size %d != 1",
+                         lv->name, dm_list_size(&lv->segments));
+               return 0;
+       }
+
+       dm_list_iterate_items(rsite, &lv->rsites) {
+               if (rsite->op_mode == DM_REPLICATOR_SYNC) {
+                       if (rsite->fall_behind_timeout) {
+                               log_error("Defined fall_behind_timeout="
+                                         "%d for sync replicator %s/%s.",
+                                         rsite->fall_behind_timeout, lv->name,
+                                         rsite->name);
+                               r = 0;
+                       }
+                       if (rsite->fall_behind_ios) {
+                               log_error("Defined fall_behind_ios="
+                                         "%d for sync replicator %s/%s.",
+                                         rsite->fall_behind_ios, lv->name, rsite->name);
+                               r = 0;
+                       }
+                       if (rsite->fall_behind_data) {
+                               log_error("Defined fall_behind_data="
+                                         "%" PRIu64 " for sync replicator %s/%s.",
+                                         rsite->fall_behind_data, lv->name, rsite->name);
+                               r = 0;
+                       }
+               } else {
+                       if (rsite->fall_behind_timeout && rsite->fall_behind_ios) {
+                               log_error("Defined fall_behind_timeout and"
+                                         " fall_behind_ios for async replicator %s/%s.",
+                                         lv->name, rsite->name);
+                               r = 0;
+                       }
+                       if (rsite->fall_behind_timeout && rsite->fall_behind_data) {
+                               log_error("Defined fall_behind_timeout and"
+                                         " fall_behind_data for async replicator %s/%s.",
+                                         lv->name, rsite->name);
+                               r = 0;
+                       }
+                       if (rsite->fall_behind_ios && rsite->fall_behind_data) {
+                               log_error("Defined fall_behind_ios and"
+                                         " fall_behind_data for async replicator %s/%s.",
+                                         lv->name, rsite->name);
+                               r = 0;
+                       }
+                       if (!rsite->fall_behind_ios &&
+                           !rsite->fall_behind_data &&
+                           !rsite->fall_behind_timeout) {
+                               log_error("fall_behind_timeout,"
+                                         " fall_behind_ios and fall_behind_data are"
+                                         " undefined for async replicator %s/%s.",
+                                         lv->name, rsite->name);
+                               r = 0;
+                       }
+               }
+               dm_list_iterate_items(rsiteb, &lv->rsites) {
+                       if (rsite == rsiteb)
+                               break;
+                       if (strcasecmp(rsite->name, rsiteb->name) == 0) {
+                               log_error("Duplicate site name "
+                                         "%s detected for replicator %s.",
+                                         rsite->name, lv->name);
+                               r = 0;
+                       }
+                       if ((rsite->vg_name && rsiteb->vg_name &&
+                            strcasecmp(rsite->vg_name, rsiteb->vg_name) == 0) ||
+                           (!rsite->vg_name && !rsiteb->vg_name)) {
+                               log_error("Duplicate VG name "
+                                         "%s detected for replicator %s.",
+                                         (rsite->vg_name) ? rsite->vg_name : "<local>",
+                                         lv->name);
+                               r = 0;
+                       }
+                       if (rsite->site_index == rsiteb->site_index) {
+                               log_error("Duplicate site index %d detected "
+                                         "for replicator site %s/%s.",
+                                         rsite->site_index, lv->name,
+                                         rsite->name);
+                               r = 0;
+                       }
+                       if (rsite->site_index > rseg->rsite_index_highest) {
+                               log_error("Site index %d > %d (too high) "
+                                         "for replicator site %s/%s.",
+                                         rsite->site_index,
+                                         rseg->rsite_index_highest,
+                                         lv->name, rsite->name);
+                               r = 0;
+                       }
+               }
+
+               dm_list_iterate_items(rdev, &rsite->rdevices) {
+                       dm_list_iterate_items(rdevb, &rsite->rdevices) {
+                               if (rdev == rdevb)
+                                       break;
+                               if (rdev->slog && (rdev->slog == rdevb->slog)) {
+                                       log_error("Duplicate sync log %s "
+                                                 "detected for replicator %s.",
+                                                 rdev->slog->name, lv->name);
+                                       r = 0;
+                               }
+                               if (strcasecmp(rdev->name, rdevb->name) == 0) {
+                                       log_error("Duplicate device name %s "
+                                                 "detected for replicator %s.",
+                                                 rdev->name, lv->name);
+                                       r = 0;
+                               }
+                               if (rdev->device_index == rdevb->device_index) {
+                                       log_error("Duplicate device index %"
+                                                 PRId64 " detected for "
+                                                 "replicator site %s/%s.",
+                                                 rdev->device_index,
+                                                 lv->name, rsite->name);
+                                       r = 0;
+                               }
+                               if (rdev->device_index > rseg->rdevice_index_highest) {
+                                       log_error("Device index %" PRIu64
+                                                 " > %" PRIu64 " (too high) "
+                                                 "for replicator site %s/%s.",
+                                                 rdev->device_index,
+                                                 rseg->rdevice_index_highest,
+                                                 lv->name, rsite->name);
+                                       r = 0;
+                               }
+                       }
+               }
+       }
+
+       return r;
+}
+
+/**
+ * Is this segment part of active replicator
+ */
+int lv_is_active_replicator_dev(const struct logical_volume *lv)
+{
+       return ((lv->status & REPLICATOR) &&
+               lv->rdevice &&
+               lv->rdevice->rsite &&
+               lv->rdevice->rsite->state == REPLICATOR_STATE_ACTIVE);
+}
+
+/**
+ * Is this LV replicator control device
+ */
+int lv_is_replicator(const struct logical_volume *lv)
+{
+       return ((lv->status & REPLICATOR) &&
+               !dm_list_empty(&lv->segments) &&
+               seg_is_replicator(first_seg(lv)));
+}
+
+/**
+ * Is this LV replicator device
+ */
+int lv_is_replicator_dev(const struct logical_volume *lv)
+{
+       return ((lv->status & REPLICATOR) &&
+               !dm_list_empty(&lv->segments) &&
+               seg_is_replicator_dev(first_seg(lv)));
+}
+
+/**
+ * Is this LV replicated origin lv
+ */
+int lv_is_rimage(const struct logical_volume *lv)
+{
+       return (lv->rdevice && lv->rdevice->lv == lv);
+}
+
+/**
+ * Is this LV rlog
+ */
+int lv_is_rlog(const struct logical_volume *lv)
+{
+       return (lv->status & REPLICATOR_LOG);
+}
+
+/**
+ * Is this LV sync log
+ */
+int lv_is_slog(const struct logical_volume *lv)
+{
+       return (lv->rdevice && lv->rdevice->slog == lv);
+}
+
+/**
+ * Returns first replicator-dev in site in case the LV is replicator-dev,
+ * NULL otherwise
+ */
+struct logical_volume *first_replicator_dev(const struct logical_volume *lv)
+{
+       struct replicator_device *rdev;
+       struct replicator_site *rsite;
+
+       if (lv_is_replicator_dev(lv))
+               dm_list_iterate_items(rsite, &first_seg(lv)->replicator->rsites) {
+                       dm_list_iterate_items(rdev, &rsite->rdevices)
+                               return rdev->replicator_dev->lv;
+                       break;
+               }
+
+       return NULL;
+}
+
+/**
+ * Add VG open parameters to sorted cmd_vg list.
+ *
+ * Maintain the alphabeticaly ordered list, avoid duplications.
+ *
+ * \return     Returns newly created or already present cmd_vg entry,
+ *             or NULL in error case.
+ */
+struct cmd_vg *cmd_vg_add(struct dm_pool *mem, struct dm_list *cmd_vgs,
+                         const char *vg_name, const char *vgid,
+                         uint32_t flags)
+{
+       struct cmd_vg *cvl, *ins;
+
+       if (!vg_name && !vgid) {
+               log_error("Either vg_name or vgid must be set.");
+               return NULL;
+       }
+
+       /* Is it already in the list ? */
+       if ((cvl = cmd_vg_lookup(cmd_vgs, vg_name, vgid)))
+               return cvl;
+
+       if (!(cvl = dm_pool_zalloc(mem, sizeof(*cvl)))) {
+               log_error("Allocation of cmd_vg failed.");
+               return NULL;
+       }
+
+       if (vg_name && !(cvl->vg_name = dm_pool_strdup(mem, vg_name))) {
+               dm_pool_free(mem, cvl);
+               log_error("Allocation of vg_name failed.");
+               return NULL;
+       }
+
+       if (vgid && !(cvl->vgid = dm_pool_strdup(mem, vgid))) {
+               dm_pool_free(mem, cvl);
+               log_error("Allocation of vgid failed.");
+               return NULL;
+       }
+
+       cvl->flags = flags;
+
+       if (vg_name)
+               dm_list_iterate_items(ins, cmd_vgs)
+                       if (strcmp(vg_name, ins->vg_name) < 0) {
+                               cmd_vgs = &ins->list; /* new position */
+                               break;
+                       }
+
+       dm_list_add(cmd_vgs, &cvl->list);
+
+       return cvl;
+}
+
+/**
+ * Find cmd_vg with given vg_name in cmd_vgs list.
+ *
+ * \param cmd_vgs      List of cmd_vg entries.
+ *
+ * \param vg_name      Name of VG to be found.
+
+ * \param vgid         UUID of VG to be found.
+ *
+ * \return             Returns cmd_vg entry if vg_name or vgid is found,
+ *                     NULL otherwise.
+ */
+struct cmd_vg *cmd_vg_lookup(struct dm_list *cmd_vgs,
+                            const char *vg_name, const char *vgid)
+{
+       struct cmd_vg *cvl;
+
+       dm_list_iterate_items(cvl, cmd_vgs)
+               if ((vgid && cvl->vgid && !strcmp(vgid, cvl->vgid)) ||
+                   (vg_name && cvl->vg_name && !strcmp(vg_name, cvl->vg_name)))
+                       return cvl;
+       return NULL;
+}
+
+/**
+ * Read and lock multiple VGs stored in cmd_vgs list alphabeticaly.
+ * On the success list head pointer is set to VGs' cmd_vgs.
+ * (supports FAILED_INCONSISTENT)
+ *
+ * \param cmd_vg       Contains list of cmd_vg entries.
+ *
+ * \return             Returns 1 if all VG in cmd_vgs list are correctly
+ *                     openned and locked, 0 otherwise.
+ */
+int cmd_vg_read(struct cmd_context *cmd, struct dm_list *cmd_vgs)
+{
+       struct cmd_vg *cvl;
+
+       /* Iterate through alphabeticaly ordered cmd_vg list */
+       dm_list_iterate_items(cvl, cmd_vgs) {
+               cvl->vg = vg_read(cmd, cvl->vg_name, cvl->vgid, cvl->flags);
+               if (vg_read_error(cvl->vg)) {
+                       log_debug("Failed to vg_read %s", cvl->vg_name);
+                       return 0;
+               }
+               cvl->vg->cmd_vgs = cmd_vgs;     /* Make it usable in VG */
+       }
+
+       return 1;
+}
+
+/**
+ * Release opened and locked VGs from list.
+ *
+ * \param cmd_vgs      Contains list of cmd_vg entries.
+ */
+void free_cmd_vgs(struct dm_list *cmd_vgs)
+{
+       struct cmd_vg *cvl;
+
+       /* Backward iterate cmd_vg list */
+       dm_list_iterate_back_items(cvl, cmd_vgs) {
+               if (vg_read_error(cvl->vg))
+                       free_vg(cvl->vg);
+               else
+                       unlock_and_free_vg(cvl->vg->cmd, cvl->vg, cvl->vg_name);
+               cvl->vg = NULL;
+       }
+}
+
+/**
+ * Find all needed remote VGs for processing given LV.
+ * Missing VGs are added to VG's cmd_vg list and flag cmd_missing_vgs is set.
+ */
+int find_replicator_vgs(struct logical_volume *lv)
+{
+       struct replicator_site *rsite;
+       int ret = 1;
+
+       if (!lv_is_replicator_dev(lv))
+               return 1;
+
+       dm_list_iterate_items(rsite, &first_seg(lv)->replicator->rsites) {
+               if (!rsite->vg_name || !lv->vg->cmd_vgs ||
+                   cmd_vg_lookup(lv->vg->cmd_vgs, rsite->vg_name, NULL))
+                       continue;
+               ret = 0;
+               /* Using cmd memory pool for cmd_vg list allocation */
+               if (!cmd_vg_add(lv->vg->cmd->mem, lv->vg->cmd_vgs,
+                               rsite->vg_name, NULL, 0)) {
+                       lv->vg->cmd_missing_vgs = 0; /* do not retry */
+                       stack;
+                       break;
+               }
+
+               log_debug("VG: %s added as missing.", rsite->vg_name);
+               lv->vg->cmd_missing_vgs++;
+       }
+
+       return ret;
+}
+
+/**
+ * Read all remote VGs from lv's replicator sites.
+ * Function is used in activation context and needs all VGs already locked.
+ */
+int lv_read_replicator_vgs(struct logical_volume *lv)
+{
+       struct replicator_device *rdev;
+       struct replicator_site *rsite;
+       struct volume_group *vg;
+
+       if (!lv_is_replicator_dev(lv))
+               return 1;
+
+       dm_list_iterate_items(rsite, &first_seg(lv)->replicator->rsites) {
+               if (!rsite->vg_name)
+                       continue;
+               vg = vg_read(lv->vg->cmd, rsite->vg_name, 0, 0); // READ_WITHOUT_LOCK
+               if (vg_read_error(vg)) {
+                       log_error("Unable to read volume group %s",
+                                 rsite->vg_name);
+                       goto bad;
+               }
+               rsite->vg = vg;
+               /* FIXME: handling missing LVs needs to be better */
+               dm_list_iterate_items(rdev, &rsite->rdevices)
+                       if (!(rdev->lv = find_lv(vg, rdev->name))) {
+                               log_error("Unable to find %s in volume group %s",
+                                         rdev->name, rsite->vg_name);
+                               goto bad;
+                       }
+       }
+
+       return 1;
+bad:
+       lv_release_replicator_vgs(lv);
+       return 0;
+}
+
+/**
+ * Release all VG resources taken by lv's replicator sites.
+ * Function is used in activation context and needs all VGs already locked.
+ */
+void lv_release_replicator_vgs(struct logical_volume *lv)
+{
+       struct replicator_site *rsite;
+
+       if (!lv_is_replicator_dev(lv))
+               return;
+
+       dm_list_iterate_back_items(rsite, &first_seg(lv)->replicator->rsites)
+               if (rsite->vg_name && rsite->vg) {
+                       free_vg(rsite->vg);
+                       rsite->vg = NULL;
+               }
+}
diff --git a/lib/metadata/segtype.c b/lib/metadata/segtype.c
new file mode 100644 (file)
index 0000000..7218743
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+
+struct segment_type *get_segtype_from_string(struct cmd_context *cmd,
+                                            const char *str)
+{
+       struct segment_type *segtype;
+
+       dm_list_iterate_items(segtype, &cmd->segtypes) {
+               if (!strcmp(segtype->name, str))
+                       return segtype;
+       }
+
+       if (!(segtype = init_unknown_segtype(cmd, str)))
+               return_NULL;
+
+       segtype->library = NULL;
+       dm_list_add(&cmd->segtypes, &segtype->list);
+       log_warn("WARNING: Unrecognised segment type %s", str);
+       return segtype;
+}
diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h
new file mode 100644 (file)
index 0000000..6d50746
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _SEGTYPES_H
+#define _SEGTYPES_H
+
+#include "metadata-exported.h"
+
+struct segtype_handler;
+struct cmd_context;
+struct config_tree;
+struct lv_segment;
+struct formatter;
+struct config_node;
+struct dev_manager;
+
+/* Feature flags */
+#define SEG_CAN_SPLIT          0x00000001U
+#define SEG_AREAS_STRIPED      0x00000002U
+#define SEG_AREAS_MIRRORED     0x00000004U
+#define SEG_SNAPSHOT           0x00000008U
+#define SEG_FORMAT1_SUPPORT    0x00000010U
+#define SEG_VIRTUAL            0x00000020U
+#define SEG_CANNOT_BE_ZEROED   0x00000040U
+#define SEG_MONITORED          0x00000080U
+#define SEG_REPLICATOR         0x00000100U
+#define SEG_REPLICATOR_DEV     0x00000200U
+#define SEG_UNKNOWN            0x80000000U
+
+#define seg_is_mirrored(seg)   ((seg)->segtype->flags & SEG_AREAS_MIRRORED ? 1 : 0)
+#define seg_is_replicator(seg) ((seg)->segtype->flags & SEG_REPLICATOR ? 1 : 0)
+#define seg_is_replicator_dev(seg) ((seg)->segtype->flags & SEG_REPLICATOR_DEV ? 1 : 0)
+#define seg_is_striped(seg)    ((seg)->segtype->flags & SEG_AREAS_STRIPED ? 1 : 0)
+#define seg_is_snapshot(seg)   ((seg)->segtype->flags & SEG_SNAPSHOT ? 1 : 0)
+#define seg_is_virtual(seg)    ((seg)->segtype->flags & SEG_VIRTUAL ? 1 : 0)
+#define seg_can_split(seg)     ((seg)->segtype->flags & SEG_CAN_SPLIT ? 1 : 0)
+#define seg_cannot_be_zeroed(seg) ((seg)->segtype->flags & SEG_CANNOT_BE_ZEROED ? 1 : 0)
+#define seg_monitored(seg)     ((seg)->segtype->flags & SEG_MONITORED ? 1 : 0)
+#define seg_unknown(seg)       ((seg)->segtype->flags & SEG_UNKNOWN ? 1 : 0)
+
+#define segtype_is_striped(segtype)    ((segtype)->flags & SEG_AREAS_STRIPED ? 1 : 0)
+#define segtype_is_mirrored(segtype)   ((segtype)->flags & SEG_AREAS_MIRRORED ? 1 : 0)
+#define segtype_is_virtual(segtype)    ((segtype)->flags & SEG_VIRTUAL ? 1 : 0)
+
+struct segment_type {
+       struct dm_list list;            /* Internal */
+       struct cmd_context *cmd;        /* lvm_register_segtype() sets this. */
+       uint32_t flags;
+       struct segtype_handler *ops;
+       const char *name;
+       void *library;                  /* lvm_register_segtype() sets this. */
+       void *private;                  /* For the segtype handler to use. */
+};
+
+struct segtype_handler {
+       const char *(*name) (const struct lv_segment * seg);
+       const char *(*target_name) (const struct lv_segment * seg);
+       void (*display) (const struct lv_segment * seg);
+       int (*text_export) (const struct lv_segment * seg,
+                           struct formatter * f);
+       int (*text_import_area_count) (const struct config_node * sn,
+                                      uint32_t *area_count);
+       int (*text_import) (struct lv_segment * seg,
+                           const struct config_node * sn,
+                           struct dm_hash_table * pv_hash);
+       int (*merge_segments) (struct lv_segment * seg1,
+                              struct lv_segment * seg2);
+       int (*add_target_line) (struct dev_manager *dm, struct dm_pool *mem,
+                                struct cmd_context *cmd, void **target_state,
+                                struct lv_segment *seg,
+                                struct dm_tree_node *node, uint64_t len,
+                                uint32_t *pvmove_mirror_count);
+       int (*target_status_compatible) (const char *type);
+       int (*check_transient_status) (struct lv_segment *seg, char *params);
+       int (*target_percent) (void **target_state,
+                              percent_t *percent,
+                              struct dm_pool * mem,
+                              struct cmd_context *cmd,
+                              struct lv_segment *seg, char *params,
+                              uint64_t *total_numerator,
+                              uint64_t *total_denominator);
+       int (*target_present) (struct cmd_context *cmd,
+                              const struct lv_segment *seg,
+                              unsigned *attributes);
+       int (*modules_needed) (struct dm_pool *mem,
+                              const struct lv_segment *seg,
+                              struct dm_list *modules);
+       void (*destroy) (struct segment_type * segtype);
+       int (*target_monitored) (struct lv_segment *seg, int *pending);
+       int (*target_monitor_events) (struct lv_segment *seg, int events);
+       int (*target_unmonitor_events) (struct lv_segment *seg, int events);
+};
+
+struct segment_type *get_segtype_from_string(struct cmd_context *cmd,
+                                            const char *str);
+
+struct segtype_library;
+int lvm_register_segtype(struct segtype_library *seglib,
+                        struct segment_type *segtype);
+
+struct segment_type *init_striped_segtype(struct cmd_context *cmd);
+struct segment_type *init_zero_segtype(struct cmd_context *cmd);
+struct segment_type *init_error_segtype(struct cmd_context *cmd);
+struct segment_type *init_free_segtype(struct cmd_context *cmd);
+struct segment_type *init_unknown_segtype(struct cmd_context *cmd, const char *name);
+
+#ifdef REPLICATOR_INTERNAL
+int init_replicator_segtype(struct segtype_library *seglib);
+#endif
+
+#ifdef SNAPSHOT_INTERNAL
+struct segment_type *init_snapshot_segtype(struct cmd_context *cmd);
+#endif
+
+#ifdef MIRRORED_INTERNAL
+struct segment_type *init_mirrored_segtype(struct cmd_context *cmd);
+#endif
+
+#ifdef CRYPT_INTERNAL
+struct segment_type *init_crypt_segtype(struct cmd_context *cmd);
+#endif
+
+#endif
diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c
new file mode 100644 (file)
index 0000000..cb5df6b
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "locking.h"
+#include "toolcontext.h"
+#include "lv_alloc.h"
+#include "activate.h"
+
+int lv_is_origin(const struct logical_volume *lv)
+{
+       return lv->origin_count ? 1 : 0;
+}
+
+int lv_is_cow(const struct logical_volume *lv)
+{
+       return (!lv_is_origin(lv) && lv->snapshot) ? 1 : 0;
+}
+
+int lv_is_visible(const struct logical_volume *lv)
+{
+       if (lv->status & SNAPSHOT)
+               return 0;
+
+       if (lv_is_cow(lv)) {
+               if (lv_is_virtual_origin(origin_from_cow(lv)))
+                       return 1;
+
+               if (lv_is_merging_cow(lv))
+                       return 0;
+
+               return lv_is_visible(origin_from_cow(lv));
+       }
+
+       return lv->status & VISIBLE_LV ? 1 : 0;
+}
+
+int lv_is_virtual_origin(const struct logical_volume *lv)
+{
+       return (lv->status & VIRTUAL_ORIGIN) ? 1 : 0;
+}
+
+int lv_is_merging_origin(const struct logical_volume *origin)
+{
+       return (origin->status & MERGING) ? 1 : 0;
+}
+
+struct lv_segment *find_merging_cow(const struct logical_volume *origin)
+{
+       if (!lv_is_merging_origin(origin))
+               return NULL;
+
+       return find_cow(origin);
+}
+
+int lv_is_merging_cow(const struct logical_volume *snapshot)
+{
+       /* checks lv_segment's status to see if cow is merging */
+       return (find_cow(snapshot)->status & MERGING) ? 1 : 0;
+}
+
+/* Given a cow LV, return the snapshot lv_segment that uses it */
+struct lv_segment *find_cow(const struct logical_volume *lv)
+{
+       return lv->snapshot;
+}
+
+/* Given a cow LV, return its origin */
+struct logical_volume *origin_from_cow(const struct logical_volume *lv)
+{
+       return lv->snapshot->origin;
+}
+
+void init_snapshot_seg(struct lv_segment *seg, struct logical_volume *origin,
+                      struct logical_volume *cow, uint32_t chunk_size, int merge)
+{
+       seg->chunk_size = chunk_size;
+       seg->origin = origin;
+       seg->cow = cow;
+
+       lv_set_hidden(cow);
+
+       cow->snapshot = seg;
+
+       origin->origin_count++;
+
+       /* FIXME Assumes an invisible origin belongs to a sparse device */
+       if (!lv_is_visible(origin))
+               origin->status |= VIRTUAL_ORIGIN;
+
+       seg->lv->status |= (SNAPSHOT | VIRTUAL);
+       if (merge)
+               init_snapshot_merge(seg, origin);
+
+       dm_list_add(&origin->snapshot_segs, &seg->origin_list);
+}
+
+void init_snapshot_merge(struct lv_segment *cow_seg,
+                        struct logical_volume *origin)
+{
+       /*
+        * Even though lv_is_visible(cow_seg->lv) returns 0,
+        * the cow_seg->lv (name: snapshotX) is _not_ hidden;
+        * this is part of the lvm2 snapshot fiction.  Must
+        * clear VISIBLE_LV directly (lv_set_visible can't)
+        * - cow_seg->lv->status is used to control whether 'lv'
+        *   (with user provided snapshot LV name) is visible
+        * - this also enables vg_validate() to succeed with
+        *   merge metadata (cow_seg->lv is now "internal")
+        */
+       cow_seg->lv->status &= ~VISIBLE_LV;
+       cow_seg->status |= MERGING;
+       origin->snapshot = cow_seg;
+       origin->status |= MERGING;
+}
+
+void clear_snapshot_merge(struct logical_volume *origin)
+{
+       /* clear merge attributes */
+       origin->snapshot->status &= ~MERGING;
+       origin->snapshot = NULL;
+       origin->status &= ~MERGING;
+}
+
+int vg_add_snapshot(struct logical_volume *origin,
+                   struct logical_volume *cow, union lvid *lvid,
+                   uint32_t extent_count, uint32_t chunk_size)
+{
+       struct logical_volume *snap;
+       struct lv_segment *seg;
+
+       /*
+        * Is the cow device already being used ?
+        */
+       if (lv_is_cow(cow)) {
+               log_error("'%s' is already in use as a snapshot.", cow->name);
+               return 0;
+       }
+
+       if (cow == origin) {
+               log_error("Snapshot and origin LVs must differ.");
+               return 0;
+       }
+
+       if (!(snap = lv_create_empty("snapshot%d",
+                                    lvid, LVM_READ | LVM_WRITE | VISIBLE_LV,
+                                    ALLOC_INHERIT, origin->vg)))
+               return_0;
+
+       snap->le_count = extent_count;
+
+       if (!(seg = alloc_snapshot_seg(snap, 0, 0)))
+               return_0;
+
+       init_snapshot_seg(seg, origin, cow, chunk_size, 0);
+
+       return 1;
+}
+
+int vg_remove_snapshot(struct logical_volume *cow)
+{
+       int preload_origin = 0;
+       struct logical_volume *origin = origin_from_cow(cow);
+
+       dm_list_del(&cow->snapshot->origin_list);
+       origin->origin_count--;
+
+       if (find_merging_cow(origin) == find_cow(cow)) {
+               clear_snapshot_merge(origin);
+               /*
+                * preload origin IFF "snapshot-merge" target is active
+                * - IMPORTANT: avoids preload if onactivate merge is pending
+                */
+               if (lv_has_target_type(origin->vg->cmd->mem, origin, NULL,
+                                      "snapshot-merge")) {
+                       /*
+                        * preload origin to:
+                        * - allow proper release of -cow
+                        * - avoid allocations with other devices suspended
+                        *   when transitioning from "snapshot-merge" to
+                        *   "snapshot-origin after a merge completes.
+                        */
+                       preload_origin = 1;
+               }
+       }
+
+       if (!lv_remove(cow->snapshot->lv)) {
+               log_error("Failed to remove internal snapshot LV %s",
+                         cow->snapshot->lv->name);
+               return 0;
+       }
+
+       cow->snapshot = NULL;
+       lv_set_visible(cow);
+
+       if (preload_origin) {
+               if (!vg_write(origin->vg))
+                       return_0;
+               if (!suspend_lv(origin->vg->cmd, origin)) {
+                       log_error("Failed to refresh %s without snapshot.",
+                                 origin->name);
+                       return 0;
+               }
+               if (!vg_commit(origin->vg))
+                       return_0;
+               if (!resume_lv(origin->vg->cmd, origin)) {
+                       log_error("Failed to resume %s.", origin->name);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c
new file mode 100644 (file)
index 0000000..9d0d5dd
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "display.h"
+#include "activate.h"
+
+char *vg_fmt_dup(const struct volume_group *vg)
+{
+       if (!vg->fid || !vg->fid->fmt)
+               return NULL;
+       return dm_pool_strdup(vg->vgmem, vg->fid->fmt->name);
+}
+
+char *vg_name_dup(const struct volume_group *vg)
+{
+       return dm_pool_strdup(vg->vgmem, vg->name);
+}
+
+char *vg_system_id_dup(const struct volume_group *vg)
+{
+       return dm_pool_strdup(vg->vgmem, vg->system_id);
+}
+
+char *vg_uuid_dup(const struct volume_group *vg)
+{
+       return id_format_and_copy(vg->vgmem, &vg->id);
+}
+
+char *vg_tags_dup(const struct volume_group *vg)
+{
+       return tags_format_and_copy(vg->vgmem, &vg->tags);
+}
+
+uint32_t vg_seqno(const struct volume_group *vg)
+{
+       return vg->seqno;
+}
+
+uint64_t vg_status(const struct volume_group *vg)
+{
+       return vg->status;
+}
+
+uint64_t vg_size(const struct volume_group *vg)
+{
+       return (uint64_t) vg->extent_count * vg->extent_size;
+}
+
+uint64_t vg_free(const struct volume_group *vg)
+{
+       return (uint64_t) vg->free_count * vg->extent_size;
+}
+
+uint64_t vg_extent_size(const struct volume_group *vg)
+{
+       return (uint64_t) vg->extent_size;
+}
+
+uint64_t vg_extent_count(const struct volume_group *vg)
+{
+       return (uint64_t) vg->extent_count;
+}
+
+uint64_t vg_free_count(const struct volume_group *vg)
+{
+       return (uint64_t) vg->free_count;
+}
+
+uint64_t vg_pv_count(const struct volume_group *vg)
+{
+       return (uint64_t) vg->pv_count;
+}
+
+uint64_t vg_max_pv(const struct volume_group *vg)
+{
+       return (uint64_t) vg->max_pv;
+}
+
+uint64_t vg_max_lv(const struct volume_group *vg)
+{
+       return (uint64_t) vg->max_lv;
+}
+
+unsigned snapshot_count(const struct volume_group *vg)
+{
+       struct lv_list *lvl;
+       unsigned num_snapshots = 0;
+
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (lv_is_cow(lvl->lv))
+                       num_snapshots++;
+
+       return num_snapshots;
+}
+
+unsigned vg_visible_lvs(const struct volume_group *vg)
+{
+       struct lv_list *lvl;
+       unsigned lv_count = 0;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (lv_is_visible(lvl->lv))
+                       lv_count++;
+       }
+
+       return lv_count;
+}
+
+uint32_t vg_mda_count(const struct volume_group *vg)
+{
+       return dm_list_size(&vg->fid->metadata_areas_in_use) +
+               dm_list_size(&vg->fid->metadata_areas_ignored);
+}
+
+uint32_t vg_mda_used_count(const struct volume_group *vg)
+{
+       uint32_t used_count = 0;
+       struct metadata_area *mda;
+
+       /*
+        * Ignored mdas could be on either list - the reason being the state
+        * may have changed from ignored to un-ignored and we need to write
+        * the state to disk.
+        */
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use)
+              if (!mda_is_ignored(mda))
+                      used_count++;
+
+       return used_count;
+}
+
+uint32_t vg_mda_copies(const struct volume_group *vg)
+{
+       return vg->mda_copies;
+}
+
+uint64_t vg_mda_size(const struct volume_group *vg)
+{
+       return find_min_mda_size(&vg->fid->metadata_areas_in_use);
+}
+
+uint64_t vg_mda_free(const struct volume_group *vg)
+{
+       uint64_t freespace = UINT64_MAX, mda_free;
+       struct metadata_area *mda;
+
+       dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+               if (!mda->ops->mda_free_sectors)
+                       continue;
+               mda_free = mda->ops->mda_free_sectors(mda);
+               if (mda_free < freespace)
+                       freespace = mda_free;
+       }
+
+       if (freespace == UINT64_MAX)
+               freespace = UINT64_C(0);
+       return freespace;
+}
+
+int vg_set_mda_copies(struct volume_group *vg, uint32_t mda_copies)
+{
+       vg->mda_copies = mda_copies;
+
+       /* FIXME Use log_verbose when this is due to specific cmdline request. */
+       log_debug("Setting mda_copies to %"PRIu32" for VG %s",
+                   mda_copies, vg->name);
+
+       return 1;
+}
+
+static int _recalc_extents(uint32_t *extents, const char *desc1,
+                          const char *desc2, uint32_t old_size,
+                          uint32_t new_size)
+{
+       uint64_t size = (uint64_t) old_size * (*extents);
+
+       if (size % new_size) {
+               log_error("New size %" PRIu64 " for %s%s not an exact number "
+                         "of new extents.", size, desc1, desc2);
+               return 0;
+       }
+
+       size /= new_size;
+
+       if (size > UINT32_MAX) {
+               log_error("New extent count %" PRIu64 " for %s%s exceeds "
+                         "32 bits.", size, desc1, desc2);
+               return 0;
+       }
+
+       *extents = (uint32_t) size;
+
+       return 1;
+}
+
+int vg_set_extent_size(struct volume_group *vg, uint32_t new_size)
+{
+       uint32_t old_size = vg->extent_size;
+       struct pv_list *pvl;
+       struct lv_list *lvl;
+       struct physical_volume *pv;
+       struct logical_volume *lv;
+       struct lv_segment *seg;
+       struct pv_segment *pvseg;
+       uint32_t s;
+
+       if (!vg_is_resizeable(vg)) {
+               log_error("Volume group \"%s\" must be resizeable "
+                         "to change PE size", vg->name);
+               return 0;
+       }
+
+       if (!new_size) {
+               log_error("Physical extent size may not be zero");
+               return 0;
+       }
+
+       if (new_size == vg->extent_size)
+               return 1;
+
+       if (new_size & (new_size - 1)) {
+               log_error("Physical extent size must be a power of 2.");
+               return 0;
+       }
+
+       if (new_size > vg->extent_size) {
+               if ((uint64_t) vg_size(vg) % new_size) {
+                       /* FIXME Adjust used PV sizes instead */
+                       log_error("New extent size is not a perfect fit");
+                       return 0;
+               }
+       }
+
+       vg->extent_size = new_size;
+
+       if (vg->fid->fmt->ops->vg_setup &&
+           !vg->fid->fmt->ops->vg_setup(vg->fid, vg))
+               return_0;
+
+       if (!_recalc_extents(&vg->extent_count, vg->name, "", old_size,
+                            new_size))
+               return_0;
+
+       if (!_recalc_extents(&vg->free_count, vg->name, " free space",
+                            old_size, new_size))
+               return_0;
+
+       /* foreach PV */
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+
+               pv->pe_size = new_size;
+               if (!_recalc_extents(&pv->pe_count, pv_dev_name(pv), "",
+                                    old_size, new_size))
+                       return_0;
+
+               if (!_recalc_extents(&pv->pe_alloc_count, pv_dev_name(pv),
+                                    " allocated space", old_size, new_size))
+                       return_0;
+
+               /* foreach free PV Segment */
+               dm_list_iterate_items(pvseg, &pv->segments) {
+                       if (pvseg_is_allocated(pvseg))
+                               continue;
+
+                       if (!_recalc_extents(&pvseg->pe, pv_dev_name(pv),
+                                            " PV segment start", old_size,
+                                            new_size))
+                               return_0;
+                       if (!_recalc_extents(&pvseg->len, pv_dev_name(pv),
+                                            " PV segment length", old_size,
+                                            new_size))
+                               return_0;
+               }
+       }
+
+       /* foreach LV */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv = lvl->lv;
+
+               if (!_recalc_extents(&lv->le_count, lv->name, "", old_size,
+                                    new_size))
+                       return_0;
+
+               dm_list_iterate_items(seg, &lv->segments) {
+                       if (!_recalc_extents(&seg->le, lv->name,
+                                            " segment start", old_size,
+                                            new_size))
+                               return_0;
+
+                       if (!_recalc_extents(&seg->len, lv->name,
+                                            " segment length", old_size,
+                                            new_size))
+                               return_0;
+
+                       if (!_recalc_extents(&seg->area_len, lv->name,
+                                            " area length", old_size,
+                                            new_size))
+                               return_0;
+
+                       if (!_recalc_extents(&seg->extents_copied, lv->name,
+                                            " extents moved", old_size,
+                                            new_size))
+                               return_0;
+
+                       /* foreach area */
+                       for (s = 0; s < seg->area_count; s++) {
+                               switch (seg_type(seg, s)) {
+                               case AREA_PV:
+                                       if (!_recalc_extents
+                                           (&seg_pe(seg, s),
+                                            lv->name,
+                                            " pvseg start", old_size,
+                                            new_size))
+                                               return_0;
+                                       if (!_recalc_extents
+                                           (&seg_pvseg(seg, s)->len,
+                                            lv->name,
+                                            " pvseg length", old_size,
+                                            new_size))
+                                               return_0;
+                                       break;
+                               case AREA_LV:
+                                       if (!_recalc_extents
+                                           (&seg_le(seg, s), lv->name,
+                                            " area start", old_size,
+                                            new_size))
+                                               return_0;
+                                       break;
+                               case AREA_UNASSIGNED:
+                                       log_error("Unassigned area %u found in "
+                                                 "segment", s);
+                                       return 0;
+                               }
+                       }
+               }
+
+       }
+
+       return 1;
+}
+
+int vg_set_max_lv(struct volume_group *vg, uint32_t max_lv)
+{
+       if (!vg_is_resizeable(vg)) {
+               log_error("Volume group \"%s\" must be resizeable "
+                         "to change MaxLogicalVolume", vg->name);
+               return 0;
+       }
+
+       if (!(vg->fid->fmt->features & FMT_UNLIMITED_VOLS)) {
+               if (!max_lv)
+                       max_lv = 255;
+               else if (max_lv > 255) {
+                       log_error("MaxLogicalVolume limit is 255");
+                       return 0;
+               }
+       }
+
+       if (max_lv && max_lv < vg_visible_lvs(vg)) {
+               log_error("MaxLogicalVolume is less than the current number "
+                         "%d of LVs for %s", vg_visible_lvs(vg),
+                         vg->name);
+               return 0;
+       }
+       vg->max_lv = max_lv;
+
+       return 1;
+}
+
+int vg_set_max_pv(struct volume_group *vg, uint32_t max_pv)
+{
+       if (!vg_is_resizeable(vg)) {
+               log_error("Volume group \"%s\" must be resizeable "
+                         "to change MaxPhysicalVolumes", vg->name);
+               return 0;
+       }
+
+       if (!(vg->fid->fmt->features & FMT_UNLIMITED_VOLS)) {
+               if (!max_pv)
+                       max_pv = 255;
+               else if (max_pv > 255) {
+                       log_error("MaxPhysicalVolume limit is 255");
+                       return 0;
+               }
+       }
+
+       if (max_pv && max_pv < vg->pv_count) {
+               log_error("MaxPhysicalVolumes is less than the current number "
+                         "%d of PVs for \"%s\"", vg->pv_count,
+                         vg->name);
+               return 0;
+       }
+       vg->max_pv = max_pv;
+       return 1;
+}
+
+int vg_set_alloc_policy(struct volume_group *vg, alloc_policy_t alloc)
+{
+       if (alloc == ALLOC_INHERIT) {
+               log_error("Volume Group allocation policy cannot inherit "
+                         "from anything");
+               return 0;
+       }
+
+       if (alloc == vg->alloc)
+               return 1;
+
+       vg->alloc = alloc;
+       return 1;
+}
+
+int vg_set_clustered(struct volume_group *vg, int clustered)
+{
+       struct lv_list *lvl;
+
+       /*
+        * We do not currently support switching the cluster attribute
+        * on active mirrors or snapshots.
+        */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (lv_is_mirrored(lvl->lv) && lv_is_active(lvl->lv)) {
+                       log_error("Mirror logical volumes must be inactive "
+                                 "when changing the cluster attribute.");
+                       return 0;
+               }
+
+               if (clustered) {
+                       if (lv_is_origin(lvl->lv) || lv_is_cow(lvl->lv)) {
+                               log_error("Volume group %s contains snapshots "
+                                         "that are not yet supported.",
+                                         vg->name);
+                               return 0;
+                       }
+               }
+
+               if ((lv_is_origin(lvl->lv) || lv_is_cow(lvl->lv)) &&
+                   lv_is_active(lvl->lv)) {
+                       log_error("Snapshot logical volumes must be inactive "
+                                 "when changing the cluster attribute.");
+                       return 0;
+               }
+       }
+
+       if (clustered)
+               vg->status |= CLUSTERED;
+       else
+               vg->status &= ~CLUSTERED;
+       return 1;
+}
+
+char *vg_attr_dup(struct dm_pool *mem, const struct volume_group *vg)
+{
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(mem, 7))) {
+               log_error("dm_pool_alloc failed");
+               return NULL;
+       }
+
+       repstr[0] = (vg->status & LVM_WRITE) ? 'w' : 'r';
+       repstr[1] = (vg_is_resizeable(vg)) ? 'z' : '-';
+       repstr[2] = (vg_is_exported(vg)) ? 'x' : '-';
+       repstr[3] = (vg_missing_pv_count(vg)) ? 'p' : '-';
+       repstr[4] = alloc_policy_char(vg->alloc);
+       repstr[5] = (vg_is_clustered(vg)) ? 'c' : '-';
+       return repstr;
+}
diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h
new file mode 100644 (file)
index 0000000..448dcfe
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_VG_H
+#define _LVM_VG_H
+
+struct cmd_context;
+struct dm_pool;
+struct format_instance;
+struct dm_list;
+struct id;
+
+typedef enum {
+       ALLOC_INVALID,
+       ALLOC_CONTIGUOUS,
+       ALLOC_CLING,
+       ALLOC_CLING_BY_TAGS,    /* Internal - never written or displayed. */
+       ALLOC_NORMAL,
+       ALLOC_ANYWHERE,
+       ALLOC_INHERIT
+} alloc_policy_t;
+
+struct volume_group {
+       struct cmd_context *cmd;
+       struct dm_pool *vgmem;
+       struct format_instance *fid;
+       struct dm_list *cmd_vgs;/* List of wanted/locked and opened VGs */
+       uint32_t cmd_missing_vgs;/* Flag marks missing VG */
+       uint32_t seqno;         /* Metadata sequence number */
+
+       alloc_policy_t alloc;
+       uint64_t status;
+
+       struct id id;
+       const char *name;
+       const char *old_name;           /* Set during vgrename and vgcfgrestore */
+       char *system_id;
+
+       uint32_t extent_size;
+       uint32_t extent_count;
+       uint32_t free_count;
+
+       uint32_t max_lv;
+       uint32_t max_pv;
+
+       /* physical volumes */
+       uint32_t pv_count;
+       struct dm_list pvs;
+
+       /*
+        * logical volumes
+        * The following relationship should always hold:
+        * dm_list_size(lvs) = user visible lv_count + snapshot_count + other invisible LVs
+        *
+        * Snapshots consist of 2 instances of "struct logical_volume":
+        * - cow (lv_name is visible to the user)
+        * - snapshot (lv_name is 'snapshotN')
+        *
+        * Mirrors consist of multiple instances of "struct logical_volume":
+        * - one for the mirror log
+        * - one for each mirror leg
+        * - one for the user-visible mirror LV
+        */
+       struct dm_list lvs;
+
+       struct dm_list tags;
+
+       /*
+        * FIXME: Move the next fields into a different struct?
+        */
+
+       /*
+        * List of removed physical volumes by pvreduce.
+        * They have to get cleared on vg_commit.
+        */
+       struct dm_list removed_pvs;
+       uint32_t open_mode; /* FIXME: read or write - check lock type? */
+
+       /*
+        * Store result of the last vg_read().
+        * 0 for success else appropriate FAILURE_* bits set.
+        */
+       uint32_t read_status;
+       uint32_t mda_copies; /* target number of mdas for this VG */
+};
+
+char *vg_fmt_dup(const struct volume_group *vg);
+char *vg_name_dup(const struct volume_group *vg);
+char *vg_system_id_dup(const struct volume_group *vg);
+uint32_t vg_seqno(const struct volume_group *vg);
+uint64_t vg_status(const struct volume_group *vg);
+int vg_set_alloc_policy(struct volume_group *vg, alloc_policy_t alloc);
+int vg_set_clustered(struct volume_group *vg, int clustered);
+uint64_t vg_size(const struct volume_group *vg);
+uint64_t vg_free(const struct volume_group *vg);
+uint64_t vg_extent_size(const struct volume_group *vg);
+int vg_set_extent_size(struct volume_group *vg, uint32_t new_extent_size);
+uint64_t vg_extent_count(const struct volume_group *vg);
+uint64_t vg_free_count(const struct volume_group *vg);
+uint64_t vg_pv_count(const struct volume_group *vg);
+uint64_t vg_max_pv(const struct volume_group *vg);
+int vg_set_max_pv(struct volume_group *vg, uint32_t max_pv);
+uint64_t vg_max_lv(const struct volume_group *vg);
+int vg_set_max_lv(struct volume_group *vg, uint32_t max_lv);
+uint32_t vg_mda_count(const struct volume_group *vg);
+uint32_t vg_mda_used_count(const struct volume_group *vg);
+uint32_t vg_mda_copies(const struct volume_group *vg);
+int vg_set_mda_copies(struct volume_group *vg, uint32_t mda_copies);
+/*
+ * Returns visible LV count - number of LVs from user perspective
+ */
+unsigned vg_visible_lvs(const struct volume_group *vg);
+/*
+ * Count snapshot LVs.
+ */
+unsigned snapshot_count(const struct volume_group *vg);
+
+uint64_t vg_mda_size(const struct volume_group *vg);
+uint64_t vg_mda_free(const struct volume_group *vg);
+char *vg_attr_dup(struct dm_pool *mem, const struct volume_group *vg);
+char *vg_uuid_dup(const struct volume_group *vg);
+char *vg_tags_dup(const struct volume_group *vg);
+
+#endif /* _LVM_VG_H */
diff --git a/lib/mirror/.exported_symbols b/lib/mirror/.exported_symbols
new file mode 100644 (file)
index 0000000..1c92c6a
--- /dev/null
@@ -0,0 +1 @@
+init_segtype
diff --git a/lib/mirror/Makefile.in b/lib/mirror/Makefile.in
new file mode 100644 (file)
index 0000000..5168eda
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = mirrored.c
+
+LIB_SHARED = liblvm2mirror.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
diff --git a/lib/mirror/mirrored.c b/lib/mirror/mirrored.c
new file mode 100644 (file)
index 0000000..3d25d70
--- /dev/null
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "defaults.h"
+#include "lvm-string.h"
+#include "targets.h"
+#include "activate.h"
+#include "sharedlib.h"
+#include "str_list.h"
+
+#include <sys/utsname.h>
+
+static int _block_on_error_available = 0;
+static unsigned _mirror_attributes = 0;
+
+enum {
+       MIRR_DISABLED,
+       MIRR_RUNNING,
+       MIRR_COMPLETED
+};
+
+struct mirror_state {
+       uint32_t default_region_size;
+};
+
+static const char *_mirrored_name(const struct lv_segment *seg)
+{
+       return seg->segtype->name;
+}
+
+static void _mirrored_display(const struct lv_segment *seg)
+{
+       const char *size;
+       uint32_t s;
+
+       log_print("  Mirrors\t\t%u", seg->area_count);
+       log_print("  Mirror size\t\t%u", seg->area_len);
+       if (seg->log_lv)
+               log_print("  Mirror log volume\t%s", seg->log_lv->name);
+
+       if (seg->region_size) {
+               size = display_size(seg->lv->vg->cmd,
+                                   (uint64_t) seg->region_size);
+               log_print("  Mirror region size\t%s", size);
+       }
+
+       log_print("  Mirror original:");
+       display_stripe(seg, 0, "    ");
+       log_print("  Mirror destinations:");
+       for (s = 1; s < seg->area_count; s++)
+               display_stripe(seg, s, "    ");
+       log_print(" ");
+}
+
+static int _mirrored_text_import_area_count(const struct config_node *sn, uint32_t *area_count)
+{
+       if (!get_config_uint32(sn, "mirror_count", area_count)) {
+               log_error("Couldn't read 'mirror_count' for "
+                         "segment '%s'.", config_parent_name(sn));
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _mirrored_text_import(struct lv_segment *seg, const struct config_node *sn,
+                       struct dm_hash_table *pv_hash)
+{
+       const struct config_node *cn;
+       const char *logname = NULL;
+
+       if (find_config_node(sn, "extents_moved")) {
+               if (get_config_uint32(sn, "extents_moved",
+                                     &seg->extents_copied))
+                       seg->status |= PVMOVE;
+               else {
+                       log_error("Couldn't read 'extents_moved' for "
+                                 "segment %s of logical volume %s.",
+                                 config_parent_name(sn), seg->lv->name);
+                       return 0;
+               }
+       }
+
+       if (find_config_node(sn, "region_size")) {
+               if (!get_config_uint32(sn, "region_size",
+                                     &seg->region_size)) {
+                       log_error("Couldn't read 'region_size' for "
+                                 "segment %s of logical volume %s.",
+                                 config_parent_name(sn), seg->lv->name);
+                       return 0;
+               }
+       }
+
+       if ((cn = find_config_node(sn, "mirror_log"))) {
+               if (!cn->v || !cn->v->v.str) {
+                       log_error("Mirror log type must be a string.");
+                       return 0;
+               }
+               logname = cn->v->v.str;
+               if (!(seg->log_lv = find_lv(seg->lv->vg, logname))) {
+                       log_error("Unrecognised mirror log in "
+                                 "segment %s of logical volume %s.",
+                                 config_parent_name(sn), seg->lv->name);
+                       return 0;
+               }
+               seg->log_lv->status |= MIRROR_LOG;
+       }
+
+       if (logname && !seg->region_size) {
+               log_error("Missing region size for mirror log for "
+                         "segment %s of logical volume %s.",
+                         config_parent_name(sn), seg->lv->name);
+               return 0;
+       }
+
+       if (!(cn = find_config_node(sn, "mirrors"))) {
+               log_error("Couldn't find mirrors array for "
+                         "segment %s of logical volume %s.",
+                         config_parent_name(sn), seg->lv->name);
+               return 0;
+       }
+
+       return text_import_areas(seg, sn, cn, pv_hash, MIRROR_IMAGE);
+}
+
+static int _mirrored_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+       outf(f, "mirror_count = %u", seg->area_count);
+       if (seg->status & PVMOVE)
+               outsize(f, (uint64_t) seg->extents_copied * seg->lv->vg->extent_size,
+                       "extents_moved = %" PRIu32, seg->extents_copied);
+       if (seg->log_lv)
+               outf(f, "mirror_log = \"%s\"", seg->log_lv->name);
+       if (seg->region_size)
+               outf(f, "region_size = %" PRIu32, seg->region_size);
+
+       return out_areas(f, seg, "mirror");
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static struct mirror_state *_mirrored_init_target(struct dm_pool *mem,
+                                        struct cmd_context *cmd)
+{
+       struct mirror_state *mirr_state;
+
+       if (!(mirr_state = dm_pool_alloc(mem, sizeof(*mirr_state)))) {
+               log_error("struct mirr_state allocation failed");
+               return NULL;
+       }
+
+       mirr_state->default_region_size = 2 *
+           find_config_tree_int(cmd,
+                           "activation/mirror_region_size",
+                           DEFAULT_MIRROR_REGION_SIZE);
+
+       return mirr_state;
+}
+
+static int _mirrored_target_percent(void **target_state,
+                                   percent_t *percent,
+                                   struct dm_pool *mem,
+                                   struct cmd_context *cmd,
+                                   struct lv_segment *seg, char *params,
+                                   uint64_t *total_numerator,
+                                   uint64_t *total_denominator)
+{
+       struct mirror_state *mirr_state;
+       uint64_t numerator, denominator;
+       unsigned mirror_count, m;
+       int used;
+       char *pos = params;
+
+       if (!*target_state)
+               *target_state = _mirrored_init_target(mem, cmd);
+
+       mirr_state = *target_state;
+
+       /* Status line: <#mirrors> (maj:min)+ <synced>/<total_regions> */
+       log_debug("Mirror status: %s", params);
+
+       if (sscanf(pos, "%u %n", &mirror_count, &used) != 1) {
+               log_error("Failure parsing mirror status mirror count: %s",
+                         params);
+               return 0;
+       }
+       pos += used;
+
+       for (m = 0; m < mirror_count; m++) {
+               if (sscanf(pos, "%*x:%*x %n", &used) != 0) {
+                       log_error("Failure parsing mirror status devices: %s",
+                                 params);
+                       return 0;
+               }
+               pos += used;
+       }
+
+       if (sscanf(pos, "%" PRIu64 "/%" PRIu64 "%n", &numerator, &denominator,
+                  &used) != 2) {
+               log_error("Failure parsing mirror status fraction: %s", params);
+               return 0;
+       }
+       pos += used;
+
+       *total_numerator += numerator;
+       *total_denominator += denominator;
+
+       if (seg)
+               seg->extents_copied = seg->area_len * numerator / denominator;
+
+        *percent = make_percent(numerator, denominator);
+
+       return 1;
+}
+
+static int _mirrored_transient_status(struct lv_segment *seg, char *params)
+{
+       int i, j;
+       struct logical_volume *lv = seg->lv;
+       struct lvinfo info;
+       char *p = NULL;
+       char **args, **log_args;
+       struct logical_volume **images;
+       struct logical_volume *log;
+       int num_devs, log_argc;
+       int failed = 0;
+       char *status;
+
+       log_very_verbose("Mirrored transient status: \"%s\"", params);
+
+       /* number of devices */
+       if (!dm_split_words(params, 1, 0, &p))
+               return_0;
+
+       if (!(num_devs = atoi(p)))
+               return_0;
+
+       p += strlen(p) + 1;
+
+       if (num_devs > DEFAULT_MIRROR_MAX_IMAGES) {
+               log_error("Unexpectedly many (%d) mirror images in %s.",
+                         num_devs, lv->name);
+               return_0;
+       }
+
+       args = alloca((num_devs + 5) * sizeof(char *));
+       images = alloca(num_devs * sizeof(struct logical_volume *));
+
+       if (dm_split_words(p, num_devs + 4, 0, args) < num_devs + 4)
+               return_0;
+
+       log_argc = atoi(args[3 + num_devs]);
+       log_args = alloca(log_argc * sizeof(char *));
+
+       if (log_argc > 16) {
+               log_error("Unexpectedly many (%d) log arguments in %s.",
+                         log_argc, lv->name);
+               return_0;
+       }
+
+
+       if (dm_split_words(args[3 + num_devs] + strlen(args[3 + num_devs]) + 1,
+                          log_argc, 0, log_args) < log_argc)
+               return_0;
+
+       if (num_devs != seg->area_count) {
+               log_error("Active mirror has a wrong number of mirror images!");
+               log_error("Metadata says %d, kernel says %d.", seg->area_count, num_devs);
+               return_0;
+       }
+
+       if (!strcmp(log_args[0], "disk")) {
+               char buf[32];
+               log = first_seg(lv)->log_lv;
+               if (!lv_info(lv->vg->cmd, log, 0, &info, 0, 0)) {
+                       log_error("Check for existence of mirror log %s failed.",
+                                 log->name);
+                       return 0;
+               }
+               log_debug("Found mirror log at %d:%d", info.major, info.minor);
+               sprintf(buf, "%d:%d", info.major, info.minor);
+               if (strcmp(buf, log_args[1])) {
+                       log_error("Mirror log mismatch. Metadata says %s, kernel says %s.",
+                                 buf, log_args[1]);
+                       return_0;
+               }
+               log_very_verbose("Status of log (%s): %s", buf, log_args[2]);
+               if (log_args[2][0] != 'A') {
+                       log->status |= PARTIAL_LV;
+                       ++failed;
+               }
+       }
+
+       for (i = 0; i < num_devs; ++i)
+               images[i] = NULL;
+
+       for (i = 0; i < seg->area_count; ++i) {
+               char buf[32];
+               if (!lv_info(lv->vg->cmd, seg_lv(seg, i), 0, &info, 0, 0)) {
+                       log_error("Check for existence of mirror image %s failed.",
+                                 seg_lv(seg, i)->name);
+                       return 0;
+               }
+               log_debug("Found mirror image at %d:%d", info.major, info.minor);
+               sprintf(buf, "%d:%d", info.major, info.minor);
+               for (j = 0; j < num_devs; ++j) {
+                       if (!strcmp(buf, args[j])) {
+                           log_debug("Match: metadata image %d matches kernel image %d", i, j);
+                           images[j] = seg_lv(seg, i);
+                       }
+               }
+       }
+
+       status = args[2 + num_devs];
+
+       for (i = 0; i < num_devs; ++i) {
+               if (!images[i]) {
+                       log_error("Failed to find image %d (%s).", i, args[i]);
+                       return_0;
+               }
+               log_very_verbose("Status of image %d: %c", i, status[i]);
+               if (status[i] != 'A') {
+                       images[i]->status |= PARTIAL_LV;
+                       ++failed;
+               }
+       }
+
+       /* update PARTIAL_LV flags across the VG */
+       if (failed)
+               vg_mark_partial_lvs(lv->vg);
+
+       return 1;
+}
+
+static int _add_log(struct dm_pool *mem, struct lv_segment *seg,
+                   struct dm_tree_node *node, uint32_t area_count, uint32_t region_size)
+{
+       unsigned clustered = 0;
+       char *log_dlid = NULL;
+       uint32_t log_flags = 0;
+
+       /*
+        * Use clustered mirror log for non-exclusive activation
+        * in clustered VG.
+        */
+       if ((!(seg->lv->status & ACTIVATE_EXCL) &&
+             (vg_is_clustered(seg->lv->vg))))
+               clustered = 1;
+
+       if (seg->log_lv) {
+               /* If disk log, use its UUID */
+               if (!(log_dlid = build_dm_uuid(mem, seg->log_lv->lvid.s, NULL))) {
+                       log_error("Failed to build uuid for log LV %s.",
+                                 seg->log_lv->name);
+                       return 0;
+               }
+       } else {
+               /* If core log, use mirror's UUID and set DM_CORELOG flag */
+               if (!(log_dlid = build_dm_uuid(mem, seg->lv->lvid.s, NULL))) {
+                       log_error("Failed to build uuid for mirror LV %s.",
+                                 seg->lv->name);
+                       return 0;
+               }
+               log_flags |= DM_CORELOG;
+       }
+
+       if (mirror_in_sync() && !(seg->status & PVMOVE))
+               log_flags |= DM_NOSYNC;
+
+       if (_block_on_error_available && !(seg->status & PVMOVE))
+               log_flags |= DM_BLOCK_ON_ERROR;
+
+       return dm_tree_node_add_mirror_target_log(node, region_size, clustered, log_dlid, area_count, log_flags);
+}
+
+static int _mirrored_add_target_line(struct dev_manager *dm, struct dm_pool *mem,
+                               struct cmd_context *cmd, void **target_state,
+                               struct lv_segment *seg,
+                               struct dm_tree_node *node, uint64_t len,
+                               uint32_t *pvmove_mirror_count)
+{
+       struct mirror_state *mirr_state;
+       uint32_t area_count = seg->area_count;
+       unsigned start_area = 0u;
+       int mirror_status = MIRR_RUNNING;
+       uint32_t region_size;
+       int r;
+
+       if (!*target_state)
+               *target_state = _mirrored_init_target(mem, cmd);
+
+       mirr_state = *target_state;
+
+       /*
+        * Mirror segment could have only 1 area temporarily
+        * if the segment is under conversion.
+        */
+       if (seg->area_count == 1)
+               mirror_status = MIRR_DISABLED;
+
+       /*
+        * For pvmove, only have one mirror segment RUNNING at once.
+        * Segments before this are COMPLETED and use 2nd area.
+        * Segments after this are DISABLED and use 1st area.
+        */
+       if (seg->status & PVMOVE) {
+               if (seg->extents_copied == seg->area_len) {
+                       mirror_status = MIRR_COMPLETED;
+                       start_area = 1;
+               } else if ((*pvmove_mirror_count)++) {
+                       mirror_status = MIRR_DISABLED;
+                       area_count = 1;
+               }
+               /* else MIRR_RUNNING */
+       }
+
+       if (mirror_status != MIRR_RUNNING) {
+               if (!dm_tree_node_add_linear_target(node, len))
+                       return_0;
+               goto done;
+       }
+
+       if (!(seg->status & PVMOVE)) {
+               if (!seg->region_size) {
+                       log_error("Missing region size for mirror segment.");
+                       return 0;
+               }
+               region_size = seg->region_size;
+
+       } else
+               region_size = adjusted_mirror_region_size(seg->lv->vg->extent_size,
+                                                         seg->area_len,
+                                                         mirr_state->default_region_size);
+
+       if (!dm_tree_node_add_mirror_target(node, len))
+               return_0;
+
+       if ((r = _add_log(mem, seg, node, area_count, region_size)) <= 0) {
+               stack;
+               return r;
+       }
+
+      done:
+       return add_areas_line(dm, seg, node, start_area, area_count);
+}
+
+static int _mirrored_target_present(struct cmd_context *cmd,
+                                   const struct lv_segment *seg,
+                                   unsigned *attributes)
+{
+       static int _mirrored_checked = 0;
+       static int _mirrored_present = 0;
+       uint32_t maj, min, patchlevel;
+       unsigned maj2, min2, patchlevel2;
+       char vsn[80];
+       struct utsname uts;
+       unsigned kmaj, kmin, krel;
+
+       if (!_mirrored_checked) {
+               _mirrored_present = target_present(cmd, "mirror", 1);
+
+               /*
+                * block_on_error available as "block_on_error" log
+                * argument with mirror target >= 1.1 and <= 1.11
+                * or with 1.0 in RHEL4U3 driver >= 4.5
+                *
+                * block_on_error available as "handle_errors" mirror
+                * argument with mirror target >= 1.12.
+                *
+                * libdm-deptree.c is smart enough to handle the differences
+                * between block_on_error and handle_errors for all
+                * mirror target versions >= 1.1
+                */
+               /* FIXME Move this into libdevmapper */
+
+               if (target_version("mirror", &maj, &min, &patchlevel) &&
+                   maj == 1 &&
+                   ((min >= 1) ||
+                    (min == 0 && driver_version(vsn, sizeof(vsn)) &&
+                     sscanf(vsn, "%u.%u.%u", &maj2, &min2, &patchlevel2) == 3 &&
+                     maj2 == 4 && min2 == 5 && patchlevel2 == 0)))     /* RHEL4U3 */
+                       _block_on_error_available = 1;
+       }
+
+       /*
+        * Check only for modules if atttributes requested and no previous check.
+        * FIXME: Fails incorrectly if cmirror was built into kernel.
+        */
+       if (attributes) {
+               if (!_mirror_attributes) {
+                       /*
+                        * The dm-log-userspace module was added to the
+                        * 2.6.31 kernel.
+                        */
+                       if (!uname(&uts) &&
+                           (sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel) == 3) &&
+                           KERNEL_VERSION(kmaj, kmin, krel) < KERNEL_VERSION(2, 6, 31)) {
+                               if (module_present(cmd, "log-clustered"))
+                                       _mirror_attributes |= MIRROR_LOG_CLUSTERED;
+                       } else if (module_present(cmd, "log-userspace"))
+                               _mirror_attributes |= MIRROR_LOG_CLUSTERED;
+
+                       if (!(_mirror_attributes & MIRROR_LOG_CLUSTERED))
+                               log_verbose("Cluster mirror log module is not available");
+
+                       /*
+                        * The cluster mirror log daemon must be running,
+                        * otherwise, the kernel module will fail to make
+                        * contact.
+                        */
+#ifdef CMIRRORD_PIDFILE
+                       if (!dm_daemon_is_running(CMIRRORD_PIDFILE)) {
+                               log_verbose("Cluster mirror log daemon is not running");
+                               _mirror_attributes &= ~MIRROR_LOG_CLUSTERED;
+                       }
+#else
+                       log_verbose("Cluster mirror log daemon not included in build");
+                       _mirror_attributes &= ~MIRROR_LOG_CLUSTERED;
+#endif
+               }
+               *attributes = _mirror_attributes;
+       }
+       _mirrored_checked = 1;
+
+       return _mirrored_present;
+}
+
+#ifdef DMEVENTD
+static const char *_get_mirror_dso_path(struct cmd_context *cmd)
+{
+       return get_monitor_dso_path(cmd, find_config_tree_str(cmd, "dmeventd/mirror_library",
+                                                             DEFAULT_DMEVENTD_MIRROR_LIB));
+}
+
+/* FIXME Cache this */
+static int _target_registered(struct lv_segment *seg, int *pending)
+{
+       return target_registered_with_dmeventd(seg->lv->vg->cmd, _get_mirror_dso_path(seg->lv->vg->cmd),
+                                              seg->lv, pending);
+}
+
+/* FIXME This gets run while suspended and performs banned operations. */
+static int _target_set_events(struct lv_segment *seg, int evmask, int set)
+{
+       return target_register_events(seg->lv->vg->cmd, _get_mirror_dso_path(seg->lv->vg->cmd),
+                                     seg->lv, evmask, set, 0);
+}
+
+static int _target_monitor_events(struct lv_segment *seg, int events)
+{
+       return _target_set_events(seg, events, 1);
+}
+
+static int _target_unmonitor_events(struct lv_segment *seg, int events)
+{
+       return _target_set_events(seg, events, 0);
+}
+
+#endif /* DMEVENTD */
+#endif /* DEVMAPPER_SUPPORT */
+
+static int _mirrored_modules_needed(struct dm_pool *mem,
+                                   const struct lv_segment *seg,
+                                   struct dm_list *modules)
+{
+       if (seg->log_lv &&
+           !list_segment_modules(mem, first_seg(seg->log_lv), modules))
+               return_0;
+
+       if (vg_is_clustered(seg->lv->vg) &&
+           !str_list_add(mem, modules, "clog")) {
+               log_error("cluster log string list allocation failed");
+               return 0;
+       }
+
+       if (!str_list_add(mem, modules, "mirror")) {
+               log_error("mirror string list allocation failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _mirrored_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _mirrored_ops = {
+       .name = _mirrored_name,
+       .display = _mirrored_display,
+       .text_import_area_count = _mirrored_text_import_area_count,
+       .text_import = _mirrored_text_import,
+       .text_export = _mirrored_text_export,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _mirrored_add_target_line,
+       .target_percent = _mirrored_target_percent,
+       .target_present = _mirrored_target_present,
+       .check_transient_status = _mirrored_transient_status,
+#ifdef DMEVENTD
+       .target_monitored = _target_registered,
+       .target_monitor_events = _target_monitor_events,
+       .target_unmonitor_events = _target_unmonitor_events,
+#endif
+#endif
+       .modules_needed = _mirrored_modules_needed,
+       .destroy = _mirrored_destroy,
+};
+
+#ifdef MIRRORED_INTERNAL
+struct segment_type *init_mirrored_segtype(struct cmd_context *cmd)
+#else                          /* Shared */
+struct segment_type *init_segtype(struct cmd_context *cmd);
+struct segment_type *init_segtype(struct cmd_context *cmd)
+#endif
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_mirrored_ops;
+       segtype->name = "mirror";
+       segtype->private = NULL;
+       segtype->flags = SEG_AREAS_MIRRORED;
+
+#ifdef DMEVENTD
+       if (_get_mirror_dso_path(cmd))
+               segtype->flags |= SEG_MONITORED;
+#endif
+
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/lib/misc/configure.h.in b/lib/misc/configure.h.in
new file mode 100644 (file)
index 0000000..d8e2db8
--- /dev/null
@@ -0,0 +1,618 @@
+/* lib/misc/configure.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if the `closedir' function returns void instead of `int'. */
+#undef CLOSEDIR_VOID
+
+/* Define to 1 to include built-in support for clustered LVM locking. */
+#undef CLUSTER_LOCKING_INTERNAL
+
+/* Path to clvmd binary. */
+#undef CLVMD_PATH
+
+/* Path to clvmd pidfile. */
+#undef CLVMD_PIDFILE
+
+/* Path to cmirrord pidfile. */
+#undef CMIRRORD_PIDFILE
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Name of default metadata archive subdirectory. */
+#undef DEFAULT_ARCHIVE_SUBDIR
+
+/* Name of default metadata backup subdirectory. */
+#undef DEFAULT_BACKUP_SUBDIR
+
+/* Name of default metadata cache subdirectory. */
+#undef DEFAULT_CACHE_SUBDIR
+
+/* Default data alignment. */
+#undef DEFAULT_DATA_ALIGNMENT
+
+/* Name of default locking directory. */
+#undef DEFAULT_LOCK_DIR
+
+/* Name of default run directory. */
+#undef DEFAULT_RUN_DIR
+
+/* Define to 0 to reinstate the pre-2.02.54 handling of unit suffixes. */
+#undef DEFAULT_SI_UNIT_CONSISTENCY
+
+/* Path to LVM system directory. */
+#undef DEFAULT_SYS_DIR
+
+/* Define to 1 to enable LVM2 device-mapper interaction. */
+#undef DEVMAPPER_SUPPORT
+
+/* Define to 1 to enable the device-mapper event daemon. */
+#undef DMEVENTD
+
+/* Path to dmeventd binary. */
+#undef DMEVENTD_PATH
+
+/* Path to dmeventd pidfile. */
+#undef DMEVENTD_PIDFILE
+
+/* Library version */
+#undef DM_LIB_VERSION
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+   */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the <asm/byteorder.h> header file. */
+#undef HAVE_ASM_BYTEORDER_H
+
+/* Define to 1 if you have the <assert.h> header file. */
+#undef HAVE_ASSERT_H
+
+/* Define to 1 if canonicalize_file_name is available. */
+#undef HAVE_CANONICALIZE_FILE_NAME
+
+/* Define to 1 if you have the <ccs.h> header file. */
+#undef HAVE_CCS_H
+
+/* Define to 1 if your system has a working `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the <corosync/confdb.h> header file. */
+#undef HAVE_COROSYNC_CONFDB_H
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* Define to 1 if you have the `dup2' function. */
+#undef HAVE_DUP2
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if getline is available. */
+#undef HAVE_GETLINE
+
+/* Define to 1 if you have the `getmntent' function. */
+#undef HAVE_GETMNTENT
+
+/* Define to 1 if getopt_long is available. */
+#undef HAVE_GETOPTLONG
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#undef HAVE_LANGINFO_H
+
+/* Define to 1 if you have the <libcman.h> header file. */
+#undef HAVE_LIBCMAN_H
+
+/* Define to 1 if dynamic libraries are available. */
+#undef HAVE_LIBDL
+
+/* Define to 1 if you have the <libdlm.h> header file. */
+#undef HAVE_LIBDLM_H
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#undef HAVE_LIBGEN_H
+
+/* Define to 1 if you have the <libgulm.h> header file. */
+#undef HAVE_LIBGULM_H
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#undef HAVE_LIBINTL_H
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+#undef HAVE_LINUX_FS_H
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+#undef HAVE_LSTAT_EMPTY_STRING_BUG
+
+/* Define to 1 if you have the <machine/endian.h> header file. */
+#undef HAVE_MACHINE_ENDIAN_H
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#undef HAVE_MALLOC
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `mkdir' function. */
+#undef HAVE_MKDIR
+
+/* Define to 1 if you have the `mkfifo' function. */
+#undef HAVE_MKFIFO
+
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#undef HAVE_MNTENT_H
+
+/* Define to 1 if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#undef HAVE_NL_LANGINFO
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+#undef HAVE_READLINE_HISTORY_H
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+#undef HAVE_READLINE_READLINE_H
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+   and to 0 otherwise. */
+#undef HAVE_REALLOC
+
+/* Define to 1 to include support for realtime clock. */
+#undef HAVE_REALTIME
+
+/* Define to 1 if you have the `rl_completion_matches' function. */
+#undef HAVE_RL_COMPLETION_MATCHES
+
+/* Define to 1 if you have the `rmdir' function. */
+#undef HAVE_RMDIR
+
+/* Define to 1 if you have the <search.h> header file. */
+#undef HAVE_SEARCH_H
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 to include support for selinux. */
+#undef HAVE_SELINUX
+
+/* Define to 1 if you have the <selinux/label.h> header file. */
+#undef HAVE_SELINUX_LABEL_H
+
+/* Define to 1 if you have the <selinux/selinux.h> header file. */
+#undef HAVE_SELINUX_SELINUX_H
+
+/* Define to 1 if sepol_check_context is available. */
+#undef HAVE_SEPOL
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define to 1 if you have the `siginterrupt' function. */
+#undef HAVE_SIGINTERRUPT
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+#undef HAVE_STAT_EMPTY_STRING_BUG
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strcspn' function. */
+#undef HAVE_STRCSPN
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strrchr' function. */
+#undef HAVE_STRRCHR
+
+/* Define to 1 if you have the `strspn' function. */
+#undef HAVE_STRSPN
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define to 1 if `st_rdev' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_RDEV
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the <sys/disk.h> header file. */
+#undef HAVE_SYS_DISK_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#undef HAVE_SYS_IPC_H
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#undef HAVE_SYS_MMAN_H
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#undef HAVE_SYS_MOUNT_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/sem.h> header file. */
+#undef HAVE_SYS_SEM_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#undef HAVE_SYS_STATVFS_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#undef HAVE_SYS_UTSNAME_H
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the `uname' function. */
+#undef HAVE_UNAME
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utmpx.h> header file. */
+#undef HAVE_UTMPX_H
+
+/* Define to 1 if you have the <valgrind/memcheck.h> header file. */
+#undef HAVE_VALGRIND_MEMCHECK_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+   slash. */
+#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
+
+/* Define to 1 if 'lvm' should fall back to using LVM1 binaries if
+   device-mapper is missing from the kernel */
+#undef LVM1_FALLBACK
+
+/* Define to 1 to include built-in support for LVM1 metadata. */
+#undef LVM1_INTERNAL
+
+/* Path to lvm binary. */
+#undef LVM_PATH
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+   */
+#undef MAJOR_IN_MKDEV
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+   <sysmacros.h>. */
+#undef MAJOR_IN_SYSMACROS
+
+/* Define to 1 to include built-in support for mirrors. */
+#undef MIRRORED_INTERNAL
+
+/* The path to 'modprobe', if available. */
+#undef MODPROBE_CMD
+
+/* Define to 1 to enable O_DIRECT support. */
+#undef O_DIRECT_SUPPORT
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 to include built-in support for GFS pool metadata. */
+#undef POOL_INTERNAL
+
+/* Define to 1 to include the LVM readline shell. */
+#undef READLINE_SUPPORT
+
+/* Define to 1 to include built-in support for replicators. */
+#undef REPLICATOR_INTERNAL
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to the type of arg 1 for `select'. */
+#undef SELECT_TYPE_ARG1
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#undef SELECT_TYPE_ARG234
+
+/* Define to the type of arg 5 for `select'. */
+#undef SELECT_TYPE_ARG5
+
+/* Define to 1 to include built-in support for snapshots. */
+#undef SNAPSHOT_INTERNAL
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at runtime.
+       STACK_DIRECTION > 0 => grows toward higher addresses
+       STACK_DIRECTION < 0 => grows toward lower addresses
+       STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* Define to 1 to enable synchronisation with udev processing. */
+#undef UDEV_SYNC_SUPPORT
+
+/* Enable a valgrind aware build of pool */
+#undef VALGRIND_POOL
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+#undef int16_t
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+#undef int32_t
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef int64_t
+
+/* Define to the type of a signed integer type of width exactly 8 bits if such
+   a type exists and the standard includes do not define it. */
+#undef int8_t
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef mode_t
+
+/* Define to `long int' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint8_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
diff --git a/lib/misc/crc.c b/lib/misc/crc.c
new file mode 100644 (file)
index 0000000..9f95c37
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+
+#include "crc.h"
+
+/* Calculate an endian-independent CRC of supplied buffer */
+#ifndef DEBUG_CRC32
+uint32_t calc_crc(uint32_t initial, const uint8_t *buf, uint32_t size)
+#else
+static uint32_t _calc_crc_new(uint32_t initial, const uint8_t *buf, uint32_t size)
+#endif
+{
+       /* CRC-32 byte lookup table generated by crc_gen.c */
+       static const uint32_t crctab[] = {
+               0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+               0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+               0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+               0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+               0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+               0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+               0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+               0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+               0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+               0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+               0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+               0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+               0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+               0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+               0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+               0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+               0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+               0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+               0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+               0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+               0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+               0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+               0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+               0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+               0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+               0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+               0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+               0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+               0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+               0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+               0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+               0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+       };
+       const uint32_t *start = (const uint32_t *) buf;
+       const uint32_t *end = (const uint32_t *) (buf + (size & 0xfffffffc));
+       uint32_t crc = initial;
+   
+       /* Process 4 bytes per iteration */
+       while (start < end) {
+               crc = crc ^ *start++;
+               crc = crctab[crc & 0xff] ^ crc >> 8;
+               crc = crctab[crc & 0xff] ^ crc >> 8;
+               crc = crctab[crc & 0xff] ^ crc >> 8;
+               crc = crctab[crc & 0xff] ^ crc >> 8;
+       }
+
+       /* Process any bytes left over */
+       buf = (const uint8_t *) start;
+       size = size & 0x3;
+       while (size--) {
+               crc = crc ^ *buf++;
+               crc = crctab[crc & 0xff] ^ crc >> 8;
+       }
+
+       return crc;
+}
+
+#ifdef DEBUG_CRC32
+static uint32_t _calc_crc_old(uint32_t initial, const uint8_t *buf, uint32_t size)
+{
+       static const uint32_t crctab[] = {
+               0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+               0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+               0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+               0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+       };
+       uint32_t i, crc = initial;
+
+       for (i = 0; i < size; i++) {
+               crc ^= *buf++;
+               crc = (crc >> 4) ^ crctab[crc & 0xf];
+               crc = (crc >> 4) ^ crctab[crc & 0xf];
+       }
+       return crc;
+}
+
+uint32_t calc_crc(uint32_t initial, const uint8_t *buf, uint32_t size)
+{
+       uint32_t new_crc = _calc_crc_new(initial, buf, size);
+       uint32_t old_crc = _calc_crc_old(initial, buf, size);
+
+       if (new_crc != old_crc)
+               log_error(INTERNAL_ERROR "Old and new crc32 algorithms mismatch: 0x%08x != 0x%08x", old_crc, new_crc);
+
+       return old_crc;
+}
+
+#endif /* DEBUG_CRC32 */
diff --git a/lib/misc/crc.h b/lib/misc/crc.h
new file mode 100644 (file)
index 0000000..2910bc5
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_CRC_H
+#define _LVM_CRC_H
+
+#define INITIAL_CRC 0xf597a6cf
+
+uint32_t calc_crc(uint32_t initial, const uint8_t *buf, uint32_t size);
+
+#endif
diff --git a/lib/misc/crc_gen.c b/lib/misc/crc_gen.c
new file mode 100644 (file)
index 0000000..5e83317
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Helper program to generate table included in crc.c.
+ */
+#include "lib.h"
+
+int main(int argc, char **argv)
+{
+       uint32_t crc, i, j;
+
+       printf("\t/* CRC-32 byte lookup table generated by crc_gen.c */\n");
+       printf("\tstatic const uint32_t crctab[] = {");
+
+       for (i = 0; i < 256; i++) {
+               crc = i;
+               for (j = 0; j < 8; j++) {
+                       if (crc & 1)
+                               crc = 0xedb88320L ^ (crc >> 1);
+                       else
+                               crc = crc >> 1;
+               }
+
+               if (i % 8)
+                       printf(" ");
+               else
+                       printf("\n\t\t");
+
+               printf("0x%08.8x,", crc);
+       }
+
+       printf("\n\t};\n");
+
+       return 0;
+}
diff --git a/lib/misc/intl.h b/lib/misc/intl.h
new file mode 100644 (file)
index 0000000..fe08021
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_INTL_H
+#define _LVM_INTL_H
+
+#ifdef INTL_PACKAGE
+#  include <libintl.h>
+#  define _(String) dgettext(INTL_PACKAGE, (String))
+#else
+#  define _(String) (String)
+#endif
+
+#endif
diff --git a/lib/misc/last-path-component.h b/lib/misc/last-path-component.h
new file mode 100644 (file)
index 0000000..e0d5940
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Return the address of the last file name component of NAME.
+ * If NAME ends in a slash, return the empty string.
+ */
+
+#include <string.h>
+
+static inline const char *last_path_component(char const *name)
+{
+       char const *slash = strrchr(name, '/');
+
+       return (slash) ? slash + 1 : name;
+}
diff --git a/lib/misc/lib.h b/lib/misc/lib.h
new file mode 100644 (file)
index 0000000..100827d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This file must be included first by every library source file.
+ */
+#ifndef _LVM_LIB_H
+#define _LVM_LIB_H
+
+#include "configure.h"
+
+#define _REENTRANT
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "intl.h"
+#include "libdevmapper.h"
+#include "lvm-globals.h"
+#include "lvm-wrappers.h"
+#include "lvm-types.h"
+#include "util.h"
+
+#ifdef DM
+#  include "dm-logging.h"
+#else
+#  include "lvm-logging.h"
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#endif
diff --git a/lib/misc/lvm-exec.c b/lib/misc/lvm-exec.c
new file mode 100644 (file)
index 0000000..5cad79e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "locking.h"
+#include "lvm-exec.h"
+#include "toolcontext.h"
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+/*
+ * Create verbose string with list of parameters
+ */
+static char *_verbose_args(const char *const argv[], char *buf, size_t sz)
+{
+       int pos = 0;
+       int len;
+       unsigned i;
+
+       buf[0] = '\0';
+       for (i = 0; argv[i]; i++) {
+               if ((len = dm_snprintf(buf + pos, sz - pos,
+                                      "%s ", argv[i])) < 0)
+                       /* Truncated */
+                       break;
+               pos += len;
+       }
+
+       return buf;
+}
+
+/*
+ * Execute and wait for external command
+ */
+int exec_cmd(struct cmd_context *cmd, const char *const argv[], int *rstatus)
+{
+       pid_t pid;
+       int status;
+       char buf[PATH_MAX * 2];
+
+       log_verbose("Executing: %s", _verbose_args(argv, buf, sizeof(buf)));
+
+       if ((pid = fork()) == -1) {
+               log_error("fork failed: %s", strerror(errno));
+               return 0;
+       }
+
+       if (!pid) {
+               /* Child */
+               reset_locking();
+               dev_close_all();
+               /* FIXME Fix effect of reset_locking on cache then include this */
+               /* destroy_toolcontext(cmd); */
+               /* FIXME Use execve directly */
+               execvp(argv[0], (char **const) argv);
+               log_sys_error("execvp", argv[0]);
+               _exit(errno);
+       }
+
+       if (rstatus)
+               *rstatus = -1;
+
+       /* Parent */
+       if (wait4(pid, &status, 0, NULL) != pid) {
+               log_error("wait4 child process %u failed: %s", pid,
+                         strerror(errno));
+               return 0;
+       }
+
+       if (!WIFEXITED(status)) {
+               log_error("Child %u exited abnormally", pid);
+               return 0;
+       }
+
+       if (WEXITSTATUS(status)) {
+               if (rstatus) {
+                       *rstatus = WEXITSTATUS(status);
+                       log_verbose("%s failed: %u", argv[0], *rstatus);
+               } else
+                       log_error("%s failed: %u", argv[0], WEXITSTATUS(status));
+               return 0;
+       }
+
+       if (rstatus)
+               *rstatus = 0;
+
+       return 1;
+}
diff --git a/lib/misc/lvm-exec.h b/lib/misc/lvm-exec.h
new file mode 100644 (file)
index 0000000..6d984f8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_EXEC_H
+#define _LVM_EXEC_H
+
+#include "lib.h"
+
+struct cmd_context;
+int exec_cmd(struct cmd_context *cmd, const char *const argv[], int *rstatus);
+
+#endif
diff --git a/lib/misc/lvm-file.c b/lib/misc/lvm-file.c
new file mode 100644 (file)
index 0000000..f577840
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+/*
+ * Creates a temporary filename, and opens a descriptor to the
+ * file.  Both the filename and descriptor are needed so we can
+ * rename the file after successfully writing it.  Grab
+ * NFS-supported exclusive fcntl discretionary lock.
+ */
+int create_temp_name(const char *dir, char *buffer, size_t len, int *fd,
+                    unsigned *seed)
+{
+       int i, num;
+       pid_t pid;
+       char hostname[255];
+       struct flock lock = {
+               .l_type = F_WRLCK,
+               .l_whence = 0,
+               .l_start = 0,
+               .l_len = 0
+       };
+
+       num = rand_r(seed);
+       pid = getpid();
+       if (gethostname(hostname, sizeof(hostname)) < 0) {
+               log_sys_error("gethostname", "");
+               strcpy(hostname, "nohostname");
+       }
+
+       for (i = 0; i < 20; i++, num++) {
+
+               if (dm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d",
+                                dir, hostname, pid, num) == -1) {
+                       log_error("Not enough space to build temporary file "
+                                 "string.");
+                       return 0;
+               }
+
+               *fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
+                          S_IRUSR | S_IRGRP | S_IROTH |
+                          S_IWUSR | S_IWGRP | S_IWOTH);
+               if (*fd < 0)
+                       continue;
+
+               if (!fcntl(*fd, F_SETLK, &lock))
+                       return 1;
+
+               if (close(*fd))
+                       log_sys_error("close", buffer);
+       }
+
+       return 0;
+}
+
+/*
+ * NFS-safe rename of a temporary file to a common name, designed
+ * to avoid race conditions and not overwrite the destination if
+ * it exists.
+ *
+ * Try to create the new filename as a hard link to the original.
+ * Check the link count of the original file to see if it worked.
+ * (Assumes nothing else touches our temporary file!)  If it
+ * worked, unlink the old filename.
+ */
+int lvm_rename(const char *old, const char *new)
+{
+       struct stat buf;
+
+       if (link(old, new)) {
+               log_error("%s: rename to %s failed: %s", old, new,
+                         strerror(errno));
+               return 0;
+       }
+
+       if (stat(old, &buf)) {
+               log_sys_error("stat", old);
+               return 0;
+       }
+
+       if (buf.st_nlink != 2) {
+               log_error("%s: rename to %s failed", old, new);
+               return 0;
+       }
+
+       if (unlink(old)) {
+               log_sys_error("unlink", old);
+               return 0;
+       }
+
+       return 1;
+}
+
+int path_exists(const char *path)
+{
+       struct stat info;
+
+       if (!*path)
+               return 0;
+
+       if (stat(path, &info) < 0)
+               return 0;
+
+       return 1;
+}
+
+int dir_exists(const char *path)
+{
+       struct stat info;
+
+       if (!*path)
+               return 0;
+
+       if (stat(path, &info) < 0)
+               return 0;
+
+       if (!S_ISDIR(info.st_mode))
+               return 0;
+
+       return 1;
+}
+
+int is_empty_dir(const char *dir)
+{
+       struct dirent *dirent;
+       DIR *d;
+
+       if (!(d = opendir(dir))) {
+               log_sys_error("opendir", dir);
+               return 0;
+       }
+
+       while ((dirent = readdir(d)))
+               if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, ".."))
+                       break;
+
+       if (closedir(d)) {
+               log_sys_error("closedir", dir);
+       }
+
+       return dirent ? 0 : 1;
+}
+
+void sync_dir(const char *file)
+{
+       int fd;
+       char *dir, *c;
+
+       if (!(dir = dm_strdup(file))) {
+               log_error("sync_dir failed in strdup");
+               return;
+       }
+
+       if (!dir_exists(dir)) {
+               c = dir + strlen(dir);
+               while (*c != '/' && c > dir)
+                       c--;
+
+               if (c == dir)
+                       *c++ = '.';
+
+               *c = '\0';
+       }
+
+       if ((fd = open(dir, O_RDONLY)) == -1) {
+               log_sys_error("open", dir);
+               goto out;
+       }
+
+       if (fsync(fd) && (errno != EROFS) && (errno != EINVAL))
+               log_sys_error("fsync", dir);
+
+       if (close(fd))
+               log_sys_error("close", dir);
+
+      out:
+       dm_free(dir);
+}
+
+/*
+ * Attempt to obtain fcntl lock on a file, if necessary creating file first
+ * or waiting.
+ * Returns file descriptor on success, else -1.
+ * mode is F_WRLCK or F_RDLCK
+ */
+int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only)
+{
+       int lockfd;
+       char *dir;
+       char *c;
+       struct flock lock = {
+               .l_type = lock_type,
+               .l_whence = 0,
+               .l_start = 0,
+               .l_len = 0
+       };
+
+       if (!(dir = dm_strdup(file))) {
+               log_error("fcntl_lock_file failed in strdup.");
+               return -1;
+       }
+
+       if ((c = strrchr(dir, '/')))
+               *c = '\0';
+
+       if (!dm_create_dir(dir)) {
+               dm_free(dir);
+               return -1;
+       }
+
+       dm_free(dir);
+
+       log_very_verbose("Locking %s (%s, %hd)", file,
+                        (lock_type == F_WRLCK) ? "F_WRLCK" : "F_RDLCK",
+                        lock_type);
+       if ((lockfd = open(file, O_RDWR | O_CREAT, 0777)) < 0) {
+               /* EACCES has been reported on NFS */
+               if (warn_if_read_only || (errno != EROFS && errno != EACCES))
+                       log_sys_error("open", file);
+               else
+                       stack;
+
+               return -1;
+       }
+
+       if (fcntl(lockfd, F_SETLKW, &lock)) {
+               log_sys_error("fcntl", file);
+               close(lockfd);
+               return -1;
+       }
+
+       return lockfd;
+}
+
+void fcntl_unlock_file(int lockfd)
+{
+       struct flock lock = {
+               .l_type = F_UNLCK,
+               .l_whence = 0,
+               .l_start = 0,
+               .l_len = 0
+       };
+
+       log_very_verbose("Unlocking fd %d", lockfd);
+
+       if (fcntl(lockfd, F_SETLK, &lock) == -1)
+               log_error("fcntl unlock failed on fd %d: %s", lockfd,
+                         strerror(errno));
+
+       if (close(lockfd))
+               log_error("lock file close failed on fd %d: %s", lockfd,
+                         strerror(errno));
+}
+
+int lvm_fclose(FILE *fp, const char *filename)
+{
+       if (!dm_fclose(fp))
+               return 0;
+       if (errno == 0)
+               log_error("%s: write error", filename);
+       else
+               log_sys_error("write error", filename);
+       return EOF;
+}
diff --git a/lib/misc/lvm-file.h b/lib/misc/lvm-file.h
new file mode 100644 (file)
index 0000000..07327c3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_FILE_H
+#define _LVM_FILE_H
+
+/*
+ * Create a temporary filename, and opens a descriptor to the file.
+ */
+int create_temp_name(const char *dir, char *buffer, size_t len, int *fd,
+                    unsigned *seed);
+
+/*
+ * NFS-safe rename of a temporary file to a common name, designed
+ * to avoid race conditions and not overwrite the destination if
+ * it exists.
+ */
+int lvm_rename(const char *old, const char *new);
+
+/*
+ * Return 1 if path exists else return 0
+ */
+int path_exists(const char *path);
+int dir_exists(const char *path);
+
+/*
+ * Return 1 if dir is empty
+ */
+int is_empty_dir(const char *dir);
+
+/* Sync directory changes */
+void sync_dir(const char *file);
+
+/* fcntl locking wrappers */
+int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only);
+void fcntl_unlock_file(int lockfd);
+
+#define is_same_inode(buf1, buf2) \
+  ((buf1).st_ino == (buf2).st_ino && \
+   (buf1).st_dev == (buf2).st_dev)
+
+/*
+ * Close the specified stream, taking care to detect and diagnose any write
+ * error.  If there is an error, use the supplied file name in a diagnostic
+ * that is reported via log_error or log_sys_error, as appropriate.
+ * Use this function to close a stream when you've written data to it via
+ * unchecked fprintf, fputc, etc. calls.  Return 0 on success, EOF on failure.
+ */
+int lvm_fclose(FILE *fp, const char *filename);
+
+#endif
diff --git a/lib/misc/lvm-globals.c b/lib/misc/lvm-globals.c
new file mode 100644 (file)
index 0000000..9da61fe
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "memlock.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "defaults.h"
+
+#include <stdarg.h>
+
+static int _verbose_level = VERBOSE_BASE_LEVEL;
+static int _test = 0;
+static int _md_filtering = 0;
+static int _pvmove = 0;
+static int _full_scan_done = 0;        /* Restrict to one full scan during each cmd */
+static int _trust_cache = 0; /* Don't scan when incomplete VGs encountered */
+static int _debug_level = 0;
+static int _log_cmd_name = 0;
+static int _ignorelockingfailure = 0;
+static int _security_level = SECURITY_LEVEL;
+static char _cmd_name[30] = "";
+static int _mirror_in_sync = 0;
+static int _dmeventd_monitor = DEFAULT_DMEVENTD_MONITOR;
+static int _background_polling = DEFAULT_BACKGROUND_POLLING;
+static int _ignore_suspended_devices = 0;
+static int _error_message_produced = 0;
+static unsigned _is_static = 0;
+static int _udev_checking = 1;
+static char _sysfs_dir_path[PATH_MAX] = "";
+static int _dev_disable_after_error_count = DEFAULT_DISABLE_AFTER_ERROR_COUNT;
+
+void init_verbose(int level)
+{
+       _verbose_level = level;
+}
+
+void init_test(int level)
+{
+       if (!_test && level)
+               log_print("Test mode: Metadata will NOT be updated.");
+       _test = level;
+}
+
+void init_md_filtering(int level)
+{
+       _md_filtering = level;
+}
+
+void init_pvmove(int level)
+{
+       _pvmove = level;
+}
+
+void init_full_scan_done(int level)
+{
+       _full_scan_done = level;
+}
+
+void init_trust_cache(int trustcache)
+{
+       _trust_cache = trustcache;
+}
+
+void init_ignorelockingfailure(int level)
+{
+       _ignorelockingfailure = level;
+}
+
+void init_security_level(int level)
+{
+       _security_level = level;
+}
+
+void init_mirror_in_sync(int in_sync)
+{
+       _mirror_in_sync = in_sync;
+}
+
+void init_dmeventd_monitor(int reg)
+{
+       _dmeventd_monitor = reg;
+}
+
+void init_background_polling(int polling)
+{
+       _background_polling = polling;
+}
+
+void init_ignore_suspended_devices(int ignore)
+{
+       _ignore_suspended_devices = ignore;
+}
+
+void init_cmd_name(int status)
+{
+       _log_cmd_name = status;
+}
+
+void init_is_static(unsigned value)
+{
+       _is_static = value;
+}
+
+void init_udev_checking(int checking)
+{
+       if ((_udev_checking = checking))
+               log_debug("LVM udev checking enabled");
+       else
+               log_debug("LVM udev checking disabled");
+}
+
+void init_dev_disable_after_error_count(int value)
+{
+       _dev_disable_after_error_count = value;
+}
+
+void set_cmd_name(const char *cmd)
+{
+       strncpy(_cmd_name, cmd, sizeof(_cmd_name));
+       _cmd_name[sizeof(_cmd_name) - 1] = '\0';
+}
+
+void set_sysfs_dir_path(const char *path)
+{
+       strncpy(_sysfs_dir_path, path, sizeof(_sysfs_dir_path));
+       _sysfs_dir_path[sizeof(_sysfs_dir_path) - 1] = '\0';
+}
+
+const char *log_command_name()
+{
+       if (!_log_cmd_name)
+               return "";
+
+       return _cmd_name;
+}
+
+void init_error_message_produced(int value)
+{
+       _error_message_produced = value;
+}
+
+int error_message_produced(void)
+{
+       return _error_message_produced;
+}
+
+int test_mode()
+{
+       return _test;
+}
+
+int md_filtering()
+{
+       return _md_filtering;
+}
+
+int pvmove_mode()
+{
+       return _pvmove;
+}
+
+int full_scan_done()
+{
+       return _full_scan_done;
+}
+
+int trust_cache()
+{
+       return _trust_cache;
+}
+
+int background_polling()
+{
+       return _background_polling;
+}
+
+int ignorelockingfailure()
+{
+       return _ignorelockingfailure;
+}
+
+int security_level()
+{
+       return _security_level;
+}
+
+int mirror_in_sync(void)
+{
+       return _mirror_in_sync;
+}
+
+int dmeventd_monitor_mode(void)
+{
+       return _dmeventd_monitor;
+}
+
+int ignore_suspended_devices(void)
+{
+       return _ignore_suspended_devices;
+}
+
+void init_debug(int level)
+{
+       _debug_level = level;
+}
+
+int verbose_level()
+{
+       return _verbose_level;
+}
+
+int debug_level()
+{
+       return _debug_level;
+}
+
+unsigned is_static(void)
+{
+       return _is_static;
+}
+
+int udev_checking(void)
+{
+       return _udev_checking;
+}
+
+const char *sysfs_dir_path()
+{
+       return _sysfs_dir_path;
+}
+
+int dev_disable_after_error_count(void)
+{
+       return _dev_disable_after_error_count;
+}
diff --git a/lib/misc/lvm-globals.h b/lib/misc/lvm-globals.h
new file mode 100644 (file)
index 0000000..2fabbc7
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_GLOBALS_H
+#define _LVM_GLOBALS_H
+
+#define VERBOSE_BASE_LEVEL _LOG_WARN
+#define SECURITY_LEVEL 0
+
+void init_verbose(int level);
+void init_test(int level);
+void init_md_filtering(int level);
+void init_pvmove(int level);
+void init_full_scan_done(int level);
+void init_trust_cache(int trustcache);
+void init_debug(int level);
+void init_cmd_name(int status);
+void init_ignorelockingfailure(int level);
+void init_lockingfailed(int level);
+void init_security_level(int level);
+void init_mirror_in_sync(int in_sync);
+void init_dmeventd_monitor(int reg);
+void init_background_polling(int polling);
+void init_ignore_suspended_devices(int ignore);
+void init_error_message_produced(int produced);
+void init_is_static(unsigned value);
+void init_udev_checking(int checking);
+void init_dev_disable_after_error_count(int value);
+
+void set_cmd_name(const char *cmd_name);
+void set_sysfs_dir_path(const char *path);
+
+int test_mode(void);
+int md_filtering(void);
+int pvmove_mode(void);
+int full_scan_done(void);
+int trust_cache(void);
+int verbose_level(void);
+int debug_level(void);
+int ignorelockingfailure(void);
+int lockingfailed(void);
+int security_level(void);
+int mirror_in_sync(void);
+int background_polling(void);
+int ignore_suspended_devices(void);
+const char *log_command_name(void);
+unsigned is_static(void);
+int udev_checking(void);
+const char *sysfs_dir_path(void);
+
+#define DMEVENTD_MONITOR_IGNORE -1
+int dmeventd_monitor_mode(void);
+
+#define NO_DEV_ERROR_COUNT_LIMIT 0
+int dev_disable_after_error_count(void);
+
+#endif
diff --git a/lib/misc/lvm-percent.c b/lib/misc/lvm-percent.c
new file mode 100644 (file)
index 0000000..4b73db4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lvm-percent.h"
+
+float percent_to_float(percent_t v)
+{
+    return (float)v / PERCENT_1;
+}
+
+percent_t make_percent(uint64_t numerator, uint64_t denominator)
+{
+    percent_t percent;
+    if (!denominator)
+        return PERCENT_100; /* FIXME? */
+    if (!numerator)
+        return PERCENT_0;
+    if (numerator == denominator)
+        return PERCENT_100;
+    switch (percent = PERCENT_100 * ((double) numerator / (double) denominator)) {
+    case PERCENT_100:
+        return PERCENT_100 - 1;
+    case PERCENT_0:
+        return PERCENT_0 + 1;
+    default:
+        return percent;
+    }
+}
+
diff --git a/lib/misc/lvm-percent.h b/lib/misc/lvm-percent.h
new file mode 100644 (file)
index 0000000..a925190
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_PERCENT_H
+#define _LVM_PERCENT_H
+#include <stdint.h>
+
+/*
+ * A fixed-point representation of percent values. One percent equals to
+ * PERCENT_1 as defined below. Values that are not multiples of PERCENT_1
+ * represent fractions, with precision of 1/1000000 of a percent. See
+ * percent_to_float for a conversion to a floating-point representation.
+ *
+ * You should always use make_percent when building percent_t values. The
+ * implementation of make_percent is biased towards the middle: it ensures that
+ * the result is PERCENT_0 or PERCENT_100 if and only if this is the actual
+ * value -- it never rounds any intermediate value (> 0 or < 100) to either 0
+ * or 100.
+ */
+typedef int32_t percent_t;
+
+typedef enum {
+       PERCENT_0 = 0,
+       PERCENT_1 = 1000000,
+       PERCENT_100 = 100 * PERCENT_1,
+       PERCENT_INVALID = -1
+} percent_range_t;
+
+float percent_to_float(percent_t v);
+percent_t make_percent(uint64_t numerator, uint64_t denominator);
+
+#endif
diff --git a/lib/misc/lvm-string.c b/lib/misc/lvm-string.c
new file mode 100644 (file)
index 0000000..8fd2c04
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "lvm-string.h"
+
+#include <ctype.h>
+
+int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...)
+{
+       int n;
+       va_list ap;
+
+       va_start(ap, fmt);
+       n = vsnprintf(*buffer, *size, fmt, ap);
+       va_end(ap);
+
+       /*
+        * Revert to old glibc behaviour (version <= 2.0.6) where snprintf
+        * returned -1 if buffer was too small. From glibc 2.1 it returns number
+        * of chars that would have been written had there been room.
+        */
+       if (n < 0 || ((unsigned) n + 1 > *size))
+               n = -1;
+
+       if (n < 0 || ((size_t)n == *size))
+               return 0;
+
+       *buffer += n;
+       *size -= n;
+       return 1;
+}
+
+/*
+ * Count occurences of 'c' in 'str' until we reach a null char.
+ *
+ * Returns:
+ *  len - incremented for each char we encounter.
+ *  count - number of occurrences of 'c' and 'c2'.
+ */
+static void _count_chars(const char *str, size_t *len, int *count,
+                        const int c1, const int c2)
+{
+       const char *ptr;
+
+       for (ptr = str; *ptr; ptr++, (*len)++)
+               if (*ptr == c1 || *ptr == c2)
+                       (*count)++;
+}
+
+/*
+ * Count occurences of 'c' in 'str' of length 'size'.
+ *
+ * Returns:
+ *   Number of occurrences of 'c'
+ */
+unsigned count_chars(const char *str, size_t len, const int c)
+{
+       size_t i;
+       unsigned count = 0;
+
+       for (i = 0; i < len; i++)
+               if (str[i] == c)
+                       count++;
+
+       return count;
+}
+
+/*
+ * Length of string after escaping double quotes and backslashes.
+ */
+size_t escaped_len(const char *str)
+{
+       size_t len = 1;
+       int count = 0;
+
+       _count_chars(str, &len, &count, '\"', '\\');
+
+       return count + len;
+}
+
+/*
+ * Copies a string, quoting orig_char with quote_char.
+ * Optionally also quote quote_char.
+ */
+static void _quote_characters(char **out, const char *src,
+                             const int orig_char, const int quote_char,
+                             int quote_quote_char)
+{
+       while (*src) {
+               if (*src == orig_char ||
+                   (*src == quote_char && quote_quote_char))
+                       *(*out)++ = quote_char;
+
+               *(*out)++ = *src++;
+       }
+}
+
+static void _unquote_one_character(char *src, const char orig_char,
+                                  const char quote_char)
+{
+       char *out;
+       char s, n;
+
+       /* Optimise for the common case where no changes are needed. */
+       while ((s = *src++)) {
+               if (s == quote_char &&
+                   ((n = *src) == orig_char || n == quote_char)) {
+                       out = src++;
+                       *(out - 1) = n;
+
+                       while ((s = *src++)) {
+                               if (s == quote_char &&
+                                   ((n = *src) == orig_char || n == quote_char)) {
+                                       s = n;
+                                       src++;
+                               }
+                               *out = s;
+                               out++;
+                       }
+
+                       *out = '\0';
+                       return;
+               }
+       }
+}
+
+/*
+ * Unquote each character given in orig_char array and unquote quote_char
+ * as well. Also save the first occurrence of each character from orig_char
+ * that was found unquoted in arr_substr_first_unquoted array. This way we can
+ * process several characters in one go.
+ */
+static void _unquote_characters(char *src, const char *orig_chars,
+                               const int num_orig_chars, 
+                               const char quote_char,
+                               char *arr_substr_first_unquoted[])
+{
+       char *out = src;
+       char c, s, n;
+       unsigned i;
+
+       while ((s = *src++)) {
+               for (i = 0; i < num_orig_chars; i++) {
+                       c = orig_chars[i];
+                       if (s == quote_char &&
+                           ((n = *src) == c || n == quote_char)) {
+                               s = n;
+                               src++;
+                               break;
+                       }
+                       if (arr_substr_first_unquoted && (s == c) &&
+                           !arr_substr_first_unquoted[i])
+                               arr_substr_first_unquoted[i] = out;
+               };
+               *out++ = s;
+       }
+
+       *out = '\0';
+}
+
+/*
+ * Copies a string, quoting hyphens with hyphens.
+ */
+static void _quote_hyphens(char **out, const char *src)
+{
+       _quote_characters(out, src, '-', '-', 0);
+}
+
+/*
+ * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
+ */
+char *build_dm_name(struct dm_pool *mem, const char *vgname,
+                   const char *lvname, const char *layer)
+{
+       size_t len = 1;
+       int hyphens = 1;
+       char *r, *out;
+
+       _count_chars(vgname, &len, &hyphens, '-', 0);
+       _count_chars(lvname, &len, &hyphens, '-', 0);
+
+       if (layer && *layer) {
+               _count_chars(layer, &len, &hyphens, '-', 0);
+               hyphens++;
+       }
+
+       len += hyphens;
+
+       if (!(r = dm_pool_alloc(mem, len))) {
+               log_error("build_dm_name: Allocation failed for %" PRIsize_t
+                         " for %s %s %s.", len, vgname, lvname, layer);
+               return NULL;
+       }
+
+       out = r;
+       _quote_hyphens(&out, vgname);
+       *out++ = '-';
+       _quote_hyphens(&out, lvname);
+
+       if (layer && *layer) {
+               /* No hyphen if the layer begins with _ e.g. _mlog */
+               if (*layer != '_')
+                       *out++ = '-';
+               _quote_hyphens(&out, layer);
+       }
+       *out = '\0';
+
+       return r;
+}
+
+char *build_dm_uuid(struct dm_pool *mem, const char *lvid, const char *layer)
+{
+       char *dmuuid;
+       size_t len;
+
+       if (!layer)
+               layer = "";
+
+       len = sizeof(UUID_PREFIX) + strlen(lvid) + strlen(layer) + 1;
+
+       if (!(dmuuid = dm_pool_alloc(mem, len))) {
+               log_error("build_dm_name: Allocation failed for %" PRIsize_t
+                         " %s %s.", len, lvid, layer);
+               return NULL;
+       }
+
+       sprintf(dmuuid, UUID_PREFIX "%s%s%s", lvid, (*layer) ? "-" : "", layer);
+
+       return dmuuid;
+}
+
+/*
+ * Copies a string, quoting double quotes with backslashes.
+ */
+char *escape_double_quotes(char *out, const char *src)
+{
+       char *buf = out;
+
+       _quote_characters(&buf, src, '\"', '\\', 1);
+       *buf = '\0';
+
+       return out;
+}
+
+/*
+ * Undo quoting in situ.
+ */
+void unescape_double_quotes(char *src)
+{
+       _unquote_one_character(src, '\"', '\\');
+}
+
+/*
+ * Unescape colons and "at" signs in situ and save the substrings
+ * starting at the position of the first unescaped colon and the
+ * first unescaped "at" sign. This is normally used to unescape
+ * device names used as PVs.
+ */
+void unescape_colons_and_at_signs(char *src,
+                                 char **substr_first_unquoted_colon,
+                                 char **substr_first_unquoted_at_sign)
+{
+       const char *orig_chars = ":@";
+       char *arr_substr_first_unquoted[] = {NULL, NULL, NULL};
+
+       _unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted);
+
+       if (substr_first_unquoted_colon)
+               *substr_first_unquoted_colon = arr_substr_first_unquoted[0];
+
+       if (substr_first_unquoted_at_sign)
+               *substr_first_unquoted_at_sign = arr_substr_first_unquoted[1];
+}
+
+/*
+ * A-Za-z0-9._-+/=!:&#
+ */
+int validate_tag(const char *n)
+{
+       register char c;
+       register int len = 0;
+
+       if (!n || !*n)
+               return 0;
+
+       while ((len++, c = *n++))
+               if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+' && c != '/'
+                   && c != '=' && c != '!' && c != ':' && c != '&' && c != '#')
+                       return 0;
+
+       return 1;
+}
+
+/*
+ * Device layer names are all of the form <vg>-<lv>-<layer>, any
+ * other hyphens that appear in these names are quoted with yet
+ * another hyphen.  The top layer of any device has no layer
+ * name.  eg, vg0-lvol0.
+ */
+int validate_name(const char *n)
+{
+       register char c;
+       register int len = 0;
+
+       if (!n || !*n)
+               return 0;
+
+       /* Hyphen used as VG-LV separator - ambiguity if LV starts with it */
+       if (*n == '-')
+               return 0;
+
+       if (!strcmp(n, ".") || !strcmp(n, ".."))
+               return 0;
+
+       while ((len++, c = *n++))
+               if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+')
+                       return 0;
+
+       if (len > NAME_LEN)
+               return 0;
+
+       return 1;
+}
+
+int apply_lvname_restrictions(const char *name)
+{
+       if (!strncmp(name, "snapshot", 8)) {
+               log_error("Names starting \"snapshot\" are reserved. "
+                         "Please choose a different LV name.");
+               return 0;
+       }
+
+       if (!strncmp(name, "pvmove", 6)) {
+               log_error("Names starting \"pvmove\" are reserved. "
+                         "Please choose a different LV name.");
+               return 0;
+       }
+
+       if (strstr(name, "_mlog")) {
+               log_error("Names including \"_mlog\" are reserved. "
+                         "Please choose a different LV name.");
+               return 0;
+       }
+
+       if (strstr(name, "_mimage")) {
+               log_error("Names including \"_mimage\" are reserved. "
+                         "Please choose a different LV name.");
+               return 0;
+       }
+
+       if (strstr(name, "_vorigin")) {
+               log_error("Names including \"_vorigin\" are reserved. "
+                         "Please choose a different LV name.");
+               return 0;
+       }
+
+       return 1;
+}
+
+int is_reserved_lvname(const char *name)
+{
+       int rc, old_suppress;
+
+       old_suppress = log_suppress(2);
+       rc = !apply_lvname_restrictions(name);
+       log_suppress(old_suppress);
+
+       return rc;
+}
diff --git a/lib/misc/lvm-string.h b/lib/misc/lvm-string.h
new file mode 100644 (file)
index 0000000..20f45c9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_STRING_H
+#define _LVM_STRING_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#define NAME_LEN 128
+#define UUID_PREFIX "LVM-"
+
+struct pool;
+
+int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...)
+  __attribute__ ((format(printf, 3, 4)));
+
+char *build_dm_name(struct dm_pool *mem, const char *vg,
+                    const char *lv, const char *layer);
+char *build_dm_uuid(struct dm_pool *mem, const char *lvid,
+                   const char *layer);
+
+int validate_name(const char *n);
+int validate_tag(const char *n);
+
+int apply_lvname_restrictions(const char *name);
+int is_reserved_lvname(const char *name);
+
+/*
+ * Returns number of occurrences of c in first len characters of str.
+ */
+unsigned count_chars(const char *str, size_t len, const int c);
+
+/*
+ * Returns what length of escaped string would be including terminating NUL.
+ */
+size_t escaped_len(const char *str);
+
+/*
+ * Copies a string from src to out. 
+ * Double quotation marks and backslashes are quoted with a backslash.
+ * Caller must ensure *out has enough space - see escaped_len().
+ * Returns *out.
+ */
+char *escape_double_quotes(char *out, const char *src);
+
+/*
+ * Removes quoting of double quotation marks and backslashes in situ.
+ */
+void unescape_double_quotes(char *src);
+
+/*
+ * Unescape colons and at signs in situ and save the substring starting
+ * at the position of the first unescaped colon and the first unescaped
+ * "at" sign.
+ */
+void unescape_colons_and_at_signs(char *src,
+                                 char **substr_first_unquoted_colon,
+                                 char **substr_first_unquoted_at_sign);
+
+#endif
diff --git a/lib/misc/lvm-version.h.in b/lib/misc/lvm-version.h.in
new file mode 100644 (file)
index 0000000..2574890
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_VERSION_H
+/**
+ * The LVM version number
+ *
+ * LVM_MAJOR.LVM_MINOR.LVM_PATCHLEVEL(LVM_LIBAPI)[-LVM_RELEASE]
+ */
+
+#define LVM_VERSION @LVM_VERSION@
+#define LVM_MAJOR @LVM_MAJOR@
+#define LVM_MINOR @LVM_MINOR@
+#define LVM_PATCHLEVEL @LVM_PATCHLEVEL@
+#define LVM_LIBAPI @LVM_LIBAPI@
+#define LVM_RELEASE @LVM_RELEASE@
+#define LVM_RELEASE_DATE @LVM_RELEASE_DATE@
+#endif
diff --git a/lib/misc/lvm-wrappers.c b/lib/misc/lvm-wrappers.c
new file mode 100644 (file)
index 0000000..3ab8a71
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+int lvm_getpagesize(void)
+{
+       return getpagesize();
+}
+
+int read_urandom(void *buf, size_t len)
+{
+       int fd;
+
+       /* FIXME: we should stat here, and handle other cases */
+       /* FIXME: use common _io() routine's open/read/close */
+       if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
+               log_sys_error("open", "read_urandom: /dev/urandom");
+               return 0;
+       }
+
+       if (read(fd, buf, len) != (ssize_t) len) {
+               log_sys_error("read", "read_urandom: /dev/urandom");
+               if (close(fd))
+                       stack;
+               return 0;
+       }
+
+       if (close(fd))
+               stack;
+
+       return 1;
+}
+
diff --git a/lib/misc/lvm-wrappers.h b/lib/misc/lvm-wrappers.h
new file mode 100644 (file)
index 0000000..19a7f03
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_WRAPPERS_H
+#define _LVM_WRAPPERS_H
+
+int lvm_getpagesize(void);
+
+/*
+ * Read 'len' bytes of entropy from /dev/urandom and store in 'buf'.
+ */
+int read_urandom(void *buf, size_t len);
+
+#  ifndef HAVE_SIGINTERRUPT
+#    define siginterrupt(sig, flag) \
+       do { \
+               int ret; \
+               struct sigaction act; \
+               (void) sigaction(sig, NULL, &act); \
+               if (flag) \
+                       act.sa_flags &= SA_RESTART; \
+               else \
+                       act.sa_flags |= SA_RESTART; \
+               ret = sigaction(sig, &act, NULL); \
+               return ret; \
+       while (0)
+#  endif
+
+#endif
diff --git a/lib/misc/sharedlib.c b/lib/misc/sharedlib.c
new file mode 100644 (file)
index 0000000..cab2909
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "config.h"
+#include "lvm-string.h"
+#include "sharedlib.h"
+#include "toolcontext.h"
+
+#include <limits.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+
+void get_shared_library_path(struct cmd_context *cmd, const char *libname,
+                            char *path, size_t path_len)
+{
+       struct stat info;
+       const char *lib_dir;
+
+       /* If libname doesn't begin with '/' then use lib_dir/libname,
+        * if present */
+       if (libname[0] == '/' ||
+           !(lib_dir = find_config_tree_str(cmd, "global/library_dir", 0)) ||
+           (dm_snprintf(path, path_len, "%s/%s", lib_dir,
+                         libname) == -1) || stat(path, &info) == -1)
+               strncpy(path, libname, path_len);
+}
+
+void *load_shared_library(struct cmd_context *cmd, const char *libname,
+                         const char *desc, int silent)
+{
+       char path[PATH_MAX];
+       void *library;
+
+       if (is_static()) {
+               log_error("Not loading shared %s library %s in static mode.",
+                         desc, libname);
+               return NULL;
+       }
+
+       get_shared_library_path(cmd, libname, path, sizeof(path));
+
+       log_very_verbose("Opening shared %s library %s", desc, path);
+
+       if (!(library = dlopen(path, RTLD_LAZY | RTLD_GLOBAL))) {
+               if (silent && ignorelockingfailure())
+                       log_verbose("Unable to open external %s library %s: %s",
+                                   desc, path, dlerror());
+               else
+                       log_error("Unable to open external %s library %s: %s",
+                                 desc, path, dlerror());
+       }
+
+       return library;
+}
diff --git a/lib/misc/sharedlib.h b/lib/misc/sharedlib.h
new file mode 100644 (file)
index 0000000..d996a8b
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_SHAREDLIB_H
+#define _LVM_SHAREDLIB_H
+
+#include "config.h"
+#include <dlfcn.h>
+
+void get_shared_library_path(struct cmd_context *cmd, const char *libname,
+                            char *path, size_t path_len);
+void *load_shared_library(struct cmd_context *cmd, const char *libname,
+                         const char *what, int silent);
+
+#endif
diff --git a/lib/misc/timestamp.c b/lib/misc/timestamp.c
new file mode 100644 (file)
index 0000000..62e1be8
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2006 Rackable Systems All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Abstract out the time methods used so they can be adjusted later -
+ * the results of these routines should stay in-core.  This implementation
+ * requires librt.
+ */
+
+#include "lib.h"
+#include <stdlib.h>
+
+#include "timestamp.h"
+
+/*
+ * The realtime section uses clock_gettime with the CLOCK_MONOTONIC
+ * parameter to prevent issues with time warps
+ */
+#ifdef HAVE_REALTIME
+
+#include <time.h>
+#include <bits/time.h>
+
+struct timestamp {
+       struct timespec t;
+};
+
+struct timestamp *get_timestamp(void)
+{
+       struct timestamp *ts = NULL;
+
+       if (!(ts = dm_malloc(sizeof(*ts))))
+               return_NULL;
+
+       if (clock_gettime(CLOCK_MONOTONIC, &ts->t)) {
+               log_sys_error("clock_gettime", "get_timestamp");
+               return NULL;
+       }
+
+       return ts;
+}
+
+/* cmp_timestamp: Compare two timestamps
+ *
+ * Return: -1 if t1 is less than t2
+ *          0 if t1 is equal to t2
+ *          1 if t1 is greater than t2
+ */
+int cmp_timestamp(struct timestamp *t1, struct timestamp *t2)
+{
+       if(t1->t.tv_sec < t2->t.tv_sec)
+               return -1;
+       if(t1->t.tv_sec > t2->t.tv_sec)
+               return 1;
+
+       if(t1->t.tv_nsec < t2->t.tv_nsec)
+               return -1;
+       if(t1->t.tv_nsec > t2->t.tv_nsec)
+               return 1;
+
+       return 0;
+}
+
+#else /* ! HAVE_REALTIME */
+
+/*
+ * The !realtime section just uses gettimeofday and is therefore subject
+ * to ntp-type time warps - not sure if should allow that.
+ */
+
+#include <sys/time.h>
+
+struct timestamp {
+       struct timeval t;
+};
+
+struct timestamp *get_timestamp(void)
+{
+       struct timestamp *ts = NULL;
+
+       if (!(ts = dm_malloc(sizeof(*ts))))
+               return_NULL;
+
+       if (gettimeofday(&ts->t, NULL)) {
+               log_sys_error("gettimeofday", "get_timestamp");
+               return NULL;
+       }
+
+       return ts;
+}
+
+/* cmp_timestamp: Compare two timestamps
+ *
+ * Return: -1 if t1 is less than t2
+ *          0 if t1 is equal to t2
+ *          1 if t1 is greater than t2
+ */
+int cmp_timestamp(struct timestamp *t1, struct timestamp *t2)
+{
+       if(t1->t.tv_sec < t2->t.tv_sec)
+               return -1;
+       if(t1->t.tv_sec > t2->t.tv_sec)
+               return 1;
+
+       if(t1->t.tv_usec < t2->t.tv_usec)
+               return -1;
+       if(t1->t.tv_usec > t2->t.tv_usec)
+               return 1;
+
+       return 0;
+}
+
+#endif /* HAVE_REALTIME */
+
+void destroy_timestamp(struct timestamp *t)
+{
+       if (t)
+               dm_free(t);
+}
diff --git a/lib/misc/timestamp.h b/lib/misc/timestamp.h
new file mode 100644 (file)
index 0000000..50e2a85
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 Rackable Systems All rights reserved.  
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TIMESTAMP_H
+#define _LVM_TIMESTAMP_H
+
+struct timestamp;
+
+struct timestamp *get_timestamp(void);
+
+/* cmp_timestamp: Compare two timestamps
+ * 
+ * Return: -1 if t1 is less than t2
+ *         0 if t1 is equal to t2
+ *          1 if t1 is greater than t2
+ */
+int cmp_timestamp(struct timestamp *t1, struct timestamp *t2);
+
+void destroy_timestamp(struct timestamp *t);
+
+#endif /* _LVM_TIMESTAMP_H */
+
diff --git a/lib/misc/util.c b/lib/misc/util.c
new file mode 100644 (file)
index 0000000..75c7afa
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Return the address of the last file name component of NAME.
+ * If NAME ends in a slash, return the empty string.
+ */
+
+#include "lib.h"
+
+/* empty for now.  */
diff --git a/lib/misc/util.h b/lib/misc/util.h
new file mode 100644 (file)
index 0000000..0bcfd21
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_UTIL_H
+#define _LVM_UTIL_H
+
+#define min(a, b) ({ typeof(a) _a = (a); \
+                    typeof(b) _b = (b); \
+                    (void) (&_a == &_b); \
+                    _a < _b ? _a : _b; })
+
+#define max(a, b) ({ typeof(a) _a = (a); \
+                    typeof(b) _b = (b); \
+                    (void) (&_a == &_b); \
+                    _a > _b ? _a : _b; })
+
+#ifdef __clang__
+#define uninitialized_var(x) x
+#else
+#define uninitialized_var(x) x = x
+#endif
+
+#define KERNEL_VERSION(major, minor, release) (((major) << 16) + ((minor) << 8) + (release))
+
+#endif
diff --git a/lib/mm/memlock.c b/lib/mm/memlock.c
new file mode 100644 (file)
index 0000000..062b765
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "memlock.h"
+#include "defaults.h"
+#include "config.h"
+#include "toolcontext.h"
+
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifndef DEVMAPPER_SUPPORT
+
+void memlock_inc(struct cmd_context *cmd)
+{
+       return;
+}
+void memlock_dec(struct cmd_context *cmd)
+{
+       return;
+}
+int memlock(void)
+{
+       return 0;
+}
+void memlock_init(struct cmd_context *cmd)
+{
+       return;
+}
+
+#else                          /* DEVMAPPER_SUPPORT */
+
+static size_t _size_stack;
+static size_t _size_malloc_tmp;
+static size_t _size_malloc = 2000000;
+
+static void *_malloc_mem = NULL;
+static int _memlock_count = 0;
+static int _memlock_count_daemon = 0;
+static int _priority;
+static int _default_priority;
+
+/* list of maps, that are unconditionaly ignored */
+static const char * const _ignore_maps[] = {
+    "[vdso]",
+    "[vsyscall]",
+};
+
+/* default blacklist for maps */
+static const char * const _blacklist_maps[] = {
+    "locale/locale-archive",
+    "gconv/gconv-modules.cache",
+    "/libreadline.so.",        /* not using readline during mlock */
+    "/libncurses.so.", /* not using readline during mlock */
+    "/libdl-",         /* not using dlopen,dlsym during mlock */
+    /* "/libdevmapper-event.so" */
+};
+
+typedef enum { LVM_MLOCK, LVM_MUNLOCK } lvmlock_t;
+
+static unsigned _use_mlockall;
+static int _maps_fd;
+static size_t _maps_len = 8192; /* Initial buffer size for reading /proc/self/maps */
+static char *_maps_buffer;
+static char _procselfmaps[PATH_MAX] = "";
+#define SELF_MAPS "/self/maps"
+
+static size_t _mstats; /* statistic for maps locking */
+
+static void _touch_memory(void *mem, size_t size)
+{
+       size_t pagesize = lvm_getpagesize();
+       char *pos = mem;
+       char *end = pos + size - sizeof(long);
+
+       while (pos < end) {
+               *(long *) pos = 1;
+               pos += pagesize;
+       }
+}
+
+static void _allocate_memory(void)
+{
+       void *stack_mem, *temp_malloc_mem;
+
+       if ((stack_mem = alloca(_size_stack)))
+               _touch_memory(stack_mem, _size_stack);
+
+       if ((temp_malloc_mem = malloc(_size_malloc_tmp)))
+               _touch_memory(temp_malloc_mem, _size_malloc_tmp);
+
+       if ((_malloc_mem = malloc(_size_malloc)))
+               _touch_memory(_malloc_mem, _size_malloc);
+
+       free(temp_malloc_mem);
+}
+
+static void _release_memory(void)
+{
+       free(_malloc_mem);
+}
+
+/*
+ * mlock/munlock memory areas from /proc/self/maps
+ * format described in kernel/Documentation/filesystem/proc.txt
+ */
+static int _maps_line(const struct config_node *cn, lvmlock_t lock,
+                     const char* line, size_t* mstats)
+{
+       const struct config_value *cv;
+       long from, to;
+       int pos, i;
+       char fr, fw, fx, fp;
+       size_t sz;
+
+       if (sscanf(line, "%lx-%lx %c%c%c%c%n",
+                  &from, &to, &fr, &fw, &fx, &fp, &pos) != 6) {
+               log_error("Failed to parse maps line: %s", line);
+               return 0;
+       }
+
+       /* Select readable maps */
+       if (fr != 'r') {
+               log_debug("%s area unreadable %s : Skipping.",
+                         (lock == LVM_MLOCK) ? "mlock" : "munlock", line);
+               return 1;
+       }
+
+       /* always ignored areas */
+       for (i = 0; i < sizeof(_ignore_maps) / sizeof(_ignore_maps[0]); ++i)
+               if (strstr(line + pos, _ignore_maps[i])) {
+                       log_debug("mlock ignore filter '%s' matches '%s': Skipping.",
+                                 _ignore_maps[i], line);
+                       return 1;
+               }
+
+       sz = to - from;
+       if (!cn) {
+               /* If no blacklist configured, use an internal set */
+               for (i = 0; i < sizeof(_blacklist_maps) / sizeof(_blacklist_maps[0]); ++i)
+                       if (strstr(line + pos, _blacklist_maps[i])) {
+                               log_debug("mlock default filter '%s' matches '%s': Skipping.",
+                                         _blacklist_maps[i], line);
+                               return 1;
+                       }
+       } else {
+               for (cv = cn->v; cv; cv = cv->next) {
+                       if ((cv->type != CFG_STRING) || !cv->v.str[0])
+                               continue;
+                       if (strstr(line + pos, cv->v.str)) {
+                               log_debug("mlock_filter '%s' matches '%s': Skipping.",
+                                         cv->v.str, line);
+                               return 1;
+                       }
+               }
+       }
+
+       *mstats += sz;
+       log_debug("%s %10ldKiB %12lx - %12lx %c%c%c%c%s",
+                 (lock == LVM_MLOCK) ? "mlock" : "munlock",
+                 ((long)sz + 1023) / 1024, from, to, fr, fw, fx, fp, line + pos);
+
+       if (lock == LVM_MLOCK) {
+               if (mlock((const void*)from, sz) < 0) {
+                       log_sys_error("mlock", line);
+                       return 0;
+               }
+       } else {
+               if (munlock((const void*)from, sz) < 0) {
+                       log_sys_error("munlock", line);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _memlock_maps(struct cmd_context *cmd, lvmlock_t lock, size_t *mstats)
+{
+       const struct config_node *cn;
+       char *line, *line_end;
+       size_t len;
+       ssize_t n;
+       int ret = 1;
+
+       if (_use_mlockall) {
+#ifdef MCL_CURRENT
+               if (lock == LVM_MLOCK) {
+                       if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+                               log_sys_error("mlockall", "");
+                               return 0;
+                       }
+               } else {
+                       if (munlockall()) {
+                               log_sys_error("munlockall", "");
+                               return 0;
+                       }
+               }
+               return 1;
+#else
+               return 0;
+#endif
+       }
+
+       /* Force libc.mo load */
+       if (lock == LVM_MLOCK)
+               (void)strerror(0);
+       /* Reset statistic counters */
+       *mstats = 0;
+
+       /* read mapping into a single memory chunk without reallocation
+        * in the middle of reading maps file */
+       for (len = 0;;) {
+               if (!_maps_buffer || len >= _maps_len) {
+                       if (_maps_buffer)
+                               _maps_len *= 2;
+                       if (!(_maps_buffer = dm_realloc(_maps_buffer, _maps_len))) {
+                               log_error("Allocation of maps buffer failed");
+                               return 0;
+                       }
+               }
+               lseek(_maps_fd, 0, SEEK_SET);
+               for (len = 0 ; len < _maps_len; len += n) {
+                       if (!(n = read(_maps_fd, _maps_buffer + len, _maps_len - len))) {
+                               _maps_buffer[len] = '\0';
+                               break; /* EOF */
+                       }
+                       if (n == -1)
+                               return_0;
+               }
+               if (len < _maps_len)  /* fits in buffer */
+                       break;
+       }
+
+       line = _maps_buffer;
+       cn = find_config_tree_node(cmd, "activation/mlock_filter");
+
+       while ((line_end = strchr(line, '\n'))) {
+               *line_end = '\0'; /* remove \n */
+               if (!_maps_line(cn, lock, line, mstats))
+                       ret = 0;
+               line = line_end + 1;
+       }
+
+       log_debug("%socked %ld bytes",
+                 (lock == LVM_MLOCK) ? "L" : "Unl", (long)*mstats);
+
+       return ret;
+}
+
+/* Stop memory getting swapped out */
+static void _lock_mem(struct cmd_context *cmd)
+{
+       _allocate_memory();
+
+       /*
+        * For daemon we need to use mlockall()
+        * so even future adition of thread which may not even use lvm lib
+        * will not block memory locked thread
+        * Note: assuming _memlock_count_daemon is updated before _memlock_count
+         */
+       _use_mlockall = _memlock_count_daemon ? 1 :
+               find_config_tree_bool(cmd, "activation/use_mlockall", DEFAULT_USE_MLOCKALL);
+
+       if (!_use_mlockall) {
+               if (!*_procselfmaps &&
+                   dm_snprintf(_procselfmaps, sizeof(_procselfmaps),
+                               "%s" SELF_MAPS, cmd->proc_dir) < 0) {
+                       log_error("proc_dir too long");
+                       return;
+               }
+
+               if (!(_maps_fd = open(_procselfmaps, O_RDONLY))) {
+                       log_sys_error("open", _procselfmaps);
+                       return;
+               }
+       }
+
+       log_very_verbose("Locking memory");
+       if (!_memlock_maps(cmd, LVM_MLOCK, &_mstats))
+               stack;
+
+       errno = 0;
+       if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno)
+               log_sys_error("getpriority", "");
+       else
+               if (setpriority(PRIO_PROCESS, 0, _default_priority))
+                       log_error("setpriority %d failed: %s",
+                                 _default_priority, strerror(errno));
+}
+
+static void _unlock_mem(struct cmd_context *cmd)
+{
+       size_t unlock_mstats;
+
+       log_very_verbose("Unlocking memory");
+
+       if (!_memlock_maps(cmd, LVM_MUNLOCK, &unlock_mstats))
+               stack;
+
+       if (!_use_mlockall) {
+               if (close(_maps_fd))
+                       log_sys_error("close", _procselfmaps);
+               dm_free(_maps_buffer);
+               _maps_buffer = NULL;
+               if (_mstats < unlock_mstats)
+                       log_error(INTERNAL_ERROR "Maps lock %ld < unlock %ld",
+                                 (long)_mstats, (long)unlock_mstats);
+       }
+
+       if (setpriority(PRIO_PROCESS, 0, _priority))
+               log_error("setpriority %u failed: %s", _priority,
+                         strerror(errno));
+       _release_memory();
+}
+
+static void _lock_mem_if_needed(struct cmd_context *cmd)
+{
+       if ((_memlock_count + _memlock_count_daemon) == 1)
+               _lock_mem(cmd);
+}
+
+static void _unlock_mem_if_possible(struct cmd_context *cmd)
+{
+       if ((_memlock_count + _memlock_count_daemon) == 0)
+               _unlock_mem(cmd);
+}
+
+void memlock_inc(struct cmd_context *cmd)
+{
+       ++_memlock_count;
+       _lock_mem_if_needed(cmd);
+       log_debug("memlock_count inc to %d", _memlock_count);
+}
+
+void memlock_dec(struct cmd_context *cmd)
+{
+       if (!_memlock_count)
+               log_error(INTERNAL_ERROR "_memlock_count has dropped below 0.");
+       --_memlock_count;
+       _unlock_mem_if_possible(cmd);
+       log_debug("memlock_count dec to %d", _memlock_count);
+}
+
+/*
+ * The memlock_*_daemon functions will force the mlockall() call that we need
+ * to stay in memory, but they will have no effect on device scans (unlike
+ * normal memlock_inc and memlock_dec). Memory is kept locked as long as either
+ * of memlock or memlock_daemon is in effect.
+ */
+
+void memlock_inc_daemon(struct cmd_context *cmd)
+{
+       ++_memlock_count_daemon;
+       if (_memlock_count_daemon == 1 && _memlock_count > 0)
+                log_error(INTERNAL_ERROR "_memlock_inc_daemon used after _memlock_inc.");
+       _lock_mem_if_needed(cmd);
+       log_debug("memlock_count_daemon inc to %d", _memlock_count_daemon);
+}
+
+void memlock_dec_daemon(struct cmd_context *cmd)
+{
+       if (!_memlock_count_daemon)
+               log_error(INTERNAL_ERROR "_memlock_count_daemon has dropped below 0.");
+       --_memlock_count_daemon;
+       _unlock_mem_if_possible(cmd);
+       log_debug("memlock_count_daemon dec to %d", _memlock_count_daemon);
+}
+
+/*
+ * This disregards the daemon (dmeventd) locks, since we use memlock() to check
+ * whether it is safe to run a device scan, which would normally coincide with
+ * !memlock() -- but the daemon global memory lock breaks this assumption, so
+ * we do not take those into account here.
+ */
+int memlock(void)
+{
+       return _memlock_count;
+}
+
+void memlock_init(struct cmd_context *cmd)
+{
+       _size_stack = find_config_tree_int(cmd,
+                                     "activation/reserved_stack",
+                                     DEFAULT_RESERVED_STACK) * 1024;
+       _size_malloc_tmp = find_config_tree_int(cmd,
+                                          "activation/reserved_memory",
+                                          DEFAULT_RESERVED_MEMORY) * 1024;
+       _default_priority = find_config_tree_int(cmd,
+                                           "activation/process_priority",
+                                           DEFAULT_PROCESS_PRIORITY);
+}
+
+#endif
diff --git a/lib/mm/memlock.h b/lib/mm/memlock.h
new file mode 100644 (file)
index 0000000..fd19317
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef LVM_MEMLOCK_H
+#define LVM_MEMLOCK_H
+
+struct cmd_context;
+
+void memlock_inc(struct cmd_context *cmd);
+void memlock_dec(struct cmd_context *cmd);
+void memlock_inc_daemon(struct cmd_context *cmd);
+void memlock_dec_daemon(struct cmd_context *cmd);
+int memlock(void);
+void memlock_init(struct cmd_context *cmd);
+
+#endif
diff --git a/lib/mm/xlate.h b/lib/mm/xlate.h
new file mode 100644 (file)
index 0000000..57d7241
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_XLATE_H
+#define _LVM_XLATE_H
+
+#ifdef linux
+#  include <endian.h>
+#  include <byteswap.h>
+#else
+#  include <machine/endian.h>
+#  define bswap_16(x) (((x) & 0x00ffU) << 8 | \
+                      ((x) & 0xff00U) >> 8)
+#  define bswap_32(x) (((x) & 0x000000ffU) << 24 | \
+                      ((x) & 0xff000000U) >> 24 | \
+                      ((x) & 0x0000ff00U) << 8  | \
+                      ((x) & 0x00ff0000U) >> 8)
+#  define bswap_64(x) (((x) & 0x00000000000000ffULL) << 56 | \
+                      ((x) & 0xff00000000000000ULL) >> 56 | \
+                      ((x) & 0x000000000000ff00ULL) << 40 | \
+                      ((x) & 0x00ff000000000000ULL) >> 40 | \
+                      ((x) & 0x0000000000ff0000ULL) << 24 | \
+                      ((x) & 0x0000ff0000000000ULL) >> 24 | \
+                      ((x) & 0x00000000ff000000ULL) << 8 | \
+                      ((x) & 0x000000ff00000000ULL) >> 8)
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#  define xlate16(x) (x)
+#  define xlate32(x) (x)
+#  define xlate64(x) (x)
+#  define xlate16_be(x) bswap_16(x)
+#  define xlate32_be(x) bswap_32(x)
+#  define xlate64_be(x) bswap_64(x)
+#elif BYTE_ORDER == BIG_ENDIAN
+#  define xlate16(x) bswap_16(x)
+#  define xlate32(x) bswap_32(x)
+#  define xlate64(x) bswap_64(x)
+#  define xlate16_be(x) (x)
+#  define xlate32_be(x) (x)
+#  define xlate64_be(x) (x)
+#else
+#  include <asm/byteorder.h>
+#  define xlate16(x) __cpu_to_le16((x))
+#  define xlate32(x) __cpu_to_le32((x))
+#  define xlate64(x) __cpu_to_le64((x))
+#  define xlate16_be(x) __cpu_to_be16((x))
+#  define xlate32_be(x) __cpu_to_be32((x))
+#  define xlate64_be(x) __cpu_to_be64((x))
+#endif
+
+#endif
diff --git a/lib/replicator/.exported_symbols b/lib/replicator/.exported_symbols
new file mode 100644 (file)
index 0000000..1c92c6a
--- /dev/null
@@ -0,0 +1 @@
+init_segtype
diff --git a/lib/replicator/Makefile.in b/lib/replicator/Makefile.in
new file mode 100644 (file)
index 0000000..ac42730
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = replicator.c
+
+LIB_SHARED = liblvm2replicator.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lib_shared_plugin
diff --git a/lib/replicator/replicator.c b/lib/replicator/replicator.c
new file mode 100644 (file)
index 0000000..26a5cf1
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "activate.h"
+#include "str_list.h"
+#ifdef DMEVENTD
+#  include "sharedlib.h"
+#  include "libdevmapper-event.h"
+#endif
+
+/* Dm kernel module name for replicator */
+#define REPLICATOR_MODULE "replicator"
+#define REPLICATOR_DEV_MODULE "replicator-dev"
+
+/*
+ * Macro used as return argument - returns 0.
+ * return is left to be written in the function for better readability.
+ */
+#define SEG_LOG_ERROR(t, p...) \
+       log_error(t " segment %s of logical volume %s.", ## p, \
+                 config_parent_name(sn), seg->lv->name), 0;
+
+
+/*
+ *  Replicator target
+ */
+static const char *_replicator_name(const struct lv_segment *seg)
+{
+       return seg->segtype->name;
+}
+
+/* FIXME: missing implementation */
+static void _replicator_display(const struct lv_segment *seg)
+{
+       //const char *size;
+       //uint32_t s;
+
+       log_print("  Replicator");
+       if (seg->rlog_lv)
+               log_print("  Replicator volume\t%s", seg->rlog_lv->name);
+}
+
+/* Wrapper for get_config_uint32() with default value */
+static uint32_t _get_config_uint32(const struct config_node *cn,
+                                  const char *path,
+                                  uint32_t def)
+{
+       uint32_t t;
+
+       return get_config_uint32(cn, path, &t) ? t : def;
+}
+
+/* Wrapper for get_config_uint64() with default value */
+static uint64_t _get_config_uint64(const struct config_node *cn,
+                                  const char *path,
+                                  uint64_t def)
+{
+       uint64_t t;
+
+       return get_config_uint64(cn, path, &t) ? t : def;
+}
+
+
+/* Strings replicator_state_t enum */
+static const char _state_txt[NUM_REPLICATOR_STATE][8] = {
+       "passive",
+       "active"
+};
+
+/* Parse state string */
+static replicator_state_t _get_state(const struct config_node *sn,
+                                    const char *path, replicator_state_t def)
+{
+       const char *str;
+       unsigned i;
+
+       if (get_config_str(sn, path, &str)) {
+               for (i = 0; i < sizeof(_state_txt)/sizeof(_state_txt[0]); ++i)
+                       if (strcasecmp(str, _state_txt[i]) == 0)
+                               return (replicator_state_t) i;
+
+               log_warn("%s: unknown value '%s', using default '%s' state",
+                        path, str, _state_txt[def]);
+       }
+
+       return def;
+}
+
+/* Strings for replicator_action_t enum */
+static const char _op_mode_txt[NUM_DM_REPLICATOR_MODES][8] = {
+       "sync",
+       "warn",
+       "stall",
+       "drop",
+       "fail"
+};
+
+
+/* Parse action string */
+static dm_replicator_mode_t _get_op_mode(const struct config_node *sn,
+                                        const char *path, dm_replicator_mode_t def)
+{
+       const char *str;
+       unsigned i;
+
+       if (get_config_str(sn, path, &str)) {
+               for (i = 0; i < sizeof(_op_mode_txt)/sizeof(_op_mode_txt[0]); ++i)
+                       if (strcasecmp(str, _op_mode_txt[i]) == 0) {
+                               log_very_verbose("Setting %s to %s",
+                                                path, _op_mode_txt[i]);
+                               return (dm_replicator_mode_t) i;
+                       }
+               log_warn("%s: unknown value '%s', using default '%s' operation mode",
+                        path, str, _op_mode_txt[def]);
+       }
+
+       return def;
+}
+
+static struct replicator_site *_get_site(struct logical_volume *replicator,
+                                        const char *key)
+{
+       struct dm_pool *mem = replicator->vg->vgmem;
+       struct replicator_site *rsite;
+
+       dm_list_iterate_items(rsite, &replicator->rsites)
+               if (strcasecmp(rsite->name, key) == 0)
+                       return rsite;
+
+       if (!(rsite = dm_pool_zalloc(mem, sizeof(*rsite))))
+               return_NULL;
+
+       if (!(rsite->name = dm_pool_strdup(mem, key)))
+               return_NULL;
+
+       rsite->replicator = replicator;
+       dm_list_init(&rsite->rdevices);
+       dm_list_add(&replicator->rsites, &rsite->list);
+
+       return rsite;
+}
+
+
+/* Parse replicator site element */
+static int _add_site(struct lv_segment *seg,
+                    const char *key,
+                    const struct config_node *sn)
+{
+       struct dm_pool *mem = seg->lv->vg->vgmem;
+       const struct config_node *cn;
+       struct replicator_site *rsite;
+
+       if (!(rsite = _get_site(seg->lv, key)))
+               return_0;
+
+       if (!find_config_node(sn, "site_index"))
+               return SEG_LOG_ERROR("Mandatory site_index is missing for");
+
+       rsite->state = _get_state(sn, "state", REPLICATOR_STATE_PASSIVE);
+       rsite->site_index = _get_config_uint32(sn, "site_index", 0);
+       if (rsite->site_index > seg->rsite_index_highest)
+               return SEG_LOG_ERROR("site_index=%d > highest_site_index=%d for",
+                                    rsite->site_index, seg->rsite_index_highest);
+
+       rsite->fall_behind_data = _get_config_uint64(sn, "fall_behind_data", 0);
+       rsite->fall_behind_ios = _get_config_uint32(sn, "fall_behind_ios", 0);
+       rsite->fall_behind_timeout = _get_config_uint32(sn, "fall_behind_timeout", 0);
+       rsite->op_mode = DM_REPLICATOR_SYNC;
+
+       if (rsite->fall_behind_data ||
+           rsite->fall_behind_ios ||
+           rsite->fall_behind_timeout) {
+               if (rsite->fall_behind_data && rsite->fall_behind_ios)
+                       return SEG_LOG_ERROR("Defined both fall_behind_data "
+                                            "and fall_behind_ios in");
+
+               if (rsite->fall_behind_data && rsite->fall_behind_timeout)
+                       return SEG_LOG_ERROR("Defined both fall_behind_data "
+                                            "and fall_behind_timeout in");
+
+               if (rsite->fall_behind_ios && rsite->fall_behind_timeout)
+                       return SEG_LOG_ERROR("Defined both fall_behind_ios "
+                                            "and fall_behind_timeout in");
+
+               rsite->op_mode = _get_op_mode(sn, "operation_mode",
+                                             rsite->op_mode);
+       }
+
+       if ((cn = find_config_node(sn, "volume_group"))) {
+               if (!cn->v || cn->v->type != CFG_STRING)
+                       return SEG_LOG_ERROR("volume_group must be a string in");
+
+               if (!(rsite->vg_name = dm_pool_strdup(mem, cn->v->v.str)))
+                       return_0;
+
+       } else if (rsite->site_index != 0)
+               return SEG_LOG_ERROR("volume_group is mandatory for remote site in");
+
+       return 1;
+}
+
+
+/* Import replicator segment */
+static int _replicator_text_import(struct lv_segment *seg,
+                                  const struct config_node *sn,
+                                  struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+       const struct config_node *cn;
+       struct logical_volume *rlog_lv;
+
+       if (!replicator_add_replicator_dev(seg->lv, NULL))
+               return_0;
+
+       if (!(cn = find_config_node(sn, "replicator_log")) ||
+           !cn->v || cn->v->type != CFG_STRING)
+               return SEG_LOG_ERROR("Replicator log type must be a string in");
+
+       if (!(rlog_lv = find_lv(seg->lv->vg, cn->v->v.str)))
+               return SEG_LOG_ERROR("Unknown replicator log %s in",
+                                    cn->v->v.str);
+
+       if (!(cn = find_config_node(sn, "replicator_log_type")) ||
+           !cn->v || cn->v->type != CFG_STRING)
+               return SEG_LOG_ERROR("Replicator log's type must be a string in");
+       if (strcasecmp(cn->v->v.str, "ringbuffer"))
+               return SEG_LOG_ERROR("Only ringbuffer replicator log type is supported in");
+
+       if (!(seg->rlog_type = dm_pool_strdup(seg->lv->vg->vgmem, cn->v->v.str)))
+               return_0;
+
+
+       log_very_verbose("replicator_log = %s", rlog_lv->name);
+       log_very_verbose("replicator_log_type = %s", seg->rlog_type);
+
+       if (!replicator_add_rlog(seg, rlog_lv))
+               return_0;
+
+       seg->rdevice_index_highest = _get_config_uint64(sn, "highest_device_index", 0);
+       seg->rsite_index_highest = _get_config_uint32(sn, "highest_site_index", 0);
+
+       seg->region_size = _get_config_uint32(sn, "sync_log_size", 0);
+
+       for (; sn; sn = sn->sib)
+               if (!sn->v) {
+                       for (cn = sn->sib; cn; cn = cn->sib)
+                               if (!cn->v && (strcasecmp(cn->key ,sn->key) == 0))
+                                       return SEG_LOG_ERROR("Detected duplicate site "
+                                                            "name %s in", sn->key);
+                       if (!_add_site(seg, sn->key, sn->child))
+                               return_0;
+               }
+       return 1;
+}
+
+/* Export replicator segment */
+static int _replicator_text_export(const struct lv_segment *seg,
+                                  struct formatter *f)
+{
+       struct replicator_site *rsite;
+
+       if (!seg->rlog_lv)
+                return_0;
+
+       outf(f, "replicator_log = \"%s\"", seg->rlog_lv->name);
+       outf(f, "replicator_log_type = \"%s\"", seg->rlog_type);
+       outf(f, "highest_device_index = %" PRIu64, seg->rdevice_index_highest);
+       outf(f, "highest_site_index = %d", seg->rsite_index_highest);
+
+       if (seg->region_size)
+               outsize(f, (uint64_t)seg->region_size,
+                       "sync_log_size = %" PRIu32, seg->region_size);
+
+       if (!dm_list_empty(&seg->lv->rsites))
+               outnl(f);
+
+       dm_list_iterate_items(rsite, &seg->lv->rsites) {
+               outf(f, "%s {", rsite->name);
+               out_inc_indent(f);
+
+               outf(f, "state = \"%s\"", _state_txt[rsite->state]);
+               outf(f, "site_index = %d", rsite->site_index);
+
+               /* Only non-default parameters are written */
+               if (rsite->op_mode != DM_REPLICATOR_SYNC)
+                       outf(f, "operation_mode = \"%s\"",
+                            _op_mode_txt[rsite->op_mode]);
+               if (rsite->fall_behind_timeout)
+                       outfc(f, "# seconds", "fall_behind_timeout = %u",
+                            rsite->fall_behind_timeout);
+               if (rsite->fall_behind_ios)
+                       outfc(f, "# io operations", "fall_behind_ios = %u",
+                            rsite->fall_behind_ios);
+               if (rsite->fall_behind_data)
+                       outsize(f, rsite->fall_behind_data, "fall_behind_data = %" PRIu64,
+                               rsite->fall_behind_data);
+               if (rsite->state != REPLICATOR_STATE_ACTIVE && rsite->vg_name)
+                       outf(f, "volume_group = \"%s\"", rsite->vg_name);
+
+               out_dec_indent(f);
+               outf(f, "}");
+       }
+
+       return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _replicator_add_target_line(struct dev_manager *dm,
+                                      struct dm_pool *mem,
+                                      struct cmd_context *cmd,
+                                      void **target_state,
+                                      struct lv_segment *seg,
+                                      struct dm_tree_node *node,
+                                      uint64_t len,
+                                      uint32_t *pvmove_mirror_count)
+{
+       const char *rlog_dlid;
+       struct replicator_site *rsite;
+
+       if (!seg->rlog_lv)
+               return_0;
+
+       if (!(rlog_dlid = build_dm_uuid(mem, seg->rlog_lv->lvid.s, NULL)))
+               return_0;
+
+       dm_list_iterate_items(rsite, &seg->lv->rsites) {
+               if (!dm_tree_node_add_replicator_target(node,
+                                                       seg->rlog_lv->size,
+                                                       rlog_dlid,
+                                                       seg->rlog_type,
+                                                       rsite->site_index,
+                                                       rsite->op_mode,
+                                                       rsite->fall_behind_timeout,
+                                                       rsite->fall_behind_data,
+                                                       rsite->fall_behind_ios)) {
+                       if (rsite->site_index == 0) {
+                               log_error("Failed to add replicator log '%s' "
+                                         "to replicator '%s'.",
+                                         rlog_dlid, seg->lv->name);
+                               return 0;
+                       }
+                       // FIXME:
+               }
+       }
+
+       return 1;
+}
+
+/* FIXME: write something useful for replicator here */
+static int _replicator_target_percent(void **target_state,
+                                     percent_t *percent,
+                                     struct dm_pool *mem,
+                                     struct cmd_context *cmd,
+                                     struct lv_segment *seg,
+                                     char *params, uint64_t *total_numerator,
+                                     uint64_t *total_denominator)
+{
+       return 1;
+}
+
+/* Check for module presence */
+static int _replicator_target_present(struct cmd_context *cmd,
+                                     const struct lv_segment *seg __attribute__((unused)),
+                                     unsigned *attributes __attribute__((unused)))
+{
+       static int _checked = 0;
+       static int _present = 0;
+
+       if (!_checked) {
+               _present = target_present(cmd, REPLICATOR_MODULE, 1);
+               _checked = 1;
+       }
+
+       return _present;
+}
+
+#endif
+
+static int _replicator_modules_needed(struct dm_pool *mem,
+                                     const struct lv_segment *seg __attribute__((unused)),
+                                     struct dm_list *modules)
+{
+       if (!str_list_add(mem, modules, REPLICATOR_MODULE))
+               return_0;
+
+       if (!str_list_add(mem, modules, REPLICATOR_DEV_MODULE))
+               return_0;
+
+       return 1;
+}
+
+static void _replicator_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _replicator_ops = {
+       .name = _replicator_name,
+       .display = _replicator_display,
+       .text_import = _replicator_text_import,
+       .text_export = _replicator_text_export,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _replicator_add_target_line,
+       .target_percent = _replicator_target_percent,
+       .target_present = _replicator_target_present,
+#endif
+       .modules_needed = _replicator_modules_needed,
+       .destroy = _replicator_destroy,
+};
+
+/*
+ *  Replicator-dev  target
+ */
+static void _replicator_dev_display(const struct lv_segment *seg)
+{
+       //const char *size;
+       //uint32_t s;
+       // FIXME: debug test code for now
+       log_print("  Replicator\t\t%u", seg->area_count);
+       log_print("  Mirror size\t\t%u", seg->area_len);
+       if (seg->log_lv)
+               log_print("  Replicator log volume\t%s", seg->rlog_lv->name);
+
+}
+
+static int _add_device(struct lv_segment *seg,
+                      const char *site_name,
+                      const struct config_node *sn,
+                      uint64_t devidx)
+{
+       struct dm_pool *mem = seg->lv->vg->vgmem;
+       struct logical_volume *lv = NULL;
+       struct logical_volume *slog_lv = NULL;
+       struct replicator_site *rsite = _get_site(seg->replicator, site_name);
+       struct replicator_device *rdev;
+       const char *dev_str = NULL;
+       const char *slog_str = NULL;
+       const struct config_node *cn;
+
+       dm_list_iterate_items(rdev, &rsite->rdevices)
+               if (rdev->replicator_dev == seg)
+                       return SEG_LOG_ERROR("Duplicate site found in");
+
+       if ((cn = find_config_node(sn, "sync_log"))) {
+               if (!cn->v || !cn->v->v.str)
+                       return SEG_LOG_ERROR("Sync log must be a string in");
+               slog_str = cn->v->v.str;
+       }
+
+       if (!(cn = find_config_node(sn, "logical_volume")) ||
+           !cn->v || !cn->v->v.str)
+               return SEG_LOG_ERROR("Logical volume must be a string in");
+
+       dev_str = cn->v->v.str;
+
+       if (!seg->lv->rdevice) {
+               if (slog_str)
+                       return SEG_LOG_ERROR("Sync log %s defined for local "
+                                            "device in", slog_str);
+
+               /* Check for device in current VG */
+               if (!(lv = find_lv(seg->lv->vg, dev_str)))
+                       return SEG_LOG_ERROR("Logical volume %s not found in",
+                                            dev_str);
+       } else {
+               if (!slog_str)
+                       return SEG_LOG_ERROR("Sync log is missing for remote "
+                                            "device in");
+               /* Check for slog device in current VG */
+               if (!(slog_lv = find_lv(seg->lv->vg, slog_str)))
+                       return SEG_LOG_ERROR("Sync log %s not found in",
+                                            slog_str);
+       }
+
+       if (!(rdev = dm_pool_zalloc(mem, sizeof(*rdev))))
+               return_0;
+
+       if (!(rdev->name = dm_pool_strdup(mem, dev_str)))
+               return_0;
+
+       rdev->replicator_dev = seg;
+       rdev->rsite = rsite;
+       rdev->device_index = devidx;
+
+       if (!seg->lv->rdevice) {
+               if (!replicator_dev_add_rimage(rdev, lv))
+                       return SEG_LOG_ERROR("LV inconsistency found in");
+               seg->lv->rdevice = rdev;
+       } else {
+               if (!slog_str ||
+                   !(rdev->slog_name = dm_pool_strdup(mem, slog_str)))
+                       return_0;
+
+               if (!replicator_dev_add_slog(rdev, slog_lv))
+                       return SEG_LOG_ERROR("Sync log inconsistency found in");
+       }
+
+       dm_list_add(&rsite->rdevices, &rdev->list);// linked site list
+
+       return 1;
+}
+
+/* Import replicator segment */
+static int _replicator_dev_text_import(struct lv_segment *seg,
+                                      const struct config_node *sn,
+                                      struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+       const struct config_node *cn;
+       struct logical_volume *replicator;
+       uint64_t devidx;
+
+       if (!(cn = find_config_node(sn, "replicator")))
+               return SEG_LOG_ERROR("Replicator is missing for");
+
+       if (!cn->v || !cn->v->v.str)
+               return SEG_LOG_ERROR("Replicator must be a string for");
+
+       if (!(replicator = find_lv(seg->lv->vg, cn->v->v.str)))
+               return SEG_LOG_ERROR("Unknown replicator %s for", cn->v->v.str);
+
+       if (!replicator_add_replicator_dev(replicator, seg))
+               return_0;
+
+       log_very_verbose("replicator=%s", replicator->name);
+
+       /* Mandatory */
+       if (!find_config_node(sn, "device_index") ||
+           !get_config_uint64(sn, "device_index", &devidx))
+               return SEG_LOG_ERROR("Could not read 'device_index' for");
+
+       /* Read devices from sites */
+       for (; sn; sn = sn->sib)
+               if (!(sn->v) && !_add_device(seg, sn->key, sn->child, devidx))
+                       return_0;
+
+       if (!seg->lv->rdevice)
+               return SEG_LOG_ERROR("Replicator device without site in");
+
+       seg->rlog_lv = NULL;
+       seg->lv->status |= REPLICATOR;
+
+       return 1;
+}
+
+/* Export replicator-dev segment */
+static int _replicator_dev_text_export(const struct lv_segment *seg,
+                                      struct formatter *f)
+{
+       struct replicator_site *rsite;
+       struct replicator_device *rdev;
+
+       if (!seg->replicator || !seg->lv->rdevice)
+               return_0;
+
+       outf(f, "replicator = \"%s\"", seg->replicator->name);
+       outf(f, "device_index = %" PRId64, seg->lv->rdevice->device_index);
+
+       outnl(f);
+
+       dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+               dm_list_iterate_items(rdev, &rsite->rdevices) {
+                       if (rdev->replicator_dev != seg)
+                               continue;
+
+                       outf(f, "%s {", rdev->rsite->name);
+
+                       out_inc_indent(f);
+
+                       outf(f, "logical_volume = \"%s\"",
+                            rdev->name ? rdev->name : rdev->lv->name);
+
+                       if (rdev->slog)
+                               outf(f, "sync_log = \"%s\"", rdev->slog->name);
+                       else if (rdev->slog_name)
+                               outf(f, "sync_log = \"%s\"", rdev->slog_name);
+
+                       out_dec_indent(f);
+
+                       outf(f, "}");
+               }
+       }
+
+       return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+/*
+ * Add target for passive site matching the device index
+ */
+static int _replicator_dev_add_target_line(struct dev_manager *dm,
+                                          struct dm_pool *mem,
+                                          struct cmd_context *cmd,
+                                          void **target_state,
+                                          struct lv_segment *seg,
+                                          struct dm_tree_node *node,
+                                          uint64_t len,
+                                          uint32_t *pvmove_mirror_count)
+{
+       const char *replicator_dlid, *rdev_dlid, *slog_dlid;
+       struct replicator_device *rdev, *rdev_search;
+       struct replicator_site *rsite;
+       uint32_t slog_size;
+       uint32_t slog_flags;
+
+       if (!lv_is_active_replicator_dev(seg->lv)) {
+               /* Create passive linear mapping */
+               log_very_verbose("Inactive replicator %s using %s.",
+                                seg->lv->name, seg->lv->rdevice->lv->name);
+               if (!dm_tree_node_add_linear_target(node, seg->lv->size))
+                       return_0;
+               if (!(rdev_dlid = build_dm_uuid(mem, seg->lv->rdevice->lv->lvid.s, NULL)))
+                       return_0;
+               return dm_tree_node_add_target_area(node, NULL, rdev_dlid, 0);
+       } else if (seg->lv->rdevice->rsite->site_index) {
+               log_error("Active site with site_index != 0 (%s, %d)",
+                         seg->lv->rdevice->rsite->name,
+                         seg->lv->rdevice->rsite->site_index);
+               return 0; /* Replicator without any active site */
+       }
+
+       /*
+        * At this point all devices that have some connection with replicator
+        * must be present in dm_tree
+        */
+       if (!seg_is_replicator_dev(seg) ||
+           !(replicator_dlid = build_dm_uuid(mem, seg->replicator->lvid.s, NULL)))
+               return_0;
+
+       /* Select remote devices with the same device index */
+       dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+               if (rsite->site_index == 0) {
+                       /* Local slink0 device */
+                       rdev = seg->lv->rdevice;
+               } else {
+                       rdev = NULL;
+                       dm_list_iterate_items(rdev_search, &rsite->rdevices) {
+                               if (rdev_search->replicator_dev == seg) {
+                                       rdev = rdev_search;
+                                       break;
+                               }
+                       }
+
+                       if (!rdev) {
+                               log_error(INTERNAL_ERROR "rdev list not found.");
+                               return 0;
+                       }
+               }
+
+               if (!rdev->lv ||
+                   !(rdev_dlid = build_dm_uuid(mem, rdev->lv->lvid.s, NULL)))
+                       return_0;
+
+               slog_dlid = NULL;
+
+               /* Using either disk or core (in memory) log */
+               if (rdev->slog) {
+                       slog_flags = DM_NOSYNC;
+                       slog_size = (uint32_t) rdev->slog->size;
+                       if (!(slog_dlid = build_dm_uuid(mem, rdev->slog->lvid.s, NULL)))
+                               return_0;
+               } else if (rdev->slog_name &&
+                          sscanf(rdev->slog_name, "%" PRIu32, &slog_size) == 1) {
+                       slog_flags = DM_CORELOG | DM_FORCESYNC;
+                       if (slog_size == 0) {
+                               log_error("Failed to use empty corelog size "
+                                         "in replicator '%s'.",
+                                         rsite->replicator->name);
+                               return 0;
+                       }
+               } else  {
+                       slog_flags = DM_CORELOG | DM_FORCESYNC;
+                       slog_size = 0; /* NOLOG */
+               }
+
+               if (!dm_tree_node_add_replicator_dev_target(node,
+                                                           seg->lv->size,
+                                                           replicator_dlid,
+                                                           seg->lv->rdevice->device_index,
+                                                           rdev_dlid,
+                                                           rsite->site_index,
+                                                           slog_dlid,
+                                                           slog_flags,
+                                                           slog_size)) {
+                       return_0;
+                       /* FIXME: handle 'state = dropped' in future */
+               }
+       }
+
+       return 1;
+}
+
+/* FIXME: write something useful for replicator-dev here */
+static int _replicator_dev_target_percent(void **target_state,
+                                         percent_t *percent,
+                                         struct dm_pool *mem,
+                                         struct cmd_context *cmd,
+                                         struct lv_segment *seg,
+                                         char *params,
+                                         uint64_t *total_numerator,
+                                         uint64_t *total_denominator)
+{
+       return 1;
+}
+
+/* Check for module presence */
+static int _replicator_dev_target_present(struct cmd_context *cmd,
+                                         const struct lv_segment *seg __attribute__((unused)),
+                                         unsigned *attributes __attribute__((unused)))
+{
+       static int _checked = 0;
+       static int _present = 0;
+
+       if (!_checked) {
+               _present = target_present(cmd, REPLICATOR_DEV_MODULE, 1);
+               _checked = 1;
+       }
+
+       return _present;
+}
+
+#endif
+
+static struct segtype_handler _replicator_dev_ops = {
+       .name = _replicator_name,
+       .display = _replicator_dev_display,
+       .text_import = _replicator_dev_text_import,
+       .text_export = _replicator_dev_text_export,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _replicator_dev_add_target_line,
+       .target_percent = _replicator_dev_target_percent,
+       .target_present = _replicator_dev_target_present,
+#endif
+       .modules_needed = _replicator_modules_needed,
+       .destroy = _replicator_destroy,
+};
+
+#ifdef REPLICATOR_INTERNAL
+int init_replicator_segtype(struct segtype_library *seglib)
+#else /* Shared */
+int init_multiple_segtype(struct segtype_library *seglib);
+int init_multiple_segtype(struct segtype_library *seglib)
+#endif
+{
+       struct segment_type *segtype;
+
+       if (!(segtype = dm_malloc(sizeof(*segtype))))
+               return_0;
+
+       segtype->ops = &_replicator_ops;
+       segtype->name = REPLICATOR_MODULE;
+       segtype->private = NULL;
+       segtype->flags = SEG_REPLICATOR;
+
+       if (!lvm_register_segtype(seglib, segtype))
+               return_0;
+
+       log_very_verbose("Initialised segtype: " REPLICATOR_MODULE);
+
+       if (!(segtype = dm_malloc(sizeof(*segtype))))
+               return_0;
+
+       segtype->ops = &_replicator_dev_ops;
+       segtype->name = REPLICATOR_DEV_MODULE;
+       segtype->private = NULL;
+       segtype->flags = SEG_REPLICATOR_DEV;
+
+       if (!lvm_register_segtype(seglib, segtype))
+               return_0;
+
+       log_very_verbose("Initialised segtype: " REPLICATOR_DEV_MODULE);
+
+       return 1;
+}
diff --git a/lib/report/columns.h b/lib/report/columns.h
new file mode 100644 (file)
index 0000000..acc232f
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This file defines the fields (columns) for the reporting commands
+ * (pvs/vgs/lvs).
+ */
+/*
+ * The 'FIELD' macro arguments are defined as follows:
+ * 1. report_type.  An enum value that selects a specific
+ * struct dm_report_object_type in the _report_types array.  The value is
+ * used to select the containing base object address (see *obj_get*
+ * functions) for any data values of any field in the report.
+ * 2. Containing struct.  The structure that either contains the field data
+ * as a member or should be used to obtain the field data.  The containing
+ * struct should match the base object of the report_type.
+ * 3. Field type.  This must be either 'STR' or 'NUM'.
+ * 4. Report heading.  This is the field heading that is displayed by the
+ * reporting commands.
+ * 5. Data value pointer.  This argument is always a member of the
+ * containing struct.  It may point directly to the data value (for example,
+ * lv_uuid - see _uuid_disp()) or may be used to derive the data value (for
+ * example, seg_count - see _lvsegcount_disp()).  In the FIELD macro
+ * definition, it is used in an offset calculation to derive the offset to
+ * the data value from the containing struct base address.  Note that in some
+ * cases, the argument is the first member of the struct, in which case the
+ * data value pointer points to the start of the struct itself (for example,
+ * 'lvid' field of struct 'lv').
+ * 6. Minimum display width.  This is the minimum width used to display
+ * the field value, typically matching the width of the column heading.
+ * 7. Display function identifier.  Used to derive the full name of the
+ * function that displays this field.  Derivation is done by appending '_'
+ * then prepending this argument to '_disp'.  For example, if this argument
+ * is 'uuid', the display function is _uuid_disp().  Adding a new field may
+ * require defining a new display function (for example _myfieldname_disp()),
+ * or re-use of an existing one (for example, _uint32_disp()).
+ * 8. Unique format identifier / field id.  This name must be unique and is
+ * used to select fields via '-o' in the reporting commands (pvs/vgs/lvs).
+ * The string used to specify the field - the 'id' member of
+ * struct dm_report_field_type.
+ * 9. Description of field.  This is a brief (ideally <= 52 chars) description
+ * of the field used in the reporting commands.
+ * 10. Flags.
+ *     FIELD_MODIFIABLE.  A '_set' function exists to change the field's value.
+ *     The function name is derived in a similar way to item 7 above.
+ */
+
+#define FIELD_MODIFIABLE 0x00000001
+
+/* *INDENT-OFF* */
+FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, lv_uuid, "Unique identifier.", 0)
+FIELD(LVS, lv, STR, "LV", lvid, 4, lvname, lv_name, "Name.  LVs created for internal use are enclosed in brackets.", 0)
+FIELD(LVS, lv, STR, "Path", lvid, 4, lvpath, lv_path, "Full pathname for LV.", 0)
+FIELD(LVS, lv, STR, "Attr", lvid, 4, lvstatus, lv_attr, "Various attributes - see man page.", 0)
+FIELD(LVS, lv, NUM, "Maj", major, 3, int32, lv_major, "Persistent major number or -1 if not persistent.", 0)
+FIELD(LVS, lv, NUM, "Min", minor, 3, int32, lv_minor, "Persistent minor number or -1 if not persistent.", 0)
+FIELD(LVS, lv, NUM, "Rahead", lvid, 6, lvreadahead, lv_read_ahead, "Read ahead setting in current units.", 0)
+FIELD(LVS, lv, STR, "KMaj", lvid, 4, lvkmaj, lv_kernel_major, "Currently assigned major number or -1 if LV is not active.", 0)
+FIELD(LVS, lv, STR, "KMin", lvid, 4, lvkmin, lv_kernel_minor, "Currently assigned minor number or -1 if LV is not active.", 0)
+FIELD(LVS, lv, NUM, "KRahead", lvid, 7, lvkreadahead, lv_kernel_read_ahead, "Currently-in-use read ahead setting in current units.", 0)
+FIELD(LVS, lv, NUM, "LSize", size, 5, size64, lv_size, "Size of LV in current units.", 0)
+FIELD(LVS, lv, NUM, "#Seg", lvid, 4, lvsegcount, seg_count, "Number of segments in LV.", 0)
+FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, origin, "For snapshots, the origin device of this LV.", 0)
+FIELD(LVS, lv, NUM, "OSize", lvid, 5, originsize, origin_size, "For snapshots, the size of the origin device of this LV.", 0)
+FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, snap_percent, "For snapshots, the percentage full if LV is active.", 0)
+FIELD(LVS, lv, NUM, "Copy%", lvid, 6, copypercent, copy_percent, "For mirrors and pvmove, current percentage in-sync.", 0)
+FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, move_pv, "For pvmove, Source PV of temporary LV created by pvmove.", 0)
+FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, convert_lv, "For lvconvert, Name of temporary LV created by lvconvert.", 0)
+FIELD(LVS, lv, STR, "LV Tags", tags, 7, tags, lv_tags, "Tags, if any.", 0)
+FIELD(LVS, lv, STR, "Log", lvid, 3, loglv, mirror_log, "For mirrors, the LV holding the synchronisation log.", 0)
+FIELD(LVS, lv, STR, "Modules", lvid, 7, modules, modules, "Kernel device-mapper modules required for this LV.", 0)
+
+FIELD(LABEL, pv, STR, "Fmt", id, 3, pvfmt, pv_fmt, "Type of metadata.", 0)
+FIELD(LABEL, pv, STR, "PV UUID", id, 38, uuid, pv_uuid, "Unique identifier.", 0)
+FIELD(LABEL, pv, NUM, "DevSize", id, 7, devsize, dev_size, "Size of underlying device in current units.", 0)
+FIELD(LABEL, pv, STR, "PV", dev, 10, dev_name, pv_name, "Name.", 0)
+FIELD(LABEL, pv, NUM, "PMdaFree", id, 9, pvmdafree, pv_mda_free, "Free metadata area space on this device in current units.", 0)
+FIELD(LABEL, pv, NUM, "PMdaSize", id, 9, pvmdasize, pv_mda_size, "Size of smallest metadata area on this device in current units.", 0)
+
+FIELD(PVS, pv, NUM, "1st PE", pe_start, 7, size64, pe_start, "Offset to the start of data on the underlying device.", 0)
+FIELD(PVS, pv, NUM, "PSize", id, 5, pvsize, pv_size, "Size of PV in current units.", 0)
+FIELD(PVS, pv, NUM, "PFree", id, 5, pvfree, pv_free, "Total amount of unallocated space in current units.", 0)
+FIELD(PVS, pv, NUM, "Used", id, 4, pvused, pv_used, "Total amount of allocated space in current units.", 0)
+FIELD(PVS, pv, STR, "Attr", id, 4, pvstatus, pv_attr, "Various attributes - see man page.", 0)
+FIELD(PVS, pv, NUM, "PE", pe_count, 3, uint32, pv_pe_count, "Total number of Physical Extents.", 0)
+FIELD(PVS, pv, NUM, "Alloc", pe_alloc_count, 5, uint32, pv_pe_alloc_count, "Total number of allocated Physical Extents.", 0)
+FIELD(PVS, pv, STR, "PV Tags", tags, 7, tags, pv_tags, "Tags, if any.", 0)
+FIELD(PVS, pv, NUM, "#PMda", id, 5, pvmdas, pv_mda_count, "Number of metadata areas on this device.", 0)
+FIELD(PVS, pv, NUM, "#PMdaUse", id, 8, pvmdasused, pv_mda_used_count, "Number of metadata areas in use on this device.", 0)
+
+FIELD(VGS, vg, STR, "Fmt", cmd, 3, vgfmt, vg_fmt, "Type of metadata.", 0)
+FIELD(VGS, vg, STR, "VG UUID", id, 38, uuid, vg_uuid, "Unique identifier.", 0)
+FIELD(VGS, vg, STR, "VG", name, 4, string, vg_name, "Name.", 0)
+FIELD(VGS, vg, STR, "Attr", cmd, 5, vgstatus, vg_attr, "Various attributes - see man page.", 0)
+FIELD(VGS, vg, NUM, "VSize", cmd, 5, vgsize, vg_size, "Total size of VG in current units.", 0)
+FIELD(VGS, vg, NUM, "VFree", cmd, 5, vgfree, vg_free, "Total amount of free space in current units.", 0)
+FIELD(VGS, vg, STR, "SYS ID", system_id, 6, string, vg_sysid, "System ID indicating when and where it was created.", 0)
+FIELD(VGS, vg, NUM, "Ext", extent_size, 3, size32, vg_extent_size, "Size of Physical Extents in current units.", 0)
+FIELD(VGS, vg, NUM, "#Ext", extent_count, 4, uint32, vg_extent_count, "Total number of Physical Extents.", 0)
+FIELD(VGS, vg, NUM, "Free", free_count, 4, uint32, vg_free_count, "Total number of unallocated Physical Extents.", 0)
+FIELD(VGS, vg, NUM, "MaxLV", max_lv, 5, uint32, max_lv, "Maximum number of LVs allowed in VG or 0 if unlimited.", 0)
+FIELD(VGS, vg, NUM, "MaxPV", max_pv, 5, uint32, max_pv, "Maximum number of PVs allowed in VG or 0 if unlimited.", 0)
+FIELD(VGS, vg, NUM, "#PV", pv_count, 3, uint32, pv_count, "Number of PVs.", 0)
+FIELD(VGS, vg, NUM, "#LV", cmd, 3, lvcount, lv_count, "Number of LVs.", 0)
+FIELD(VGS, vg, NUM, "#SN", cmd, 3, snapcount, snap_count, "Number of snapshots.", 0)
+FIELD(VGS, vg, NUM, "Seq", seqno, 3, uint32, vg_seqno, "Revision number of internal metadata.  Incremented whenever it changes.", 0)
+FIELD(VGS, vg, STR, "VG Tags", tags, 7, tags, vg_tags, "Tags, if any.", 0)
+FIELD(VGS, vg, NUM, "#VMda", cmd, 5, vgmdas, vg_mda_count, "Number of metadata areas on this VG.", 0)
+FIELD(VGS, vg, NUM, "#VMdaUse", cmd, 8, vgmdasused, vg_mda_used_count, "Number of metadata areas in use on this VG.", 0)
+FIELD(VGS, vg, NUM, "VMdaFree", cmd, 9, vgmdafree, vg_mda_free, "Free metadata area space for this VG in current units.", 0)
+FIELD(VGS, vg, NUM, "VMdaSize", cmd, 9, vgmdasize, vg_mda_size, "Size of smallest metadata area for this VG in current units.", 0)
+FIELD(VGS, vg, NUM, "#VMdaCps", cmd, 8, vgmdacopies, vg_mda_copies, "Target number of in use metadata areas in the VG.", 1)
+
+FIELD(SEGS, seg, STR, "Type", list, 4, segtype, segtype, "Type of LV segment.", 0)
+FIELD(SEGS, seg, NUM, "#Str", area_count, 4, uint32, stripes, "Number of stripes or mirror legs.", 0)
+FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, stripesize, "For stripes, amount of data placed on one device before switching to the next.", 0)
+FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, stripe_size, "For stripes, amount of data placed on one device before switching to the next.", 0)
+FIELD(SEGS, seg, NUM, "Region", region_size, 6, size32, regionsize, "For mirrors, the unit of data copied when synchronising devices.", 0)
+FIELD(SEGS, seg, NUM, "Region", region_size, 6, size32, region_size, "For mirrors, the unit of data copied when synchronising devices.", 0)
+FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, chunksize, "For snapshots, the unit of data used when tracking changes.", 0)
+FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, chunk_size, "For snapshots, the unit of data used when tracking changes.", 0)
+FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, seg_start, "Offset within the LV to the start of the segment in current units.", 0)
+FIELD(SEGS, seg, NUM, "Start", list, 5, segstartpe, seg_start_pe, "Offset within the LV to the start of the segment in physical extents.", 0)
+FIELD(SEGS, seg, NUM, "SSize", list, 5, segsize, seg_size, "Size of segment in current units.", 0)
+FIELD(SEGS, seg, STR, "Seg Tags", tags, 8, tags, seg_tags, "Tags, if any.", 0)
+FIELD(SEGS, seg, STR, "PE Ranges", list, 9, peranges, seg_pe_ranges, "Ranges of Physical Extents of underlying devices in command line format.", 0)
+FIELD(SEGS, seg, STR, "Devices", list, 7, devices, devices, "Underlying devices used with starting extent numbers.", 0)
+
+FIELD(PVSEGS, pvseg, NUM, "Start", pe, 5, uint32, pvseg_start, "Physical Extent number of start of segment.", 0)
+FIELD(PVSEGS, pvseg, NUM, "SSize", len, 5, uint32, pvseg_size, "Number of extents in segment.", 0)
+/* *INDENT-ON* */
diff --git a/lib/report/properties.c b/lib/report/properties.c
new file mode 100644 (file)
index 0000000..0b4929a
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include <errno.h>
+
+#include "libdevmapper.h"
+#include "properties.h"
+#include "activate.h"
+#include "lvm-logging.h"
+#include "lvm-types.h"
+#include "metadata.h"
+
+#define GET_NUM_PROPERTY_FN(NAME, VALUE, TYPE, VAR)                    \
+static int _ ## NAME ## _get (const void *obj, struct lvm_property_type *prop) \
+{ \
+       const struct TYPE *VAR = (const struct TYPE *)obj; \
+\
+       prop->value.integer = VALUE; \
+       return 1; \
+}
+#define GET_VG_NUM_PROPERTY_FN(NAME, VALUE) \
+       GET_NUM_PROPERTY_FN(NAME, VALUE, volume_group, vg)
+#define GET_PV_NUM_PROPERTY_FN(NAME, VALUE) \
+       GET_NUM_PROPERTY_FN(NAME, VALUE, physical_volume, pv)
+#define GET_LV_NUM_PROPERTY_FN(NAME, VALUE) \
+       GET_NUM_PROPERTY_FN(NAME, VALUE, logical_volume, lv)
+#define GET_LVSEG_NUM_PROPERTY_FN(NAME, VALUE) \
+       GET_NUM_PROPERTY_FN(NAME, VALUE, lv_segment, lvseg)
+#define GET_PVSEG_NUM_PROPERTY_FN(NAME, VALUE) \
+       GET_NUM_PROPERTY_FN(NAME, VALUE, pv_segment, pvseg)
+
+#define SET_NUM_PROPERTY_FN(NAME, SETFN, TYPE, VAR)                    \
+static int _ ## NAME ## _set (void *obj, struct lvm_property_type *prop) \
+{ \
+       struct TYPE *VAR = (struct TYPE *)obj; \
+\
+       SETFN(VAR, prop->value.integer);                \
+       return 1; \
+}
+#define SET_VG_NUM_PROPERTY_FN(NAME, SETFN) \
+       SET_NUM_PROPERTY_FN(NAME, SETFN, volume_group, vg)
+#define SET_PV_NUM_PROPERTY_FN(NAME, SETFN) \
+       SET_NUM_PROPERTY_FN(NAME, SETFN, physical_volume, pv)
+#define SET_LV_NUM_PROPERTY_FN(NAME, SETFN) \
+       SET_NUM_PROPERTY_FN(NAME, SETFN, logical_volume, lv)
+
+#define GET_STR_PROPERTY_FN(NAME, VALUE, TYPE, VAR)                    \
+static int _ ## NAME ## _get (const void *obj, struct lvm_property_type *prop) \
+{ \
+       const struct TYPE *VAR = (const struct TYPE *)obj; \
+\
+       prop->value.string = (char *)VALUE;     \
+       return 1; \
+}
+#define GET_VG_STR_PROPERTY_FN(NAME, VALUE) \
+       GET_STR_PROPERTY_FN(NAME, VALUE, volume_group, vg)
+#define GET_PV_STR_PROPERTY_FN(NAME, VALUE) \
+       GET_STR_PROPERTY_FN(NAME, VALUE, physical_volume, pv)
+#define GET_LV_STR_PROPERTY_FN(NAME, VALUE) \
+       GET_STR_PROPERTY_FN(NAME, VALUE, logical_volume, lv)
+#define GET_LVSEG_STR_PROPERTY_FN(NAME, VALUE) \
+       GET_STR_PROPERTY_FN(NAME, VALUE, lv_segment, lvseg)
+#define GET_PVSEG_STR_PROPERTY_FN(NAME, VALUE) \
+       GET_STR_PROPERTY_FN(NAME, VALUE, pv_segment, pvseg)
+
+static int _not_implemented_get(const void *obj, struct lvm_property_type *prop)
+{
+       log_errno(ENOSYS, "Function not implemented");
+       return 0;
+}
+
+static int _not_implemented_set(void *obj, struct lvm_property_type *prop)
+{
+       log_errno(ENOSYS, "Function not implemented");
+       return 0;
+}
+
+static percent_t _copy_percent(const struct logical_volume *lv) {
+    percent_t perc;
+    lv_mirror_percent(lv->vg->cmd, (struct logical_volume *) lv, 0, &perc, NULL);
+    return perc;
+}
+
+static percent_t _snap_percent(const struct logical_volume *lv) {
+    percent_t perc;
+    lv_snapshot_percent(lv, &perc);
+    return perc;
+}
+
+/* PV */
+GET_PV_STR_PROPERTY_FN(pv_fmt, pv_fmt_dup(pv))
+#define _pv_fmt_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_uuid, pv_uuid_dup(pv))
+#define _pv_uuid_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(dev_size, SECTOR_SIZE * pv_dev_size(pv))
+#define _dev_size_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_name, pv_name_dup(pv))
+#define _pv_name_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_free, SECTOR_SIZE * pv_mda_free(pv))
+#define _pv_mda_free_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_size, SECTOR_SIZE * pv_mda_size(pv))
+#define _pv_mda_size_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pe_start, SECTOR_SIZE * pv->pe_start)
+#define _pe_start_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_size, SECTOR_SIZE * pv_size_field(pv))
+#define _pv_size_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_free, SECTOR_SIZE * pv_free(pv))
+#define _pv_free_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_used, SECTOR_SIZE * pv_used(pv))
+#define _pv_used_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_attr, pv_attr_dup(pv->vg->vgmem, pv))
+#define _pv_attr_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_pe_count, pv->pe_count)
+#define _pv_pe_count_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_pe_alloc_count, pv->pe_alloc_count)
+#define _pv_pe_alloc_count_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_tags, pv_tags_dup(pv))
+#define _pv_tags_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_count, pv_mda_count(pv))
+#define _pv_mda_count_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_used_count, pv_mda_used_count(pv))
+#define _pv_mda_used_count_set _not_implemented_set
+
+/* LV */
+GET_LV_STR_PROPERTY_FN(lv_uuid, lv_uuid_dup(lv))
+#define _lv_uuid_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_name, lv_name_dup(lv->vg->vgmem, lv))
+#define _lv_name_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_path, lv_path_dup(lv->vg->vgmem, lv))
+#define _lv_path_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_attr, lv_attr_dup(lv->vg->vgmem, lv))
+#define _lv_attr_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_major, lv->major)
+#define _lv_major_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_minor, lv->minor)
+#define _lv_minor_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_read_ahead, lv->read_ahead * SECTOR_SIZE)
+#define _lv_read_ahead_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_kernel_major, lv_kernel_major(lv))
+#define _lv_kernel_major_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_kernel_minor, lv_kernel_minor(lv))
+#define _lv_kernel_minor_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_kernel_read_ahead, lv_kernel_read_ahead(lv) * SECTOR_SIZE)
+#define _lv_kernel_read_ahead_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_size, lv->size * SECTOR_SIZE)
+#define _lv_size_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(seg_count, dm_list_size(&lv->segments))
+#define _seg_count_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(origin, lv_origin_dup(lv->vg->vgmem, lv))
+#define _origin_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(origin_size, lv_origin_size(lv))
+#define _origin_size_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(snap_percent, _snap_percent(lv))
+#define _snap_percent_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(copy_percent, _copy_percent(lv))
+#define _copy_percent_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(move_pv, lv_move_pv_dup(lv->vg->vgmem, lv))
+#define _move_pv_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(convert_lv, lv_convert_lv_dup(lv->vg->vgmem, lv))
+#define _convert_lv_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_tags, lv_tags_dup(lv))
+#define _lv_tags_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(mirror_log, lv_mirror_log_dup(lv->vg->vgmem, lv))
+#define _mirror_log_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(modules, lv_modules_dup(lv->vg->vgmem, lv))
+#define _modules_set _not_implemented_set
+
+/* VG */
+GET_VG_STR_PROPERTY_FN(vg_fmt, vg_fmt_dup(vg))
+#define _vg_fmt_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_uuid, vg_uuid_dup(vg))
+#define _vg_uuid_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_name, vg_name_dup(vg))
+#define _vg_name_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_attr, vg_attr_dup(vg->vgmem, vg))
+#define _vg_attr_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_size, (SECTOR_SIZE * vg_size(vg)))
+#define _vg_size_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_free, (SECTOR_SIZE * vg_free(vg)))
+#define _vg_free_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_sysid, vg_system_id_dup(vg))
+#define _vg_sysid_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_extent_size, vg->extent_size)
+#define _vg_extent_size_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_extent_count, vg->extent_count)
+#define _vg_extent_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_free_count, vg->free_count)
+#define _vg_free_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(max_lv, vg->max_lv)
+#define _max_lv_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(max_pv, vg->max_pv)
+#define _max_pv_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(pv_count, vg->pv_count)
+#define _pv_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(lv_count, (vg_visible_lvs(vg)))
+#define _lv_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(snap_count, (snapshot_count(vg)))
+#define _snap_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_seqno, vg->seqno)
+#define _vg_seqno_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_tags, vg_tags_dup(vg))
+#define _vg_tags_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_count, (vg_mda_count(vg)))
+#define _vg_mda_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_used_count, (vg_mda_used_count(vg)))
+#define _vg_mda_used_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_free, (SECTOR_SIZE * vg_mda_free(vg)))
+#define _vg_mda_free_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_size, (SECTOR_SIZE * vg_mda_size(vg)))
+#define _vg_mda_size_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_copies, (vg_mda_copies(vg)))
+SET_VG_NUM_PROPERTY_FN(vg_mda_copies, vg_set_mda_copies)
+
+/* LVSEG */
+GET_LVSEG_STR_PROPERTY_FN(segtype, lvseg_segtype_dup(lvseg))
+#define _segtype_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(stripes, lvseg->area_count)
+#define _stripes_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(stripesize, lvseg->stripe_size)
+#define _stripesize_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(stripe_size, lvseg->stripe_size)
+#define _stripe_size_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(regionsize, lvseg->region_size)
+#define _regionsize_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(region_size, lvseg->region_size)
+#define _region_size_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(chunksize, lvseg_chunksize(lvseg))
+#define _chunksize_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(chunk_size, lvseg_chunksize(lvseg))
+#define _chunk_size_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_start, lvseg_start(lvseg))
+#define _seg_start_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_start_pe, lvseg->le)
+#define _seg_start_pe_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_size, (SECTOR_SIZE * lvseg_size(lvseg)))
+#define _seg_size_set _not_implemented_set
+GET_LVSEG_STR_PROPERTY_FN(seg_tags, lvseg_tags_dup(lvseg))
+#define _seg_tags_set _not_implemented_set
+#define _seg_pe_ranges_get _not_implemented_get
+#define _seg_pe_ranges_set _not_implemented_set
+#define _devices_get _not_implemented_get
+#define _devices_set _not_implemented_set
+
+
+/* PVSEG */
+GET_PVSEG_NUM_PROPERTY_FN(pvseg_start, pvseg->pe)
+#define _pvseg_start_set _not_implemented_set
+GET_PVSEG_NUM_PROPERTY_FN(pvseg_size, pvseg->len)
+#define _pvseg_size_set _not_implemented_set
+
+
+#define STR DM_REPORT_FIELD_TYPE_STRING
+#define NUM DM_REPORT_FIELD_TYPE_NUMBER
+#define FIELD(type, strct, sorttype, head, field, width, fn, id, desc, settable) \
+       { type, #id, settable, sorttype == STR, sorttype == NUM, { .integer = 0 }, _ ## id ## _get, _ ## id ## _set },
+
+struct lvm_property_type _properties[] = {
+#include "columns.h"
+       { 0, "", 0, 0, 0, { .integer = 0 }, _not_implemented_get, _not_implemented_set },
+};
+
+#undef STR
+#undef NUM
+#undef FIELD
+
+
+static int _get_property(const void *obj, struct lvm_property_type *prop,
+                        report_type_t type)
+{
+       struct lvm_property_type *p;
+
+       p = _properties;
+       while (p->id[0]) {
+               if (!strcmp(p->id, prop->id))
+                       break;
+               p++;
+       }
+       if (!p->id[0]) {
+               log_errno(EINVAL, "Invalid property name %s", prop->id);
+               return 0;
+       }
+       if (!(p->type & type)) {
+               log_errno(EINVAL, "Property name %s does not match type %d",
+                         prop->id, p->type);
+               return 0;
+       }
+
+       *prop = *p;
+       if (!p->get(obj, prop)) {
+               return 0;
+       }
+       return 1;
+}
+
+static int _set_property(void *obj, struct lvm_property_type *prop,
+                        report_type_t type)
+{
+       struct lvm_property_type *p;
+
+       p = _properties;
+       while (p->id[0]) {
+               if (!strcmp(p->id, prop->id))
+                       break;
+               p++;
+       }
+       if (!p->id[0]) {
+               log_errno(EINVAL, "Invalid property name %s", prop->id);
+               return 0;
+       }
+       if (!p->is_settable) {
+               log_errno(EINVAL, "Unable to set read-only property %s",
+                         prop->id);
+               return 0;
+       }
+       if (!(p->type & type)) {
+               log_errno(EINVAL, "Property name %s does not match type %d",
+                         prop->id, p->type);
+               return 0;
+       }
+
+       if (p->is_string)
+               p->value.string = prop->value.string;
+       else
+               p->value.integer = prop->value.integer;
+       if (!p->set(obj, p)) {
+               return 0;
+       }
+       return 1;
+}
+
+int lvseg_get_property(const struct lv_segment *lvseg,
+                      struct lvm_property_type *prop)
+{
+       return _get_property(lvseg, prop, SEGS);
+}
+
+int lv_get_property(const struct logical_volume *lv,
+                   struct lvm_property_type *prop)
+{
+       return _get_property(lv, prop, LVS);
+}
+
+int vg_get_property(const struct volume_group *vg,
+                   struct lvm_property_type *prop)
+{
+       return _get_property(vg, prop, VGS);
+}
+
+int pvseg_get_property(const struct pv_segment *pvseg,
+                      struct lvm_property_type *prop)
+{
+       return _get_property(pvseg, prop, PVSEGS);
+}
+
+int pv_get_property(const struct physical_volume *pv,
+                   struct lvm_property_type *prop)
+{
+       return _get_property(pv, prop, PVS | LABEL);
+}
+
+int lv_set_property(struct logical_volume *lv,
+                   struct lvm_property_type *prop)
+{
+       return _set_property(lv, prop, LVS);
+}
+
+int vg_set_property(struct volume_group *vg,
+                   struct lvm_property_type *prop)
+{
+       return _set_property(vg, prop, VGS);
+}
+
+int pv_set_property(struct physical_volume *pv,
+                   struct lvm_property_type *prop)
+{
+       return _set_property(pv, prop, PVS | LABEL);
+}
diff --git a/lib/report/properties.h b/lib/report/properties.h
new file mode 100644 (file)
index 0000000..f363362
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM_PROPERTIES_H
+#define _LVM_PROPERTIES_H
+
+#include "libdevmapper.h"
+#include "lvm-types.h"
+#include "metadata.h"
+#include "report.h"
+
+struct lvm_property_type {
+       report_type_t type;
+       const char *id;
+       unsigned is_settable:1;
+       unsigned is_string:1;
+       unsigned is_integer:1;
+       union {
+               const char *string;
+               uint64_t integer;
+       } value;
+       int (*get) (const void *obj, struct lvm_property_type *prop);
+       int (*set) (void *obj, struct lvm_property_type *prop);
+};
+
+int lvseg_get_property(const struct lv_segment *lvseg,
+                      struct lvm_property_type *prop);
+int lv_get_property(const struct logical_volume *lv,
+                   struct lvm_property_type *prop);
+int vg_get_property(const struct volume_group *vg,
+                   struct lvm_property_type *prop);
+int pvseg_get_property(const struct pv_segment *pvseg,
+                      struct lvm_property_type *prop);
+int pv_get_property(const struct physical_volume *pv,
+                   struct lvm_property_type *prop);
+int lv_set_property(struct logical_volume *lv,
+                   struct lvm_property_type *prop);
+int vg_set_property(struct volume_group *vg,
+                   struct lvm_property_type *prop);
+int pv_set_property(struct physical_volume *pv,
+                   struct lvm_property_type *prop);
+
+#endif
diff --git a/lib/report/report.c b/lib/report/report.c
new file mode 100644 (file)
index 0000000..136ad4c
--- /dev/null
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "report.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "display.h"
+#include "activate.h"
+#include "segtype.h"
+#include "str_list.h"
+#include "lvmcache.h"
+
+#include <stddef.h> /* offsetof() */
+
+struct lvm_report_object {
+       struct volume_group *vg;
+       struct logical_volume *lv;
+       struct physical_volume *pv;
+       struct lv_segment *seg;
+       struct pv_segment *pvseg;
+};
+
+static const uint64_t _minusone64 = UINT64_C(-1);
+static const int32_t _minusone32 = INT32_C(-1);
+
+/*
+ * Data-munging functions to prepare each data type for display and sorting
+ */
+static int _string_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       return dm_report_field_string(rh, field, (const char **) data);
+}
+
+static int _dev_name_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                         struct dm_report_field *field,
+                         const void *data, void *private __attribute__((unused)))
+{
+       const char *name = dev_name(*(const struct device * const *) data);
+
+       return dm_report_field_string(rh, field, &name);
+}
+
+static int _format_pvsegs(struct dm_pool *mem, struct dm_report_field *field,
+                         const void *data, int range_format)
+{
+       const struct lv_segment *seg = (const struct lv_segment *) data;
+       unsigned int s;
+       const char *name = NULL;
+       uint32_t extent = 0;
+       char extent_str[32];
+
+       if (!dm_pool_begin_object(mem, 256)) {
+               log_error("dm_pool_begin_object failed");
+               return 0;
+       }
+
+       for (s = 0; s < seg->area_count; s++) {
+               switch (seg_type(seg, s)) {
+               case AREA_LV:
+                       name = seg_lv(seg, s)->name;
+                       extent = seg_le(seg, s);
+                       break;
+               case AREA_PV:
+                       name = dev_name(seg_dev(seg, s));
+                       extent = seg_pe(seg, s);
+                       break;
+               case AREA_UNASSIGNED:
+                       name = "unassigned";
+                       extent = 0;
+               }
+
+               if (!dm_pool_grow_object(mem, name, strlen(name))) {
+                       log_error("dm_pool_grow_object failed");
+                       return 0;
+               }
+
+               if (dm_snprintf(extent_str, sizeof(extent_str),
+                               "%s%" PRIu32 "%s",
+                               range_format ? ":" : "(", extent,
+                               range_format ? "-"  : ")") < 0) {
+                       log_error("Extent number dm_snprintf failed");
+                       return 0;
+               }
+               if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
+                       log_error("dm_pool_grow_object failed");
+                       return 0;
+               }
+
+               if (range_format) {
+                       if (dm_snprintf(extent_str, sizeof(extent_str),
+                                       "%" PRIu32, extent + seg->area_len - 1) < 0) {
+                               log_error("Extent number dm_snprintf failed");
+                               return 0;
+                       }
+                       if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
+                               log_error("dm_pool_grow_object failed");
+                               return 0;
+                       }
+               }
+
+               if ((s != seg->area_count - 1) &&
+                   !dm_pool_grow_object(mem, range_format ? " " : ",", 1)) {
+                       log_error("dm_pool_grow_object failed");
+                       return 0;
+               }
+       }
+
+       if (!dm_pool_grow_object(mem, "\0", 1)) {
+               log_error("dm_pool_grow_object failed");
+               return 0;
+       }
+
+       dm_report_field_set_value(field, dm_pool_end_object(mem), NULL);
+
+       return 1;
+}
+
+static int _devices_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                        struct dm_report_field *field,
+                        const void *data, void *private __attribute__((unused)))
+{
+       return _format_pvsegs(mem, field, data, 0);
+}
+
+static int _peranges_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                         struct dm_report_field *field,
+                         const void *data, void *private __attribute__((unused)))
+{
+       return _format_pvsegs(mem, field, data, 1);
+}
+
+static int _tags_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                     struct dm_report_field *field,
+                     const void *data, void *private __attribute__((unused)))
+{
+       const struct dm_list *tags = (const struct dm_list *) data;
+       char *tags_str;
+
+       if (!(tags_str = tags_format_and_copy(mem, tags)))
+               return 0;
+
+       dm_report_field_set_value(field, tags_str, NULL);
+
+       return 1;
+}
+
+static int _modules_disp(struct dm_report *rh, struct dm_pool *mem,
+                        struct dm_report_field *field,
+                        const void *data, void *private)
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       char *modules_str;
+
+       if (!(modules_str = lv_modules_dup(mem, lv)))
+               return 0;
+
+       dm_report_field_set_value(field, modules_str, NULL);
+       return 1;
+}
+
+static int _vgfmt_disp(struct dm_report *rh, struct dm_pool *mem,
+                      struct dm_report_field *field,
+                      const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+
+       if (!vg->fid) {
+               dm_report_field_set_value(field, "", NULL);
+               return 1;
+       }
+
+       return _string_disp(rh, mem, field, &vg->fid->fmt->name, private);
+}
+
+static int _pvfmt_disp(struct dm_report *rh, struct dm_pool *mem,
+                      struct dm_report_field *field,
+                      const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+
+       if (!pv->fmt) {
+               dm_report_field_set_value(field, "", NULL);
+               return 1;
+       }
+
+       return _string_disp(rh, mem, field, &pv->fmt->name, private);
+}
+
+static int _lvkmaj_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       int major;
+
+       if ((major = lv_kernel_major(lv)) >= 0)
+               return dm_report_field_int(rh, field, &major);
+
+       return dm_report_field_int32(rh, field, &_minusone32);
+}
+
+static int _lvkmin_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       int minor;
+
+       if ((minor = lv_kernel_minor(lv)) >= 0)
+               return dm_report_field_int(rh, field, &minor);
+
+       return dm_report_field_int32(rh, field, &_minusone32);
+}
+
+static int _lvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                         struct dm_report_field *field,
+                         const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       char *repstr;
+
+       if (!(repstr = lv_attr_dup(mem, lv)))
+               return 0;
+
+       dm_report_field_set_value(field, repstr, NULL);
+       return 1;
+}
+
+static int _pvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                         struct dm_report_field *field,
+                         const void *data, void *private __attribute__((unused)))
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       char *repstr;
+
+       if (!(repstr = pv_attr_dup(mem, pv)))
+               return 0;
+
+       dm_report_field_set_value(field, repstr, NULL);
+       return 1;
+}
+
+static int _vgstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                         struct dm_report_field *field,
+                         const void *data, void *private __attribute__((unused)))
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       char *repstr;
+
+       if (!(repstr = vg_attr_dup(mem, vg)))
+               return 0;
+
+       dm_report_field_set_value(field, repstr, NULL);
+       return 1;
+}
+
+static int _segtype_disp(struct dm_report *rh __attribute__((unused)),
+                        struct dm_pool *mem __attribute__((unused)),
+                        struct dm_report_field *field,
+                        const void *data, void *private __attribute__((unused)))
+{
+       const struct lv_segment *seg = (const struct lv_segment *) data;
+
+       char *name;
+       name = lvseg_segtype_dup(seg);
+       dm_report_field_set_value(field, name, NULL);
+       return 1;
+}
+
+static int _loglv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                      struct dm_report_field *field,
+                      const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       const char *name;
+
+       if ((name = lv_mirror_log_dup(mem, lv)))
+               return dm_report_field_string(rh, field, &name);
+
+       dm_report_field_set_value(field, "", NULL);
+       return 1;
+}
+
+static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       char *repstr, *lvname;
+       size_t len;
+
+       if (lv_is_visible(lv)) {
+               repstr = lv->name;
+               return dm_report_field_string(rh, field, (const char **) &repstr);
+       }
+
+       len = strlen(lv->name) + 3;
+       if (!(repstr = dm_pool_zalloc(mem, len))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, len, "[%s]", lv->name) < 0) {
+               log_error("lvname snprintf failed");
+               return 0;
+       }
+
+       if (!(lvname = dm_pool_strdup(mem, lv->name))) {
+               log_error("dm_pool_strdup failed");
+               return 0;
+       }
+
+       dm_report_field_set_value(field, repstr, lvname);
+
+       return 1;
+}
+
+static int _lvpath_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       char *repstr;
+
+       if (!(repstr = lv_path_dup(mem, lv)))
+               return 0;
+
+       dm_report_field_set_value(field, repstr, NULL);
+
+       return 1;
+}
+
+static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+
+       if (lv_is_cow(lv))
+               return _lvname_disp(rh, mem, field, origin_from_cow(lv), private);
+
+       dm_report_field_set_value(field, "", NULL);
+       return 1;
+}
+
+static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       const char *name;
+
+       if (!(name = lv_move_pv_dup(mem, lv)))
+               dm_report_field_set_value(field, "", NULL);
+       else
+               return dm_report_field_string(rh, field, &name);
+       return 1;
+}
+
+static int _convertlv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                          struct dm_report_field *field,
+                          const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       const char *name = NULL;
+
+       name = lv_convert_lv_dup(mem, lv);
+       if (name)
+               return dm_report_field_string(rh, field, &name);
+
+       dm_report_field_set_value(field, "", NULL);
+       return 1;
+}
+
+static int _size32_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const uint32_t size = *(const uint32_t *) data;
+       const char *disp, *repstr;
+       uint64_t *sortval;
+
+       if (!*(disp = display_size_units(private, (uint64_t) size)))
+               return_0;
+
+       if (!(repstr = dm_pool_strdup(mem, disp))) {
+               log_error("dm_pool_strdup failed");
+               return 0;
+       }
+
+       if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       *sortval = (const uint64_t) size;
+
+       dm_report_field_set_value(field, repstr, sortval);
+
+       return 1;
+}
+
+static int _size64_disp(struct dm_report *rh __attribute__((unused)),
+                       struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const uint64_t size = *(const uint64_t *) data;
+       const char *disp, *repstr;
+       uint64_t *sortval;
+
+       if (!*(disp = display_size_units(private, size)))
+               return_0;
+
+       if (!(repstr = dm_pool_strdup(mem, disp))) {
+               log_error("dm_pool_strdup failed");
+               return 0;
+       }
+
+       if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       *sortval = size;
+       dm_report_field_set_value(field, repstr, sortval);
+
+       return 1;
+}
+
+static int _lvreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
+                            struct dm_report_field *field,
+                            const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+
+       if (lv->read_ahead == DM_READ_AHEAD_AUTO) {
+               dm_report_field_set_value(field, "auto", &_minusone64);
+               return 1;
+       }
+
+       return _size32_disp(rh, mem, field, &lv->read_ahead, private);
+}
+
+static int _lvkreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
+                             struct dm_report_field *field,
+                             const void *data,
+                             void *private)
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       uint32_t read_ahead;
+
+       if ((read_ahead = lv_kernel_read_ahead(lv)) == UINT32_MAX)
+               return dm_report_field_int32(rh, field, &_minusone32);
+
+       return _size32_disp(rh, mem, field, &read_ahead, private);
+}
+
+static int _vgsize_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint64_t size;
+
+       size = (uint64_t) vg_size(vg);
+
+       return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _segstart_disp(struct dm_report *rh, struct dm_pool *mem,
+                         struct dm_report_field *field,
+                         const void *data, void *private)
+{
+       const struct lv_segment *seg = (const struct lv_segment *) data;
+       uint64_t start;
+
+       start = lvseg_start(seg);
+
+       return _size64_disp(rh, mem, field, &start, private);
+}
+
+static int _segstartpe_disp(struct dm_report *rh,
+                           struct dm_pool *mem __attribute__((unused)),
+                           struct dm_report_field *field,
+                           const void *data,
+                           void *private __attribute__((unused)))
+{
+       const struct lv_segment *seg = (const struct lv_segment *) data;
+
+       return dm_report_field_uint32(rh, field, &seg->le);
+}
+
+static int _segsize_disp(struct dm_report *rh, struct dm_pool *mem,
+                        struct dm_report_field *field,
+                        const void *data, void *private)
+{
+       const struct lv_segment *seg = (const struct lv_segment *) data;
+       uint64_t size;
+
+       size = lvseg_size(seg);
+
+       return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private)
+{
+       const struct lv_segment *seg = (const struct lv_segment *) data;
+       uint64_t size;
+
+       size = lvseg_chunksize(seg);
+
+       return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _originsize_disp(struct dm_report *rh, struct dm_pool *mem,
+                           struct dm_report_field *field,
+                           const void *data, void *private)
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       uint64_t size;
+
+       size = lv_origin_size(lv);
+
+       return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _pvused_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       uint64_t used;
+
+       used = pv_used(pv);
+
+       return _size64_disp(rh, mem, field, &used, private);
+}
+
+static int _pvfree_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       uint64_t freespace;
+
+       freespace = pv_free(pv);
+
+       return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _pvsize_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       uint64_t size;
+
+       size = pv_size_field(pv);
+
+       return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _devsize_disp(struct dm_report *rh, struct dm_pool *mem,
+                        struct dm_report_field *field,
+                        const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       uint64_t size;
+
+       size = pv_dev_size(pv);
+
+       return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _vgfree_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint64_t freespace;
+
+       freespace = (uint64_t) vg_free(vg);
+
+       return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _uuid_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                     struct dm_report_field *field,
+                     const void *data, void *private __attribute__((unused)))
+{
+       char *repstr = NULL;
+
+       if (!(repstr = id_format_and_copy(mem, (struct id *)data)))
+               return_0;
+
+       dm_report_field_set_value(field, repstr, NULL);
+       return 1;
+}
+
+static int _uint32_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                       struct dm_report_field *field,
+                       const void *data, void *private __attribute__((unused)))
+{
+       return dm_report_field_uint32(rh, field, data);
+}
+
+static int _int32_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
+                      struct dm_report_field *field,
+                      const void *data, void *private __attribute__((unused)))
+{
+       return dm_report_field_int32(rh, field, data);
+}
+
+static int _pvmdas_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       uint32_t count;
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+
+       count = pv_mda_count(pv);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _pvmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
+                            struct dm_report_field *field,
+                            const void *data, void *private)
+{
+       uint32_t count;
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+
+       count = pv_mda_used_count(pv);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _vgmdas_disp(struct dm_report *rh, struct dm_pool *mem,
+                       struct dm_report_field *field,
+                       const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint32_t count;
+
+       count = vg_mda_count(vg);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _vgmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
+                            struct dm_report_field *field,
+                            const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint32_t count;
+
+       count = vg_mda_used_count(vg);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _vgmdacopies_disp(struct dm_report *rh, struct dm_pool *mem,
+                                  struct dm_report_field *field,
+                                  const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint32_t count;
+
+       count = vg_mda_copies(vg);
+
+       if (count == VGMETADATACOPIES_UNMANAGED) {
+               dm_report_field_set_value(field, "unmanaged", &_minusone64);
+               return 1;
+       }
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _pvmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       uint64_t freespace;
+
+       freespace = pv_mda_free(pv);
+
+       return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _pvmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private)
+{
+       const struct physical_volume *pv =
+           (const struct physical_volume *) data;
+       uint64_t min_mda_size;
+
+       min_mda_size = pv_mda_size(pv);
+
+       return _size64_disp(rh, mem, field, &min_mda_size, private);
+}
+
+static int _vgmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint64_t min_mda_size;
+
+       min_mda_size = vg_mda_size(vg);
+
+       return _size64_disp(rh, mem, field, &min_mda_size, private);
+}
+
+static int _vgmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint64_t freespace;
+
+       freespace = vg_mda_free(vg);
+
+       return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _lvcount_disp(struct dm_report *rh, struct dm_pool *mem,
+                        struct dm_report_field *field,
+                        const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint32_t count;
+
+       count = vg_visible_lvs(vg);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _lvsegcount_disp(struct dm_report *rh, struct dm_pool *mem,
+                           struct dm_report_field *field,
+                           const void *data, void *private)
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       uint32_t count;
+
+       count = dm_list_size(&lv->segments);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _snapcount_disp(struct dm_report *rh, struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private)
+{
+       const struct volume_group *vg = (const struct volume_group *) data;
+       uint32_t count;
+
+       count = snapshot_count(vg);
+
+       return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _snpercent_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+                          struct dm_report_field *field,
+                          const void *data, void *private __attribute__((unused)))
+{
+       const struct logical_volume *lv = (const struct logical_volume *) data;
+       struct lvinfo info;
+       percent_t snap_percent;
+       uint64_t *sortval;
+       char *repstr;
+
+       /* Suppress snapshot percentage if not using driver */
+       if (!activation()) {
+               dm_report_field_set_value(field, "", NULL);
+               return 1;
+       }
+
+       if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       if ((!lv_is_cow(lv) && !lv_is_merging_origin(lv)) ||
+           !lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) || !info.exists) {
+               *sortval = UINT64_C(0);
+               dm_report_field_set_value(field, "", sortval);
+               return 1;
+       }
+
+       if (!lv_snapshot_percent(lv, &snap_percent) ||
+                                (snap_percent == PERCENT_INVALID)) {
+               if (!lv_is_merging_origin(lv)) {
+                       *sortval = UINT64_C(100);
+                       dm_report_field_set_value(field, "100.00", sortval);
+               } else {
+                       /* onactivate merge that hasn't started yet would
+                        * otherwise display incorrect snap% in origin
+                        */
+                       *sortval = UINT64_C(0);
+                       dm_report_field_set_value(field, "", sortval);
+               }
+               return 1;
+       }
+
+       if (!(repstr = dm_pool_zalloc(mem, 8))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, 7, "%.2f", percent_to_float(snap_percent)) < 0) {
+               log_error("snapshot percentage too large");
+               return 0;
+       }
+
+       *sortval = (uint64_t)(snap_percent * 1000.f);
+       dm_report_field_set_value(field, repstr, sortval);
+
+       return 1;
+}
+
+static int _copypercent_disp(struct dm_report *rh __attribute__((unused)),
+                            struct dm_pool *mem,
+                            struct dm_report_field *field,
+                            const void *data, void *private __attribute__((unused)))
+{
+       struct logical_volume *lv = (struct logical_volume *) data;
+       percent_t percent;
+       uint64_t *sortval;
+       char *repstr;
+
+       if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       if ((!(lv->status & PVMOVE) && !(lv->status & MIRRORED)) ||
+           !lv_mirror_percent(lv->vg->cmd, lv, 0, &percent,
+                              NULL) || (percent == PERCENT_INVALID)) {
+               *sortval = UINT64_C(0);
+               dm_report_field_set_value(field, "", sortval);
+               return 1;
+       }
+
+       percent = copy_percent(lv);
+
+       if (!(repstr = dm_pool_zalloc(mem, 8))) {
+               log_error("dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, 7, "%.2f", percent_to_float(percent)) < 0) {
+               log_error("copy percentage too large");
+               return 0;
+       }
+
+       *sortval = (uint64_t)(percent * 1000.f);
+       dm_report_field_set_value(field, repstr, sortval);
+
+       return 1;
+}
+
+/* Report object types */
+
+/* necessary for displaying something for PVs not belonging to VG */
+static struct format_instance _dummy_fid = {
+       .metadata_areas_in_use = { &(_dummy_fid.metadata_areas_in_use), &(_dummy_fid.metadata_areas_in_use) },
+       .metadata_areas_ignored = { &(_dummy_fid.metadata_areas_ignored), &(_dummy_fid.metadata_areas_ignored) },
+};
+
+static struct volume_group _dummy_vg = {
+       .fid = &_dummy_fid,
+       .name = (char *) "",
+       .system_id = (char *) "",
+       .pvs = { &(_dummy_vg.pvs), &(_dummy_vg.pvs) },
+       .lvs = { &(_dummy_vg.lvs), &(_dummy_vg.lvs) },
+       .tags = { &(_dummy_vg.tags), &(_dummy_vg.tags) },
+};
+
+static void *_obj_get_vg(void *obj)
+{
+       struct volume_group *vg = ((struct lvm_report_object *)obj)->vg;
+
+       return vg ? vg : &_dummy_vg;
+}
+
+static void *_obj_get_lv(void *obj)
+{
+       return ((struct lvm_report_object *)obj)->lv;
+}
+
+static void *_obj_get_pv(void *obj)
+{
+       return ((struct lvm_report_object *)obj)->pv;
+}
+
+static void *_obj_get_seg(void *obj)
+{
+       return ((struct lvm_report_object *)obj)->seg;
+}
+
+static void *_obj_get_pvseg(void *obj)
+{
+       return ((struct lvm_report_object *)obj)->pvseg;
+}
+
+static const struct dm_report_object_type _report_types[] = {
+       { VGS, "Volume Group", "vg_", _obj_get_vg },
+       { LVS, "Logical Volume", "lv_", _obj_get_lv },
+       { PVS, "Physical Volume", "pv_", _obj_get_pv },
+       { LABEL, "Physical Volume Label", "pv_", _obj_get_pv },
+       { SEGS, "Logical Volume Segment", "seg_", _obj_get_seg },
+       { PVSEGS, "Physical Volume Segment", "pvseg_", _obj_get_pvseg },
+       { 0, "", "", NULL },
+};
+
+/*
+ * Import column definitions
+ */
+
+#define STR DM_REPORT_FIELD_TYPE_STRING
+#define NUM DM_REPORT_FIELD_TYPE_NUMBER
+#define FIELD(type, strct, sorttype, head, field, width, func, id, desc, writeable) \
+       {type, sorttype, offsetof(type_ ## strct, field), width, \
+        #id, head, &_ ## func ## _disp, desc},
+
+typedef struct physical_volume type_pv;
+typedef struct logical_volume type_lv;
+typedef struct volume_group type_vg;
+typedef struct lv_segment type_seg;
+typedef struct pv_segment type_pvseg;
+
+static const struct dm_report_field_type _fields[] = {
+#include "columns.h"
+{0, 0, 0, 0, "", "", NULL, NULL},
+};
+
+#undef STR
+#undef NUM
+#undef FIELD
+
+void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
+                 report_type_t *report_type, const char *separator,
+                 int aligned, int buffered, int headings, int field_prefixes,
+                 int quoted, int columns_as_rows)
+{
+       uint32_t report_flags = 0;
+       void *rh;
+
+       if (aligned)
+               report_flags |= DM_REPORT_OUTPUT_ALIGNED;
+
+       if (buffered)
+               report_flags |= DM_REPORT_OUTPUT_BUFFERED;
+
+       if (headings)
+               report_flags |= DM_REPORT_OUTPUT_HEADINGS;
+
+       if (field_prefixes)
+               report_flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
+
+       if (!quoted)
+               report_flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
+
+       if (columns_as_rows)
+               report_flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
+
+       rh = dm_report_init(report_type, _report_types, _fields, format,
+                           separator, report_flags, keys, cmd);
+
+       if (rh && field_prefixes)
+               dm_report_set_output_field_name_prefix(rh, "lvm2_");
+
+       return rh;
+}
+
+/*
+ * Create a row of data for an object
+ */
+int report_object(void *handle, struct volume_group *vg,
+                 struct logical_volume *lv, struct physical_volume *pv,
+                 struct lv_segment *seg, struct pv_segment *pvseg)
+{
+       struct lvm_report_object obj;
+
+       /* The two format fields might as well match. */
+       if (!vg && pv)
+               _dummy_fid.fmt = pv->fmt;
+
+       obj.vg = vg;
+       obj.lv = lv;
+       obj.pv = pv;
+       obj.seg = seg;
+       obj.pvseg = pvseg;
+
+       return dm_report_object(handle, &obj);
+}
diff --git a/lib/report/report.h b/lib/report/report.h
new file mode 100644 (file)
index 0000000..26cc2f7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_REPORT_H
+#define _LVM_REPORT_H
+
+#include "metadata-exported.h"
+
+typedef enum {
+       LVS     = 1,
+       PVS     = 2,
+       VGS     = 4,
+       SEGS    = 8,
+       PVSEGS  = 16,
+       LABEL   = 32
+} report_type_t;
+
+struct field;
+struct report_handle;
+
+typedef int (*field_report_fn) (struct report_handle * dh, struct field * field,
+                               const void *data);
+
+void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
+                 report_type_t *report_type, const char *separator,
+                 int aligned, int buffered, int headings, int field_prefixes,
+                 int quoted, int columns_as_rows);
+void report_free(void *handle);
+int report_object(void *handle, struct volume_group *vg,
+                 struct logical_volume *lv, struct physical_volume *pv,
+                 struct lv_segment *seg, struct pv_segment *pvseg);
+int report_output(void *handle);
+
+#endif
diff --git a/lib/snapshot/.exported_symbols b/lib/snapshot/.exported_symbols
new file mode 100644 (file)
index 0000000..1c92c6a
--- /dev/null
@@ -0,0 +1 @@
+init_segtype
diff --git a/lib/snapshot/Makefile.in b/lib/snapshot/Makefile.in
new file mode 100644 (file)
index 0000000..72399f3
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = snapshot.c
+
+LIB_SHARED = liblvm2snapshot.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
diff --git a/lib/snapshot/snapshot.c b/lib/snapshot/snapshot.c
new file mode 100644 (file)
index 0000000..1a98d7e
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "text_export.h"
+#include "config.h"
+#include "activate.h"
+#include "str_list.h"
+#include "defaults.h"
+
+static const char *_snap_name(const struct lv_segment *seg)
+{
+       return seg->segtype->name;
+}
+
+static const char *_snap_target_name(const struct lv_segment *seg)
+{
+       if (seg->status & MERGING)
+               return "snapshot-merge";
+
+       return _snap_name(seg);
+}
+
+static int _snap_text_import(struct lv_segment *seg, const struct config_node *sn,
+                       struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+       uint32_t chunk_size;
+       const char *org_name, *cow_name;
+       struct logical_volume *org, *cow;
+       int old_suppress, merge = 0;
+
+       if (!get_config_uint32(sn, "chunk_size", &chunk_size)) {
+               log_error("Couldn't read chunk size for snapshot.");
+               return 0;
+       }
+
+       old_suppress = log_suppress(1);
+
+       if ((cow_name = find_config_str(sn, "merging_store", NULL))) {
+               if (find_config_str(sn, "cow_store", NULL)) {
+                       log_suppress(old_suppress);
+                       log_error("Both snapshot cow and merging storage were specified.");
+                       return 0;
+               }
+               merge = 1;
+       }
+       else if (!(cow_name = find_config_str(sn, "cow_store", NULL))) {
+               log_suppress(old_suppress);
+               log_error("Snapshot cow storage not specified.");
+               return 0;
+       }
+
+       if (!(org_name = find_config_str(sn, "origin", NULL))) {
+               log_suppress(old_suppress);
+               log_error("Snapshot origin not specified.");
+               return 0;
+       }
+
+       log_suppress(old_suppress);
+
+       if (!(cow = find_lv(seg->lv->vg, cow_name))) {
+               log_error("Unknown logical volume specified for "
+                         "snapshot cow store.");
+               return 0;
+       }
+
+       if (!(org = find_lv(seg->lv->vg, org_name))) {
+               log_error("Unknown logical volume specified for "
+                         "snapshot origin.");
+               return 0;
+       }
+
+       init_snapshot_seg(seg, org, cow, chunk_size, merge);
+
+       return 1;
+}
+
+static int _snap_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+       outf(f, "chunk_size = %u", seg->chunk_size);
+       outf(f, "origin = \"%s\"", seg->origin->name);
+       if (!(seg->status & MERGING))
+               outf(f, "cow_store = \"%s\"", seg->cow->name);
+       else
+               outf(f, "merging_store = \"%s\"", seg->cow->name);
+
+       return 1;
+}
+
+static int _snap_target_status_compatible(const char *type)
+{
+       return (strcmp(type, "snapshot-merge") == 0);
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _snap_target_percent(void **target_state __attribute__((unused)),
+                               percent_t *percent,
+                               struct dm_pool *mem __attribute__((unused)),
+                               struct cmd_context *cmd __attribute__((unused)),
+                               struct lv_segment *seg __attribute__((unused)),
+                               char *params, uint64_t *total_numerator,
+                               uint64_t *total_denominator)
+{
+       uint64_t total_sectors, sectors_allocated, metadata_sectors;
+       int r;
+
+       /*
+        * snapshot target's percent format:
+        * <= 1.7.0: <sectors_allocated>/<total_sectors>
+        * >= 1.8.0: <sectors_allocated>/<total_sectors> <metadata_sectors>
+        */
+       r = sscanf(params, "%" PRIu64 "/%" PRIu64 " %" PRIu64,
+                  &sectors_allocated, &total_sectors, &metadata_sectors);
+       if (r == 2 || r == 3) {
+               *total_numerator += sectors_allocated;
+               *total_denominator += total_sectors;
+               if (r == 3 && sectors_allocated == metadata_sectors)
+                       *percent = PERCENT_0;
+               else if (sectors_allocated == total_sectors)
+                       *percent = PERCENT_100;
+               else
+                       *percent = make_percent(*total_numerator, *total_denominator);
+       } else if (!strcmp(params, "Invalid") ||
+                  !strcmp(params, "Merge failed"))
+               *percent = PERCENT_INVALID;
+       else
+               return 0;
+
+       return 1;
+}
+
+static int _snap_target_present(struct cmd_context *cmd,
+                               const struct lv_segment *seg,
+                               unsigned *attributes __attribute__((unused)))
+{
+       static int _snap_checked = 0;
+       static int _snap_merge_checked = 0;
+       static int _snap_present = 0;
+       static int _snap_merge_present = 0;
+
+       if (!_snap_checked) {
+               _snap_present = target_present(cmd, "snapshot", 1) &&
+                   target_present(cmd, "snapshot-origin", 0);
+               _snap_checked = 1;
+       }
+
+       if (!_snap_merge_checked && seg && (seg->status & MERGING)) {
+               _snap_merge_present = target_present(cmd, "snapshot-merge", 0);
+               _snap_merge_checked = 1;
+               return _snap_present && _snap_merge_present;
+       }
+
+       return _snap_present;
+}
+
+#ifdef DMEVENTD
+
+static const char *_get_snapshot_dso_path(struct cmd_context *cmd)
+{
+       return get_monitor_dso_path(cmd, find_config_tree_str(cmd, "dmeventd/snapshot_library",
+                                                             DEFAULT_DMEVENTD_SNAPSHOT_LIB));
+}
+
+/* FIXME Cache this */
+static int _target_registered(struct lv_segment *seg, int *pending)
+{
+       return target_registered_with_dmeventd(seg->lv->vg->cmd, _get_snapshot_dso_path(seg->lv->vg->cmd),
+                                              seg->cow, pending);
+}
+
+/* FIXME This gets run while suspended and performs banned operations. */
+static int _target_set_events(struct lv_segment *seg, int evmask, int set)
+{
+       /* FIXME Make timeout (10) configurable */
+       return target_register_events(seg->lv->vg->cmd, _get_snapshot_dso_path(seg->lv->vg->cmd),
+                                     seg->cow, evmask, set, 10);
+}
+
+static int _target_register_events(struct lv_segment *seg,
+                                  int events)
+{
+       return _target_set_events(seg, events, 1);
+}
+
+static int _target_unregister_events(struct lv_segment *seg,
+                                    int events)
+{
+       return _target_set_events(seg, events, 0);
+}
+
+#endif /* DMEVENTD */
+#endif
+
+static int _snap_modules_needed(struct dm_pool *mem,
+                               const struct lv_segment *seg __attribute__((unused)),
+                               struct dm_list *modules)
+{
+       if (!str_list_add(mem, modules, "snapshot")) {
+               log_error("snapshot string list allocation failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _snap_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _snapshot_ops = {
+       .name = _snap_name,
+       .target_name = _snap_target_name,
+       .text_import = _snap_text_import,
+       .text_export = _snap_text_export,
+       .target_status_compatible = _snap_target_status_compatible,
+#ifdef DEVMAPPER_SUPPORT
+       .target_percent = _snap_target_percent,
+       .target_present = _snap_target_present,
+#ifdef DMEVENTD
+       .target_monitored = _target_registered,
+       .target_monitor_events = _target_register_events,
+       .target_unmonitor_events = _target_unregister_events,
+#endif
+#endif
+       .modules_needed = _snap_modules_needed,
+       .destroy = _snap_destroy,
+};
+
+#ifdef SNAPSHOT_INTERNAL
+struct segment_type *init_snapshot_segtype(struct cmd_context *cmd)
+#else                          /* Shared */
+struct segment_type *init_segtype(struct cmd_context *cmd);
+struct segment_type *init_segtype(struct cmd_context *cmd)
+#endif
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_snapshot_ops;
+       segtype->name = "snapshot";
+       segtype->private = NULL;
+       segtype->flags = SEG_SNAPSHOT;
+
+#ifdef DMEVENTD
+       if (_get_snapshot_dso_path(cmd))
+               segtype->flags |= SEG_MONITORED;
+#endif
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/lib/striped/striped.c b/lib/striped/striped.c
new file mode 100644 (file)
index 0000000..51bf24a
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "pv_alloc.h"
+#include "metadata.h"
+
+static const char *_striped_name(const struct lv_segment *seg)
+{
+       return (seg->area_count == 1) ? "linear" : seg->segtype->name;
+}
+
+static void _striped_display(const struct lv_segment *seg)
+{
+       uint32_t s;
+
+       if (seg->area_count == 1)
+               display_stripe(seg, 0, "  ");
+       else {
+               log_print("  Stripes\t\t%u", seg->area_count);
+
+               if (seg->lv->vg->cmd->si_unit_consistency)
+                       log_print("  Stripe size\t\t%s",
+                                 display_size(seg->lv->vg->cmd,
+                                              (uint64_t) seg->stripe_size));
+               else
+                       log_print("  Stripe size\t\t%u KB",
+                                 seg->stripe_size / 2);
+
+               for (s = 0; s < seg->area_count; s++) {
+                       log_print("  Stripe %d:", s);
+                       display_stripe(seg, s, "    ");
+               }
+       }
+       log_print(" ");
+}
+
+static int _striped_text_import_area_count(const struct config_node *sn, uint32_t *area_count)
+{
+       if (!get_config_uint32(sn, "stripe_count", area_count)) {
+               log_error("Couldn't read 'stripe_count' for "
+                         "segment '%s'.", config_parent_name(sn));
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _striped_text_import(struct lv_segment *seg, const struct config_node *sn,
+                       struct dm_hash_table *pv_hash)
+{
+       const struct config_node *cn;
+
+       if ((seg->area_count != 1) &&
+           !get_config_uint32(sn, "stripe_size", &seg->stripe_size)) {
+               log_error("Couldn't read stripe_size for segment %s "
+                         "of logical volume %s.", config_parent_name(sn), seg->lv->name);
+               return 0;
+       }
+
+       if (!(cn = find_config_node(sn, "stripes"))) {
+               log_error("Couldn't find stripes array for segment %s "
+                         "of logical volume %s.", config_parent_name(sn), seg->lv->name);
+               return 0;
+       }
+
+       seg->area_len /= seg->area_count;
+
+       return text_import_areas(seg, sn, cn, pv_hash, 0);
+}
+
+static int _striped_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+
+       outf(f, "stripe_count = %u%s", seg->area_count,
+            (seg->area_count == 1) ? "\t# linear" : "");
+
+       if (seg->area_count > 1)
+               outsize(f, (uint64_t) seg->stripe_size,
+                       "stripe_size = %u", seg->stripe_size);
+
+       return out_areas(f, seg, "stripe");
+}
+
+/*
+ * Test whether two segments could be merged by the current merging code
+ */
+static int _striped_segments_compatible(struct lv_segment *first,
+                               struct lv_segment *second)
+{
+       uint32_t width;
+       unsigned s;
+
+       if ((first->area_count != second->area_count) ||
+           (first->stripe_size != second->stripe_size))
+               return 0;
+
+       for (s = 0; s < first->area_count; s++) {
+
+               /* FIXME Relax this to first area type != second area type */
+               /*       plus the additional AREA_LV checks needed */
+               if ((seg_type(first, s) != AREA_PV) ||
+                   (seg_type(second, s) != AREA_PV))
+                       return 0;
+
+               width = first->area_len;
+
+               if ((seg_pv(first, s) !=
+                    seg_pv(second, s)) ||
+                   (seg_pe(first, s) + width !=
+                    seg_pe(second, s)))
+                       return 0;
+       }
+
+       if (!str_list_lists_equal(&first->tags, &second->tags))
+               return 0;
+
+       return 1;
+}
+
+static int _striped_merge_segments(struct lv_segment *seg1, struct lv_segment *seg2)
+{
+       uint32_t s;
+
+       if (!_striped_segments_compatible(seg1, seg2))
+               return 0;
+
+       seg1->len += seg2->len;
+       seg1->area_len += seg2->area_len;
+
+       for (s = 0; s < seg1->area_count; s++)
+               if (seg_type(seg1, s) == AREA_PV)
+                       merge_pv_segments(seg_pvseg(seg1, s),
+                                         seg_pvseg(seg2, s));
+
+       return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _striped_add_target_line(struct dev_manager *dm,
+                               struct dm_pool *mem __attribute__((unused)),
+                               struct cmd_context *cmd __attribute__((unused)),
+                               void **target_state __attribute__((unused)),
+                               struct lv_segment *seg,
+                               struct dm_tree_node *node, uint64_t len,
+                               uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+       if (!seg->area_count) {
+               log_error(INTERNAL_ERROR "striped add_target_line called "
+                         "with no areas for %s.", seg->lv->name);
+               return 0;
+       }
+       if (seg->area_count == 1) {
+               if (!dm_tree_node_add_linear_target(node, len))
+                       return_0;
+       } else if (!dm_tree_node_add_striped_target(node, len,
+                                                 seg->stripe_size))
+               return_0;
+
+       return add_areas_line(dm, seg, node, 0u, seg->area_count);
+}
+
+static int _striped_target_present(struct cmd_context *cmd,
+                                  const struct lv_segment *seg __attribute__((unused)),
+                                  unsigned *attributes __attribute__((unused)))
+{
+       static int _striped_checked = 0;
+       static int _striped_present = 0;
+
+       if (!_striped_checked)
+               _striped_present = target_present(cmd, "linear", 0) &&
+                         target_present(cmd, "striped", 0);
+
+       _striped_checked = 1;
+
+       return _striped_present;
+}
+#endif
+
+static void _striped_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _striped_ops = {
+       .name = _striped_name,
+       .display = _striped_display,
+       .text_import_area_count = _striped_text_import_area_count,
+       .text_import = _striped_text_import,
+       .text_export = _striped_text_export,
+       .merge_segments = _striped_merge_segments,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _striped_add_target_line,
+       .target_present = _striped_target_present,
+#endif
+       .destroy = _striped_destroy,
+};
+
+struct segment_type *init_striped_segtype(struct cmd_context *cmd)
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_striped_ops;
+       segtype->name = "striped";
+       segtype->private = NULL;
+       segtype->flags =
+           SEG_CAN_SPLIT | SEG_AREAS_STRIPED | SEG_FORMAT1_SUPPORT;
+
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/lib/unknown/unknown.c b/lib/unknown/unknown.c
new file mode 100644 (file)
index 0000000..332ae99
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "str_list.h"
+#include "metadata.h"
+
+static const char *_unknown_name(const struct lv_segment *seg)
+{
+
+       return seg->segtype->name;
+}
+
+static int _unknown_text_import(struct lv_segment *seg, const struct config_node *sn,
+                               struct dm_hash_table *pv_hash)
+{
+       struct config_node *new, *last = NULL, *head = NULL;
+       const struct config_node *current;
+       log_verbose("importing unknown segment");
+       for (current = sn; current != NULL; current = current->sib) {
+               if (!strcmp(current->key, "type") || !strcmp(current->key, "start_extent") ||
+                   !strcmp(current->key, "tags") || !strcmp(current->key, "extent_count"))
+                       continue;
+               new = clone_config_node(seg->lv->vg->vgmem, current, 0);
+               if (!new)
+                       return_0;
+               if (last)
+                       last->sib = new;
+               if (!head)
+                       head = new;
+               last = new;
+       }
+       seg->segtype_private = head;
+       return 1;
+}
+
+static int _unknown_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+       struct config_node *cn = seg->segtype_private;
+       return out_config_node(f, cn);
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _unknown_add_target_line(struct dev_manager *dm __attribute__((unused)),
+                               struct dm_pool *mem __attribute__((unused)),
+                               struct cmd_context *cmd __attribute__((unused)),
+                               void **target_state __attribute__((unused)),
+                               struct lv_segment *seg __attribute__((unused)),
+                               struct dm_tree_node *node, uint64_t len,
+                               uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+       return dm_tree_node_add_error_target(node, len);
+}
+#endif
+
+static void _unknown_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _unknown_ops = {
+       .name = _unknown_name,
+       .text_import = _unknown_text_import,
+       .text_export = _unknown_text_export,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _unknown_add_target_line,
+#endif
+       .destroy = _unknown_destroy,
+};
+
+struct segment_type *init_unknown_segtype(struct cmd_context *cmd, const char *name)
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_unknown_ops;
+       segtype->name = dm_pool_strdup(cmd->mem, name);
+       segtype->private = NULL;
+       segtype->flags = SEG_UNKNOWN | SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/lib/uuid/uuid.c b/lib/uuid/uuid.c
new file mode 100644 (file)
index 0000000..e85e852
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "uuid.h"
+#include "lvm-wrappers.h"
+
+#include <assert.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+
+static const char _c[] =
+    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#";
+
+static int _built_inverse;
+static char _inverse_c[256];
+
+int lvid_create(union lvid *lvid, struct id *vgid)
+{
+       memcpy(lvid->id, vgid, sizeof(*lvid->id));
+       return id_create(&lvid->id[1]);
+}
+
+void uuid_from_num(char *uuid, uint32_t num)
+{
+       unsigned i;
+
+       for (i = ID_LEN; i; i--) {
+               uuid[i - 1] = _c[num % (sizeof(_c) - 1)];
+               num /= sizeof(_c) - 1;
+       }
+}
+
+int lvid_from_lvnum(union lvid *lvid, struct id *vgid, uint32_t lv_num)
+{
+       int i;
+
+       memcpy(lvid->id, vgid, sizeof(*lvid->id));
+
+       for (i = ID_LEN; i; i--) {
+               lvid->id[1].uuid[i - 1] = _c[lv_num % (sizeof(_c) - 1)];
+               lv_num /= sizeof(_c) - 1;
+       }
+
+       lvid->s[sizeof(lvid->s) - 1] = '\0';
+
+       return 1;
+}
+
+int lvnum_from_lvid(union lvid *lvid)
+{
+       int i, lv_num = 0;
+       char *c;
+
+       for (i = 0; i < ID_LEN; i++) {
+               lv_num *= sizeof(_c) - 1;
+               if ((c = strchr(_c, lvid->id[1].uuid[i])))
+                       lv_num += (int) (c - _c);
+               if (lv_num < 0)
+                       lv_num = 0;
+       }
+
+       return lv_num;
+}
+
+int lvid_in_restricted_range(union lvid *lvid)
+{
+       int i;
+
+       for (i = 0; i < ID_LEN - 3; i++)
+               if (lvid->id[1].uuid[i] != '0')
+                       return 0;
+
+       for (i = ID_LEN - 3; i < ID_LEN; i++)
+               if (!isdigit(lvid->id[1].uuid[i]))
+                       return 0;
+
+       return 1;
+}
+
+
+int id_create(struct id *id)
+{
+       unsigned i;
+       size_t len = sizeof(id->uuid);
+
+       memset(id->uuid, 0, len);
+       if (!read_urandom(&id->uuid, len)) {
+               return 0;
+       }
+
+       /*
+        * Skip out the last 2 chars in randomized creation for LVM1
+        * backwards compatibility.
+        */
+       for (i = 0; i < len; i++)
+               id->uuid[i] = _c[id->uuid[i] % (sizeof(_c) - 3)];
+
+       return 1;
+}
+
+/*
+ * The only validity check we have is that
+ * the uuid just contains characters from
+ * '_c'.  A checksum would have been nice :(
+ */
+static void _build_inverse(void)
+{
+       const char *ptr;
+
+       if (_built_inverse)
+               return;
+
+       memset(_inverse_c, 0, sizeof(_inverse_c));
+
+       for (ptr = _c; *ptr; ptr++)
+               _inverse_c[(int) *ptr] = (char) 0x1;
+}
+
+int id_valid(struct id *id)
+{
+       int i;
+
+       _build_inverse();
+
+       for (i = 0; i < ID_LEN; i++)
+               if (!_inverse_c[id->uuid[i]]) {
+                       log_error("UUID contains invalid character");
+                       return 0;
+               }
+
+       return 1;
+}
+
+int id_equal(const struct id *lhs, const struct id *rhs)
+{
+       return !memcmp(lhs->uuid, rhs->uuid, sizeof(lhs->uuid));
+}
+
+#define GROUPS (ID_LEN / 4)
+
+int id_write_format(const struct id *id, char *buffer, size_t size)
+{
+       int i, tot;
+
+       static unsigned group_size[] = { 6, 4, 4, 4, 4, 4, 6 };
+
+       assert(ID_LEN == 32);
+
+       /* split into groups separated by dashes */
+       if (size < (32 + 6 + 1)) {
+               log_error("Couldn't write uuid, buffer too small.");
+               return 0;
+       }
+
+       for (i = 0, tot = 0; i < 7; i++) {
+               memcpy(buffer, id->uuid + tot, group_size[i]);
+               buffer += group_size[i];
+               tot += group_size[i];
+               *buffer++ = '-';
+       }
+
+       *--buffer = '\0';
+       return 1;
+}
+
+int id_read_format(struct id *id, const char *buffer)
+{
+       int out = 0;
+
+       /* just strip out any dashes */
+       while (*buffer) {
+
+               if (*buffer == '-') {
+                       buffer++;
+                       continue;
+               }
+
+               if (out >= ID_LEN) {
+                       log_error("Too many characters to be uuid.");
+                       return 0;
+               }
+
+               id->uuid[out++] = *buffer++;
+       }
+
+       if (out != ID_LEN) {
+               log_error("Couldn't read uuid: incorrect number of "
+                         "characters.");
+               return 0;
+       }
+
+       return id_valid(id);
+}
+
+char *id_format_and_copy(struct dm_pool *mem, const struct id *id)
+{
+       char *repstr = NULL;
+
+       if (!(repstr = dm_pool_alloc(mem, 40))) {
+               log_error("dm_pool_alloc failed");
+               return NULL;
+       }
+
+       if (!id_write_format(id, repstr, 40))
+               return_NULL;
+
+       return repstr;
+}
diff --git a/lib/uuid/uuid.h b/lib/uuid/uuid.h
new file mode 100644 (file)
index 0000000..5c8382d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_UUID_H
+#define _LVM_UUID_H
+
+#define ID_LEN 32
+#define ID_LEN_S "32"
+
+struct id {
+       int8_t uuid[ID_LEN];
+};
+
+/*
+ * Unique logical volume identifier
+ * With format1 this is VG uuid + LV uuid + '\0' + padding
+ */
+union lvid {
+       struct id id[2];
+       char s[2 * sizeof(struct id) + 1 + 7];
+};
+
+int lvid_from_lvnum(union lvid *lvid, struct id *vgid, uint32_t lv_num);
+int lvnum_from_lvid(union lvid *lvid);
+int lvid_in_restricted_range(union lvid *lvid);
+
+void uuid_from_num(char *uuid, uint32_t num);
+
+int lvid_create(union lvid *lvid, struct id *vgid);
+int id_create(struct id *id);
+int id_valid(struct id *id);
+int id_equal(const struct id *lhs, const struct id *rhs);
+
+/*
+ * Fills 'buffer' with a more human readable form
+ * of the uuid.
+ */
+int id_write_format(const struct id *id, char *buffer, size_t size);
+
+/*
+ * Reads a formatted uuid.
+ */
+int id_read_format(struct id *id, const char *buffer);
+
+char *id_format_and_copy(struct dm_pool *mem, const struct id *id);
+
+#endif
diff --git a/lib/zero/zero.c b/lib/zero/zero.c
new file mode 100644 (file)
index 0000000..16ce66c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "metadata.h"
+
+static const char *_zero_name(const struct lv_segment *seg)
+{
+       return seg->segtype->name;
+}
+
+static int _zero_merge_segments(struct lv_segment *seg1, struct lv_segment *seg2)
+{
+       seg1->len += seg2->len;
+       seg1->area_len += seg2->area_len;
+
+       return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _zero_add_target_line(struct dev_manager *dm __attribute__((unused)),
+                                struct dm_pool *mem __attribute__((unused)),
+                                struct cmd_context *cmd __attribute__((unused)),
+                                void **target_state __attribute__((unused)),
+                                struct lv_segment *seg __attribute__((unused)),
+                                struct dm_tree_node *node,uint64_t len,
+                                uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+       return dm_tree_node_add_zero_target(node, len);
+}
+
+static int _zero_target_present(struct cmd_context *cmd,
+                               const struct lv_segment *seg __attribute__((unused)),
+                               unsigned *attributes __attribute__((unused)))
+{
+       static int _zero_checked = 0;
+       static int _zero_present = 0;
+
+       if (!_zero_checked)
+               _zero_present = target_present(cmd, "zero", 1);
+
+       _zero_checked = 1;
+
+       return _zero_present;
+}
+#endif
+
+static int _zero_modules_needed(struct dm_pool *mem,
+                               const struct lv_segment *seg __attribute__((unused)),
+                               struct dm_list *modules)
+{
+       if (!str_list_add(mem, modules, "zero")) {
+               log_error("zero module string list allocation failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _zero_destroy(struct segment_type *segtype)
+{
+       dm_free(segtype);
+}
+
+static struct segtype_handler _zero_ops = {
+       .name = _zero_name,
+       .merge_segments = _zero_merge_segments,
+#ifdef DEVMAPPER_SUPPORT
+       .add_target_line = _zero_add_target_line,
+       .target_present = _zero_target_present,
+#endif
+       .modules_needed = _zero_modules_needed,
+       .destroy = _zero_destroy,
+};
+
+struct segment_type *init_zero_segtype(struct cmd_context *cmd)
+{
+       struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+       if (!segtype)
+               return_NULL;
+
+       segtype->cmd = cmd;
+       segtype->ops = &_zero_ops;
+       segtype->name = "zero";
+       segtype->private = NULL;
+       segtype->flags = SEG_CAN_SPLIT | SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+       log_very_verbose("Initialised segtype: %s", segtype->name);
+
+       return segtype;
+}
diff --git a/libdm/.exported_symbols b/libdm/.exported_symbols
new file mode 100644 (file)
index 0000000..3a69860
--- /dev/null
@@ -0,0 +1,2 @@
+dm_log
+dm_log_with_errno
diff --git a/libdm/Makefile.in b/libdm/Makefile.in
new file mode 100644 (file)
index 0000000..70be309
--- /dev/null
@@ -0,0 +1,97 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the device-mapper userspace tools.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU Lesser General Public License v.2.1.
+#
+# 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+       datastruct/bitset.c \
+       datastruct/hash.c \
+       datastruct/list.c \
+       libdm-common.c \
+       libdm-file.c \
+       libdm-deptree.c \
+       libdm-string.c \
+       libdm-report.c \
+       mm/dbg_malloc.c \
+       mm/pool.c \
+       regex/matcher.c \
+       regex/parse_rx.c \
+       regex/ttree.c \
+       $(interface)/libdm-iface.c
+
+INCLUDES = -I$(srcdir)/$(interface) -I$(srcdir)
+
+ifeq ("@STATIC_LINK@", "yes")
+LIB_STATIC = $(interface)/libdevmapper.a
+endif
+
+LIB_SHARED = $(interface)/libdevmapper.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_DM)
+TARGETS += libdevmapper.$(LIB_SUFFIX) libdevmapper.$(LIB_SUFFIX).$(LIB_VERSION)
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = libdevmapper.cflow
+
+EXPORTED_HEADER = $(srcdir)/libdevmapper.h
+EXPORTED_FN_PREFIX = dm
+
+include $(top_builddir)/make.tmpl
+
+DEFS += -DDM_DEVICE_UID=@DM_DEVICE_UID@ -DDM_DEVICE_GID=@DM_DEVICE_GID@ \
+       -DDM_DEVICE_MODE=@DM_DEVICE_MODE@
+
+LIBS += $(SELINUX_LIBS) $(UDEV_LIBS)
+
+device-mapper: all
+
+libdevmapper.$(LIB_SUFFIX) libdevmapper.$(LIB_SUFFIX).$(LIB_VERSION): $(LIB_SHARED)
+       $(LN_S) -f $< $@
+
+.PHONY: install_dynamic install_static install_include \
+       install_ioctl install_ioctl_static \
+       install_pkgconfig
+
+INSTALL_TYPE = install_dynamic
+
+ifeq ("@STATIC_LINK@", "yes")
+  INSTALL_TYPE += install_static
+endif
+
+ifeq ("@PKGCONFIG@", "yes")
+  INSTALL_TYPE += install_pkgconfig
+endif
+
+install: $(INSTALL_TYPE) install_include
+
+install_device-mapper: install
+
+install_include: $(srcdir)/libdevmapper.h
+       $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_dynamic: install_@interface@
+
+install_static: install_@interface@_static
+
+install_ioctl: install_lib_shared
+
+install_pkgconfig: libdevmapper.pc
+       $(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper.pc
+
+install_ioctl_static: $(LIB_STATIC)
+       $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
+
+CLEAN_TARGETS += ioctl/libdevmapper.a
+DISTCLEAN_TARGETS += libdevmapper.pc .exported_symbols_generated
diff --git a/libdm/datastruct/bitset.c b/libdm/datastruct/bitset.c
new file mode 100644 (file)
index 0000000..5cd8e38
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+
+/* FIXME: calculate this. */
+#define INT_SHIFT 5
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits)
+{
+       unsigned n = (num_bits / DM_BITS_PER_INT) + 2;
+       size_t size = sizeof(int) * n;
+       dm_bitset_t bs;
+       
+       if (mem)
+               bs = dm_pool_zalloc(mem, size);
+       else
+               bs = dm_zalloc(size);
+
+       if (!bs)
+               return NULL;
+
+       *bs = num_bits;
+
+       return bs;
+}
+
+void dm_bitset_destroy(dm_bitset_t bs)
+{
+       dm_free(bs);
+}
+
+int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2)
+{
+       int i;
+
+       for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+               if (in1[i] != in2[i])
+                       return 0;
+
+       return 1;
+}
+
+void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+       int i;
+
+       for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+               out[i] = in1[i] & in2[i];
+}
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+       int i;
+       for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+               out[i] = in1[i] | in2[i];
+}
+
+static int _test_word(uint32_t test, int bit)
+{
+       uint32_t tb = test >> bit;
+
+       return (tb ? ffs(tb) + bit - 1 : -1);
+}
+
+int dm_bit_get_next(dm_bitset_t bs, int last_bit)
+{
+       int bit, word;
+       uint32_t test;
+
+       last_bit++;             /* otherwise we'll return the same bit again */
+
+       /*
+        * bs[0] holds number of bits
+        */
+       while (last_bit < (int) bs[0]) {
+               word = last_bit >> INT_SHIFT;
+               test = bs[word + 1];
+               bit = last_bit & (DM_BITS_PER_INT - 1);
+
+               if ((bit = _test_word(test, bit)) >= 0)
+                       return (word * DM_BITS_PER_INT) + bit;
+
+               last_bit = last_bit - (last_bit & (DM_BITS_PER_INT - 1)) +
+                   DM_BITS_PER_INT;
+       }
+
+       return -1;
+}
+
+int dm_bit_get_first(dm_bitset_t bs)
+{
+       return dm_bit_get_next(bs, -1);
+}
diff --git a/libdm/datastruct/hash.c b/libdm/datastruct/hash.c
new file mode 100644 (file)
index 0000000..d4543df
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+
+struct dm_hash_node {
+       struct dm_hash_node *next;
+       void *data;
+       unsigned keylen;
+       char key[0];
+};
+
+struct dm_hash_table {
+       unsigned num_nodes;
+       unsigned num_slots;
+       struct dm_hash_node **slots;
+};
+
+/* Permutation of the Integers 0 through 255 */
+static unsigned char _nums[] = {
+       1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51,
+       87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65,
+       49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28,
+       12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172,
+       144,
+       176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254,
+       178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54,
+       221,
+       102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93,
+       166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189,
+       121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185,
+       194,
+       193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232,
+       139,
+       6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112,
+       84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196,
+       43,
+       249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231,
+       71,
+       230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47,
+       109,
+       44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184,
+       163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120,
+       209
+};
+
+static struct dm_hash_node *_create_node(const char *str, unsigned len)
+{
+       struct dm_hash_node *n = dm_malloc(sizeof(*n) + len);
+
+       if (n) {
+               memcpy(n->key, str, len);
+               n->keylen = len;
+       }
+
+       return n;
+}
+
+static unsigned long _hash(const char *str, unsigned len)
+{
+       unsigned long h = 0, g;
+       unsigned i;
+
+       for (i = 0; i < len; i++) {
+               h <<= 4;
+               h += _nums[(unsigned char) *str++];
+               g = h & ((unsigned long) 0xf << 16u);
+               if (g) {
+                       h ^= g >> 16u;
+                       h ^= g >> 5u;
+               }
+       }
+
+       return h;
+}
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint)
+{
+       size_t len;
+       unsigned new_size = 16u;
+       struct dm_hash_table *hc = dm_zalloc(sizeof(*hc));
+
+       if (!hc)
+               return_0;
+
+       /* round size hint up to a power of two */
+       while (new_size < size_hint)
+               new_size = new_size << 1;
+
+       hc->num_slots = new_size;
+       len = sizeof(*(hc->slots)) * new_size;
+       if (!(hc->slots = dm_malloc(len))) {
+               stack;
+               goto bad;
+       }
+       memset(hc->slots, 0, len);
+       return hc;
+
+      bad:
+       dm_free(hc->slots);
+       dm_free(hc);
+       return 0;
+}
+
+static void _free_nodes(struct dm_hash_table *t)
+{
+       struct dm_hash_node *c, *n;
+       unsigned i;
+
+       for (i = 0; i < t->num_slots; i++)
+               for (c = t->slots[i]; c; c = n) {
+                       n = c->next;
+                       dm_free(c);
+               }
+}
+
+void dm_hash_destroy(struct dm_hash_table *t)
+{
+       _free_nodes(t);
+       dm_free(t->slots);
+       dm_free(t);
+}
+
+static struct dm_hash_node **_find(struct dm_hash_table *t, const char *key,
+                                  uint32_t len)
+{
+       unsigned h = _hash(key, len) & (t->num_slots - 1);
+       struct dm_hash_node **c;
+
+       for (c = &t->slots[h]; *c; c = &((*c)->next)) {
+               if ((*c)->keylen != len)
+                       continue;
+
+               if (!memcmp(key, (*c)->key, len))
+                       break;
+       }
+
+       return c;
+}
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *key,
+                        uint32_t len)
+{
+       struct dm_hash_node **c = _find(t, key, len);
+
+       return *c ? (*c)->data : 0;
+}
+
+int dm_hash_insert_binary(struct dm_hash_table *t, const char *key,
+                         uint32_t len, void *data)
+{
+       struct dm_hash_node **c = _find(t, key, len);
+
+       if (*c)
+               (*c)->data = data;
+       else {
+               struct dm_hash_node *n = _create_node(key, len);
+
+               if (!n)
+                       return 0;
+
+               n->data = data;
+               n->next = 0;
+               *c = n;
+               t->num_nodes++;
+       }
+
+       return 1;
+}
+
+void dm_hash_remove_binary(struct dm_hash_table *t, const char *key,
+                       uint32_t len)
+{
+       struct dm_hash_node **c = _find(t, key, len);
+
+       if (*c) {
+               struct dm_hash_node *old = *c;
+               *c = (*c)->next;
+               dm_free(old);
+               t->num_nodes--;
+       }
+}
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key)
+{
+       return dm_hash_lookup_binary(t, key, strlen(key) + 1);
+}
+
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data)
+{
+       return dm_hash_insert_binary(t, key, strlen(key) + 1, data);
+}
+
+void dm_hash_remove(struct dm_hash_table *t, const char *key)
+{
+       dm_hash_remove_binary(t, key, strlen(key) + 1);
+}
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t)
+{
+       return t->num_nodes;
+}
+
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f)
+{
+       struct dm_hash_node *c, *n;
+       unsigned i;
+
+       for (i = 0; i < t->num_slots; i++)
+               for (c = t->slots[i]; c; c = n) {
+                       n = c->next;
+                       f(c->data);
+               }
+}
+
+void dm_hash_wipe(struct dm_hash_table *t)
+{
+       _free_nodes(t);
+       memset(t->slots, 0, sizeof(struct dm_hash_node *) * t->num_slots);
+       t->num_nodes = 0u;
+}
+
+char *dm_hash_get_key(struct dm_hash_table *t __attribute__((unused)),
+                     struct dm_hash_node *n)
+{
+       return n->key;
+}
+
+void *dm_hash_get_data(struct dm_hash_table *t __attribute__((unused)),
+                      struct dm_hash_node *n)
+{
+       return n->data;
+}
+
+static struct dm_hash_node *_next_slot(struct dm_hash_table *t, unsigned s)
+{
+       struct dm_hash_node *c = NULL;
+       unsigned i;
+
+       for (i = s; i < t->num_slots && !c; i++)
+               c = t->slots[i];
+
+       return c;
+}
+
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t)
+{
+       return _next_slot(t, 0);
+}
+
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n)
+{
+       unsigned h = _hash(n->key, n->keylen) & (t->num_slots - 1);
+
+       return n->next ? n->next : _next_slot(t, h + 1);
+}
diff --git a/libdm/datastruct/list.c b/libdm/datastruct/list.c
new file mode 100644 (file)
index 0000000..c7a052f
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include <assert.h>
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+void dm_list_init(struct dm_list *head)
+{
+       head->n = head->p = head;
+}
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem)
+{
+       assert(head->n);
+
+       elem->n = head;
+       elem->p = head->p;
+
+       head->p->n = elem;
+       head->p = elem;
+}
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem)
+{
+       assert(head->n);
+
+       elem->n = head->n;
+       elem->p = head;
+
+       head->n->p = elem;
+       head->n = elem;
+}
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem)
+{
+       elem->n->p = elem->p;
+       elem->p->n = elem->n;
+}
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem)
+{
+        dm_list_del(elem);
+        dm_list_add(head, elem);
+}
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head)
+{
+       return head->n == head;
+}
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem)
+{
+       return elem->p == head;
+}
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem)
+{
+       return elem->n == head;
+}
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head)
+{
+       return (dm_list_empty(head) ? NULL : head->n);
+}
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head)
+{
+       return (dm_list_empty(head) ? NULL : head->p);
+}
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem)
+{
+       return (dm_list_start(head, elem) ? NULL : elem->p);
+}
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem)
+{
+       return (dm_list_end(head, elem) ? NULL : elem->n);
+}
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head)
+{
+       unsigned int s = 0;
+       const struct dm_list *v;
+
+       dm_list_iterate(v, head)
+           s++;
+
+       return s;
+}
+
+/*
+ * Join two lists together.
+ * This moves all the elements of the list 'head1' to the end of the list
+ * 'head', leaving 'head1' empty.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1)
+{
+       assert(head->n);
+       assert(head1->n);
+
+       if (dm_list_empty(head1))
+           return;
+
+       head1->p->n = head;
+       head1->n->p = head->p;
+
+       head->p->n = head1->n;
+       head->p = head1->p;
+
+       dm_list_init(head1);
+}
diff --git a/libdm/ioctl/libdm-compat.h b/libdm/ioctl/libdm-compat.h
new file mode 100644 (file)
index 0000000..5ba63ce
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LINUX_LIBDM_COMPAT_H
+#define _LINUX_LIBDM_COMPAT_H
+
+#include "kdev_t.h"
+#include "dm-ioctl.h"
+#include <inttypes.h>
+#include <sys/ioctl.h>
+
+struct dm_task;
+struct dm_info;
+
+/*
+ * Old versions of structures for backwards compatibility.
+ */
+
+struct dm_ioctl_v1 {
+       uint32_t version[3];    /* in/out */
+       uint32_t data_size;     /* total size of data passed in
+                                * including this struct */
+
+       uint32_t data_start;    /* offset to start of data
+                                * relative to start of this struct */
+
+       int32_t target_count;   /* in/out */
+       int32_t open_count;     /* out */
+       uint32_t flags;         /* in/out */
+
+       __kernel_dev_t dev;     /* in/out */
+
+       char name[DM_NAME_LEN]; /* device name */
+       char uuid[DM_UUID_LEN]; /* unique identifier for
+                                * the block device */
+};
+
+struct dm_target_spec_v1 {
+       int32_t status;         /* used when reading from kernel only */
+       uint64_t sector_start;
+       uint32_t length;
+       uint32_t next;
+
+       char target_type[DM_MAX_TYPE_NAME];
+
+};
+
+struct dm_target_deps_v1 {
+       uint32_t count;
+
+       __kernel_dev_t dev[0];  /* out */
+};
+
+enum {
+       /* Top level cmds */
+       DM_VERSION_CMD_V1 = 0,
+       DM_REMOVE_ALL_CMD_V1,
+
+       /* device level cmds */
+       DM_DEV_CREATE_CMD_V1,
+       DM_DEV_REMOVE_CMD_V1,
+       DM_DEV_RELOAD_CMD_V1,
+       DM_DEV_RENAME_CMD_V1,
+       DM_DEV_SUSPEND_CMD_V1,
+       DM_DEV_DEPS_CMD_V1,
+       DM_DEV_STATUS_CMD_V1,
+
+       /* target level cmds */
+       DM_TARGET_STATUS_CMD_V1,
+       DM_TARGET_WAIT_CMD_V1,
+};
+
+#define DM_VERSION_V1       _IOWR(DM_IOCTL, DM_VERSION_CMD_V1, struct dm_ioctl)
+#define DM_REMOVE_ALL_V1    _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD_V1, struct dm_ioctl)
+
+#define DM_DEV_CREATE_V1    _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD_V1, struct dm_ioctl)
+#define DM_DEV_REMOVE_V1    _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD_V1, struct dm_ioctl)
+#define DM_DEV_RELOAD_V1    _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD_V1, struct dm_ioctl)
+#define DM_DEV_SUSPEND_V1   _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD_V1, struct dm_ioctl)
+#define DM_DEV_RENAME_V1    _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD_V1, struct dm_ioctl)
+#define DM_DEV_DEPS_V1      _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD_V1, struct dm_ioctl)
+#define DM_DEV_STATUS_V1    _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD_V1, struct dm_ioctl)
+
+#define DM_TARGET_STATUS_V1 _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD_V1, struct dm_ioctl)
+#define DM_TARGET_WAIT_V1   _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD_V1, struct dm_ioctl)
+
+/* *INDENT-OFF* */
+static struct cmd_data _cmd_data_v1[] = {
+       { "create",     DM_DEV_CREATE_V1,       {1, 0, 0} },
+       { "reload",     DM_DEV_RELOAD_V1,       {1, 0, 0} },
+       { "remove",     DM_DEV_REMOVE_V1,       {1, 0, 0} },
+       { "remove_all", DM_REMOVE_ALL_V1,       {1, 0, 0} },
+       { "suspend",    DM_DEV_SUSPEND_V1,      {1, 0, 0} },
+       { "resume",     DM_DEV_SUSPEND_V1,      {1, 0, 0} },
+       { "info",       DM_DEV_STATUS_V1,       {1, 0, 0} },
+       { "deps",       DM_DEV_DEPS_V1,         {1, 0, 0} },
+       { "rename",     DM_DEV_RENAME_V1,       {1, 0, 0} },
+       { "version",    DM_VERSION_V1,          {1, 0, 0} },
+       { "status",     DM_TARGET_STATUS_V1,    {1, 0, 0} },
+       { "table",      DM_TARGET_STATUS_V1,    {1, 0, 0} },
+       { "waitevent",  DM_TARGET_WAIT_V1,      {1, 0, 0} },
+       { "names",      0,                      {4, 0, 0} },
+       { "clear",      0,                      {4, 0, 0} },
+       { "mknodes",    0,                      {4, 0, 0} },
+       { "versions",   0,                      {4, 1, 0} },
+       { "message",    0,                      {4, 2, 0} },
+       { "setgeometry",0,                      {4, 6, 0} },
+};
+/* *INDENT-ON* */
+
+#endif
diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c
new file mode 100644 (file)
index 0000000..4a8dfc6
--- /dev/null
@@ -0,0 +1,2141 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+
+#ifdef DM_COMPAT
+#  include "libdm-compat.h"
+#endif
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <limits.h>
+
+#ifdef linux
+#  include "kdev_t.h"
+#  include <linux/limits.h>
+#else
+#  define MAJOR(x) major((x))
+#  define MINOR(x) minor((x))
+#  define MKDEV(x,y) makedev((x),(y))
+#endif
+
+#include "dm-ioctl.h"
+
+/*
+ * Ensure build compatibility.  
+ * The hard-coded versions here are the highest present 
+ * in the _cmd_data arrays.
+ */
+
+#if !((DM_VERSION_MAJOR == 1 && DM_VERSION_MINOR >= 0) || \
+      (DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 0))
+#error The version of dm-ioctl.h included is incompatible.
+#endif
+
+/* FIXME This should be exported in device-mapper.h */
+#define DM_NAME "device-mapper"
+
+#define PROC_MISC "/proc/misc"
+#define PROC_DEVICES "/proc/devices"
+#define MISC_NAME "misc"
+
+#define NUMBER_OF_MAJORS 4096
+
+/*
+ * Static minor number assigned since kernel version 2.6.36.
+ * The original definition is in kernel's include/linux/miscdevice.h.
+ * This number is also visible in modules.devname exported by depmod
+ * utility (support included in module-init-tools version >= 3.12).
+ */
+#define MAPPER_CTRL_MINOR 236
+#define MISC_MAJOR 10
+
+/* dm major version no for running kernel */
+static unsigned _dm_version = DM_VERSION_MAJOR;
+static unsigned _dm_version_minor = 0;
+static unsigned _dm_version_patchlevel = 0;
+static int _log_suppress = 0;
+
+static int _kernel_major;
+static int _kernel_minor;
+static int _kernel_release;
+
+/*
+ * If the kernel dm driver only supports one major number
+ * we store it in _dm_device_major.  Otherwise we indicate
+ * which major numbers have been claimed by device-mapper
+ * in _dm_bitset.
+ */
+static unsigned _dm_multiple_major_support = 1;
+static dm_bitset_t _dm_bitset = NULL;
+static uint32_t _dm_device_major = 0;
+
+static int _control_fd = -1;
+static int _version_checked = 0;
+static int _version_ok = 1;
+static unsigned _ioctl_buffer_double_factor = 0;
+
+/*
+ * Support both old and new major numbers to ease the transition.
+ * Clumsy, but only temporary.
+ */
+#if DM_VERSION_MAJOR == 4 && defined(DM_COMPAT)
+const int _dm_compat = 1;
+#else
+const int _dm_compat = 0;
+#endif
+
+
+/* *INDENT-OFF* */
+static struct cmd_data _cmd_data_v4[] = {
+       {"create",      DM_DEV_CREATE,          {4, 0, 0}},
+       {"reload",      DM_TABLE_LOAD,          {4, 0, 0}},
+       {"remove",      DM_DEV_REMOVE,          {4, 0, 0}},
+       {"remove_all",  DM_REMOVE_ALL,          {4, 0, 0}},
+       {"suspend",     DM_DEV_SUSPEND,         {4, 0, 0}},
+       {"resume",      DM_DEV_SUSPEND,         {4, 0, 0}},
+       {"info",        DM_DEV_STATUS,          {4, 0, 0}},
+       {"deps",        DM_TABLE_DEPS,          {4, 0, 0}},
+       {"rename",      DM_DEV_RENAME,          {4, 0, 0}},
+       {"version",     DM_VERSION,             {4, 0, 0}},
+       {"status",      DM_TABLE_STATUS,        {4, 0, 0}},
+       {"table",       DM_TABLE_STATUS,        {4, 0, 0}},
+       {"waitevent",   DM_DEV_WAIT,            {4, 0, 0}},
+       {"names",       DM_LIST_DEVICES,        {4, 0, 0}},
+       {"clear",       DM_TABLE_CLEAR,         {4, 0, 0}},
+       {"mknodes",     DM_DEV_STATUS,          {4, 0, 0}},
+#ifdef DM_LIST_VERSIONS
+       {"versions",    DM_LIST_VERSIONS,       {4, 1, 0}},
+#endif
+#ifdef DM_TARGET_MSG
+       {"message",     DM_TARGET_MSG,          {4, 2, 0}},
+#endif
+#ifdef DM_DEV_SET_GEOMETRY
+       {"setgeometry", DM_DEV_SET_GEOMETRY,    {4, 6, 0}},
+#endif
+};
+/* *INDENT-ON* */
+
+#define ALIGNMENT_V1 sizeof(int)
+#define ALIGNMENT 8
+
+/* FIXME Rejig library to record & use errno instead */
+#ifndef DM_EXISTS_FLAG
+#  define DM_EXISTS_FLAG 0x00000004
+#endif
+
+static void *_align(void *ptr, unsigned int a)
+{
+       register unsigned long agn = --a;
+
+       return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
+static int _uname(void)
+{
+       static int _uts_set = 0;
+       struct utsname _uts;
+
+       if (_uts_set)
+               return 1;
+
+       if (uname(&_uts)) {
+               log_error("uname failed: %s", strerror(errno));
+               return 0;
+       }
+       if (sscanf(_uts.release, "%d.%d.%d",
+                       &_kernel_major,
+                       &_kernel_minor,
+                       &_kernel_release) != 3) {
+               log_error("Could not determine kernel version used.");
+               return 0;
+       }
+
+       _uts_set = 1;
+       return 1;
+}
+
+#ifdef DM_IOCTLS
+/*
+ * Set number to NULL to populate _dm_bitset - otherwise first
+ * match is returned.
+ */
+static int _get_proc_number(const char *file, const char *name,
+                           uint32_t *number)
+{
+       FILE *fl;
+       char nm[256];
+       int c;
+       uint32_t num;
+
+       if (!(fl = fopen(file, "r"))) {
+               log_sys_error("fopen", file);
+               return 0;
+       }
+
+       while (!feof(fl)) {
+               if (fscanf(fl, "%d %255s\n", &num, &nm[0]) == 2) {
+                       if (!strcmp(name, nm)) {
+                               if (number) {
+                                       *number = num;
+                                       if (fclose(fl))
+                                               log_sys_error("fclose", file);
+                                       return 1;
+                               }
+                               dm_bit_set(_dm_bitset, num);
+                       }
+               } else do {
+                       c = fgetc(fl);
+               } while (c != EOF && c != '\n');
+       }
+       if (fclose(fl))
+               log_sys_error("fclose", file);
+
+       if (number) {
+               log_error("%s: No entry for %s found", file, name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _control_device_number(uint32_t *major, uint32_t *minor)
+{
+       if (!_get_proc_number(PROC_DEVICES, MISC_NAME, major) ||
+           !_get_proc_number(PROC_MISC, DM_NAME, minor)) {
+               *major = 0;
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Returns 1 if exists; 0 if it doesn't; -1 if it's wrong
+ */
+static int _control_exists(const char *control, uint32_t major, uint32_t minor)
+{
+       struct stat buf;
+
+       if (stat(control, &buf) < 0) {
+               if (errno != ENOENT)
+                       log_sys_error("stat", control);
+               return 0;
+       }
+
+       if (!S_ISCHR(buf.st_mode)) {
+               log_verbose("%s: Wrong inode type", control);
+               if (!unlink(control))
+                       return 0;
+               log_sys_error("unlink", control);
+               return -1;
+       }
+
+       if (major && buf.st_rdev != MKDEV(major, minor)) {
+               log_verbose("%s: Wrong device number: (%u, %u) instead of "
+                           "(%u, %u)", control,
+                           MAJOR(buf.st_mode), MINOR(buf.st_mode),
+                           major, minor);
+               if (!unlink(control))
+                       return 0;
+               log_sys_error("unlink", control);
+               return -1;
+       }
+
+       return 1;
+}
+
+static int _create_control(const char *control, uint32_t major, uint32_t minor)
+{
+       int ret;
+       mode_t old_umask;
+
+       if (!major)
+               return 0;
+
+       (void) dm_prepare_selinux_context(dm_dir(), S_IFDIR);
+       old_umask = umask(DM_DEV_DIR_UMASK);
+       ret = dm_create_dir(dm_dir());
+       umask(old_umask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       if (!ret)
+               return 0;
+
+       log_verbose("Creating device %s (%u, %u)", control, major, minor);
+
+       (void) dm_prepare_selinux_context(control, S_IFCHR);
+       if (mknod(control, S_IFCHR | S_IRUSR | S_IWUSR,
+                 MKDEV(major, minor)) < 0)  {
+               log_sys_error("mknod", control);
+               (void) dm_prepare_selinux_context(NULL, 0);
+               return 0;
+       }
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       return 1;
+}
+#endif
+
+/*
+ * FIXME Update bitset in long-running process if dm claims new major numbers.
+ */
+static int _create_dm_bitset(void)
+{
+#ifdef DM_IOCTLS
+       if (_dm_bitset || _dm_device_major)
+               return 1;
+
+       if (!_uname())
+               return 0;
+
+       /*
+        * 2.6 kernels are limited to one major number.
+        * Assume 2.4 kernels are patched not to.
+        * FIXME Check _dm_version and _dm_version_minor if 2.6 changes this.
+        */
+       if (KERNEL_VERSION(_kernel_major, _kernel_minor, _kernel_release) >=
+           KERNEL_VERSION(2, 6, 0))
+               _dm_multiple_major_support = 0;
+
+       if (!_dm_multiple_major_support) {
+               if (!_get_proc_number(PROC_DEVICES, DM_NAME, &_dm_device_major))
+                       return 0;
+               return 1;
+       }
+
+       /* Multiple major numbers supported */
+       if (!(_dm_bitset = dm_bitset_create(NULL, NUMBER_OF_MAJORS)))
+               return 0;
+
+       if (!_get_proc_number(PROC_DEVICES, DM_NAME, NULL)) {
+               dm_bitset_destroy(_dm_bitset);
+               _dm_bitset = NULL;
+               return 0;
+       }
+
+       return 1;
+#else
+       return 0;
+#endif
+}
+
+int dm_is_dm_major(uint32_t major)
+{
+       if (!_create_dm_bitset())
+               return 0;
+
+       if (_dm_multiple_major_support)
+               return dm_bit(_dm_bitset, major) ? 1 : 0;
+       else
+               return (major == _dm_device_major) ? 1 : 0;
+}
+
+static void _close_control_fd(void)
+{
+       if (_control_fd != -1) {
+               if (close(_control_fd) < 0)
+                       log_sys_error("close", "_control_fd");
+               _control_fd = -1;
+       }
+}
+
+static int _open_and_assign_control_fd(const char *control,
+                                      int ignore_nodev)
+{
+       _close_control_fd();
+
+       if ((_control_fd = open(control, O_RDWR)) < 0) {
+               if (!(ignore_nodev && errno == ENODEV))
+                       log_sys_error("open", control);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _open_control(void)
+{
+#ifdef DM_IOCTLS
+       char control[PATH_MAX];
+       uint32_t major = 0, minor;
+       int dm_mod_autoload_support, needs_open;
+
+       if (_control_fd != -1)
+               return 1;
+
+       if (!_uname())
+               return 0;
+
+       snprintf(control, sizeof(control), "%s/%s", dm_dir(), DM_CONTROL_NODE);
+
+       /*
+        * dm-mod autoloading is supported since kernel 2.6.36.
+        * Udev daemon will try to read modules.devname file extracted
+        * by depmod and create any static nodes needed.
+        * The /dev/mapper/control node can be created and prepared this way.
+        * First access to such node should load dm-mod module automatically.
+        */
+       dm_mod_autoload_support = KERNEL_VERSION(_kernel_major, _kernel_minor,
+                                 _kernel_release) >= KERNEL_VERSION(2, 6, 36);
+
+       /*
+        *  If dm-mod autoloading is supported and the control node exists
+        *  already try to open it now. This should autoload dm-mod module.
+        */
+       if (dm_mod_autoload_support) {
+               if (!_get_proc_number(PROC_DEVICES, MISC_NAME, &major))
+                       /* If major not found, just fallback to hardcoded value. */
+                       major = MISC_MAJOR;
+
+               /* Recreate the node with correct major and minor if needed. */
+               if (!_control_exists(control, major, MAPPER_CTRL_MINOR) &&
+                   !_create_control(control, major, MAPPER_CTRL_MINOR))
+                       goto error;
+
+               _open_and_assign_control_fd(control, 1);
+       }
+
+       /*
+        * Get major and minor number assigned for the control node.
+        * In case we make use of the module autoload support, this
+        * information should be accessible now as well.
+        */
+       if (!_control_device_number(&major, &minor))
+               log_error("Is device-mapper driver missing from kernel?");
+
+       /*
+        * Check the control node and its major and minor number.
+        * If there's anything wrong, remove the old node and create
+        * a correct one.
+        */
+       if ((needs_open = !_control_exists(control, major, minor)) &&
+           !_create_control(control, major, minor)) {
+               _close_control_fd();
+               goto error;
+       }
+
+       /*
+        * For older kernels without dm-mod autoloading support, we always
+        * need to open the control node here - we still haven't done that!
+        * For newer kernels with dm-mod autoloading, we open it only if the
+        * node was recreated and corrected in previous step.
+        */
+       if ((!dm_mod_autoload_support || needs_open) &&
+            !_open_and_assign_control_fd(control, 0))
+               goto error;
+
+       if (!_create_dm_bitset()) {
+               log_error("Failed to set up list of device-mapper major numbers");
+               return 0;
+       }
+
+       return 1;
+
+error:
+       log_error("Failure to communicate with kernel device-mapper driver.");
+       return 0;
+#else
+       return 1;
+#endif
+}
+
+static void _dm_zfree_string(char *string)
+{
+       if (string) {
+               memset(string, 0, strlen(string));
+               dm_free(string);
+       }
+}
+
+static void _dm_zfree_dmi(struct dm_ioctl *dmi)
+{
+       if (dmi) {
+               memset(dmi, 0, dmi->data_size);
+               dm_free(dmi);
+       }
+}
+
+void dm_task_destroy(struct dm_task *dmt)
+{
+       struct target *t, *n;
+
+       for (t = dmt->head; t; t = n) {
+               n = t->next;
+               _dm_zfree_string(t->params);
+               dm_free(t->type);
+               dm_free(t);
+       }
+
+       if (dmt->dev_name)
+               dm_free(dmt->dev_name);
+
+       if (dmt->newname)
+               dm_free(dmt->newname);
+
+       if (dmt->message)
+               dm_free(dmt->message);
+
+       _dm_zfree_dmi(dmt->dmi.v4);
+
+       if (dmt->uuid)
+               dm_free(dmt->uuid);
+
+       dm_free(dmt);
+}
+
+/*
+ * Protocol Version 1 compatibility functions.
+ */
+
+#ifdef DM_COMPAT
+
+static void _dm_zfree_dmi_v1(struct dm_ioctl_v1 *dmi)
+{
+       if (dmi) {
+               memset(dmi, 0, dmi->data_size);
+               dm_free(dmi);
+       }
+}
+
+static int _dm_task_get_driver_version_v1(struct dm_task *dmt, char *version,
+                                         size_t size)
+{
+       unsigned int *v;
+
+       if (!dmt->dmi.v1) {
+               version[0] = '\0';
+               return 0;
+       }
+
+       v = dmt->dmi.v1->version;
+       snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
+       return 1;
+}
+
+/* Unmarshall the target info returned from a status call */
+static int _unmarshal_status_v1(struct dm_task *dmt, struct dm_ioctl_v1 *dmi)
+{
+       char *outbuf = (char *) dmi + dmi->data_start;
+       char *outptr = outbuf;
+       int32_t i;
+       struct dm_target_spec_v1 *spec;
+
+       for (i = 0; i < dmi->target_count; i++) {
+               spec = (struct dm_target_spec_v1 *) outptr;
+
+               if (!dm_task_add_target(dmt, spec->sector_start,
+                                       (uint64_t) spec->length,
+                                       spec->target_type,
+                                       outptr + sizeof(*spec))) {
+                       return 0;
+               }
+
+               outptr = outbuf + spec->next;
+       }
+
+       return 1;
+}
+
+static int _dm_format_dev_v1(char *buf, int bufsize, uint32_t dev_major,
+                            uint32_t dev_minor)
+{
+       int r;
+
+       if (bufsize < 8)
+               return 0;
+
+       r = snprintf(buf, bufsize, "%03x:%03x", dev_major, dev_minor);
+       if (r < 0 || r > bufsize - 1)
+               return 0;
+
+       return 1;
+}
+
+static int _dm_task_get_info_v1(struct dm_task *dmt, struct dm_info *info)
+{
+       if (!dmt->dmi.v1)
+               return 0;
+
+       memset(info, 0, sizeof(*info));
+
+       info->exists = dmt->dmi.v1->flags & DM_EXISTS_FLAG ? 1 : 0;
+       if (!info->exists)
+               return 1;
+
+       info->suspended = dmt->dmi.v1->flags & DM_SUSPEND_FLAG ? 1 : 0;
+       info->read_only = dmt->dmi.v1->flags & DM_READONLY_FLAG ? 1 : 0;
+       info->target_count = dmt->dmi.v1->target_count;
+       info->open_count = dmt->dmi.v1->open_count;
+       info->event_nr = 0;
+       info->major = MAJOR(dmt->dmi.v1->dev);
+       info->minor = MINOR(dmt->dmi.v1->dev);
+       info->live_table = 1;
+       info->inactive_table = 0;
+
+       return 1;
+}
+
+static const char *_dm_task_get_name_v1(const struct dm_task *dmt)
+{
+       return (dmt->dmi.v1->name);
+}
+
+static const char *_dm_task_get_uuid_v1(const struct dm_task *dmt)
+{
+       return (dmt->dmi.v1->uuid);
+}
+
+static struct dm_deps *_dm_task_get_deps_v1(struct dm_task *dmt)
+{
+       log_error("deps version 1 no longer supported by libdevmapper");
+       return NULL;
+}
+
+static struct dm_names *_dm_task_get_names_v1(struct dm_task *dmt)
+{
+       return (struct dm_names *) (((void *) dmt->dmi.v1) +
+                                   dmt->dmi.v1->data_start);
+}
+
+static void *_add_target_v1(struct target *t, void *out, void *end)
+{
+       void *out_sp = out;
+       struct dm_target_spec_v1 sp;
+       size_t sp_size = sizeof(struct dm_target_spec_v1);
+       int len;
+
+       out += sp_size;
+       if (out >= end)
+               return_NULL;
+
+       sp.status = 0;
+       sp.sector_start = t->start;
+       sp.length = t->length;
+       strncpy(sp.target_type, t->type, sizeof(sp.target_type));
+
+       len = strlen(t->params);
+
+       if ((out + len + 1) >= end)
+               return_NULL;
+
+       strcpy((char *) out, t->params);
+       out += len + 1;
+
+       /* align next block */
+       out = _align(out, ALIGNMENT_V1);
+
+       sp.next = out - out_sp;
+
+       memcpy(out_sp, &sp, sp_size);
+
+       return out;
+}
+
+static struct dm_ioctl_v1 *_flatten_v1(struct dm_task *dmt)
+{
+       const size_t min_size = 16 * 1024;
+       const int (*version)[3];
+
+       struct dm_ioctl_v1 *dmi;
+       struct target *t;
+       size_t len = sizeof(struct dm_ioctl_v1);
+       void *b, *e;
+       int count = 0;
+
+       for (t = dmt->head; t; t = t->next) {
+               len += sizeof(struct dm_target_spec_v1);
+               len += strlen(t->params) + 1 + ALIGNMENT_V1;
+               count++;
+       }
+
+       if (count && dmt->newname) {
+               log_error("targets and newname are incompatible");
+               return NULL;
+       }
+
+       if (dmt->newname)
+               len += strlen(dmt->newname) + 1;
+
+       /*
+        * Give len a minimum size so that we have space to store
+        * dependencies or status information.
+        */
+       if (len < min_size)
+               len = min_size;
+
+       if (!(dmi = dm_malloc(len)))
+               return NULL;
+
+       memset(dmi, 0, len);
+
+       version = &_cmd_data_v1[dmt->type].version;
+
+       dmi->version[0] = (*version)[0];
+       dmi->version[1] = (*version)[1];
+       dmi->version[2] = (*version)[2];
+
+       dmi->data_size = len;
+       dmi->data_start = sizeof(struct dm_ioctl_v1);
+
+       if (dmt->dev_name)
+               strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name));
+
+       if (dmt->type == DM_DEVICE_SUSPEND)
+               dmi->flags |= DM_SUSPEND_FLAG;
+       if (dmt->read_only)
+               dmi->flags |= DM_READONLY_FLAG;
+
+       if (dmt->minor >= 0) {
+               if (dmt->major <= 0) {
+                       log_error("Missing major number for persistent device");
+                       return NULL;
+               }
+               dmi->flags |= DM_PERSISTENT_DEV_FLAG;
+               dmi->dev = MKDEV(dmt->major, dmt->minor);
+       }
+
+       if (dmt->uuid)
+               strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid));
+
+       dmi->target_count = count;
+
+       b = (void *) (dmi + 1);
+       e = (void *) ((char *) dmi + len);
+
+       for (t = dmt->head; t; t = t->next)
+               if (!(b = _add_target_v1(t, b, e))) {
+                       log_error("Ran out of memory building ioctl parameter");
+                       goto bad;
+               }
+
+       if (dmt->newname)
+               strcpy(b, dmt->newname);
+
+       return dmi;
+
+      bad:
+       _dm_zfree_dmi_v1(dmi);
+       return NULL;
+}
+
+static int _dm_names_v1(struct dm_ioctl_v1 *dmi)
+{
+       const char *dev_dir = dm_dir();
+       int r = 1, len;
+       const char *name;
+       struct dirent *dirent;
+       DIR *d;
+       struct dm_names *names, *old_names = NULL;
+       void *end = (void *) dmi + dmi->data_size;
+       struct stat buf;
+       char path[PATH_MAX];
+
+       log_warn("WARNING: Device list may be incomplete with interface "
+                 "version 1.");
+       log_warn("Please upgrade your kernel device-mapper driver.");
+
+       if (!(d = opendir(dev_dir))) {
+               log_sys_error("opendir", dev_dir);
+               return 0;
+       }
+
+       names = (struct dm_names *) ((void *) dmi + dmi->data_start);
+
+       names->dev = 0;         /* Flags no data */
+
+       while ((dirent = readdir(d))) {
+               name = dirent->d_name;
+
+               if (name[0] == '.' || !strcmp(name, "control"))
+                       continue;
+
+               if (old_names)
+                       old_names->next = (uint32_t) ((void *) names -
+                                                     (void *) old_names);
+               snprintf(path, sizeof(path), "%s/%s", dev_dir, name);
+               if (stat(path, &buf)) {
+                       log_sys_error("stat", path);
+                       continue;
+               }
+               if (!S_ISBLK(buf.st_mode))
+                       continue;
+               names->dev = (uint64_t) buf.st_rdev;
+               names->next = 0;
+               len = strlen(name);
+               if (((void *) (names + 1) + len + 1) >= end) {
+                       log_error("Insufficient buffer space for device list");
+                       r = 0;
+                       break;
+               }
+
+               strcpy(names->name, name);
+
+               old_names = names;
+               names = _align((void *) ++names + len + 1, ALIGNMENT);
+       }
+
+       if (closedir(d))
+               log_sys_error("closedir", dev_dir);
+
+       return r;
+}
+
+static int _dm_task_run_v1(struct dm_task *dmt)
+{
+       struct dm_ioctl_v1 *dmi;
+       unsigned int command;
+
+       dmi = _flatten_v1(dmt);
+       if (!dmi) {
+               log_error("Couldn't create ioctl argument.");
+               return 0;
+       }
+
+       if (!_open_control())
+               return 0;
+
+       if ((unsigned) dmt->type >=
+           (sizeof(_cmd_data_v1) / sizeof(*_cmd_data_v1))) {
+               log_error(INTERNAL_ERROR "unknown device-mapper task %d",
+                         dmt->type);
+               goto bad;
+       }
+
+       command = _cmd_data_v1[dmt->type].cmd;
+
+       if (dmt->type == DM_DEVICE_TABLE)
+               dmi->flags |= DM_STATUS_TABLE_FLAG;
+
+       if (dmt->new_uuid) {
+               log_error("Changing UUID is not supported by kernel.");
+               goto bad;
+       }
+
+       log_debug("dm %s %s %s%s%s [%u]", _cmd_data_v1[dmt->type].name,
+                 dmi->name, dmi->uuid, dmt->newname ? " " : "",
+                 dmt->newname ? dmt->newname : "",
+                 dmi->data_size);
+       if (dmt->type == DM_DEVICE_LIST) {
+               if (!_dm_names_v1(dmi))
+                       goto bad;
+       } 
+#ifdef DM_IOCTLS
+       else if (ioctl(_control_fd, command, dmi) < 0) {
+               if (_log_suppress)
+                       log_verbose("device-mapper: %s ioctl failed: %s", 
+                                   _cmd_data_v1[dmt->type].name,
+                                   strerror(errno));
+               else
+                       log_error("device-mapper: %s ioctl failed: %s",
+                                 _cmd_data_v1[dmt->type].name,
+                                 strerror(errno));
+               goto bad;
+       }
+#else /* Userspace alternative for testing */
+#endif
+
+       if (dmi->flags & DM_BUFFER_FULL_FLAG)
+               /* FIXME Increase buffer size and retry operation (if query) */
+               log_error("WARNING: libdevmapper buffer too small for data");
+
+       switch (dmt->type) {
+       case DM_DEVICE_CREATE:
+               add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev),
+                            dmt->uid, dmt->gid, dmt->mode, 0);
+               break;
+
+       case DM_DEVICE_REMOVE:
+               rm_dev_node(dmt->dev_name, 0);
+               break;
+
+       case DM_DEVICE_RENAME:
+               rename_dev_node(dmt->dev_name, dmt->newname, 0);
+               break;
+
+       case DM_DEVICE_MKNODES:
+               if (dmi->flags & DM_EXISTS_FLAG)
+                       add_dev_node(dmt->dev_name, MAJOR(dmi->dev),
+                                    MINOR(dmi->dev), dmt->uid,
+                                    dmt->gid, dmt->mode, 0);
+               else
+                       rm_dev_node(dmt->dev_name, 0);
+               break;
+
+       case DM_DEVICE_STATUS:
+       case DM_DEVICE_TABLE:
+               if (!_unmarshal_status_v1(dmt, dmi))
+                       goto bad;
+               break;
+
+       case DM_DEVICE_SUSPEND:
+       case DM_DEVICE_RESUME:
+               dmt->type = DM_DEVICE_INFO;
+               if (!dm_task_run(dmt))
+                       goto bad;
+               _dm_zfree_dmi_v1(dmi);  /* We'll use what info returned */
+               return 1;
+       }
+
+       dmt->dmi.v1 = dmi;
+       return 1;
+
+      bad:
+       _dm_zfree_dmi_v1(dmi);
+       return 0;
+}
+
+#endif
+
+/*
+ * Protocol Version 4 functions.
+ */
+
+int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size)
+{
+       unsigned *v;
+
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_get_driver_version_v1(dmt, version, size);
+#endif
+
+       if (!dmt->dmi.v4) {
+               version[0] = '\0';
+               return 0;
+       }
+
+       v = dmt->dmi.v4->version;
+       snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
+       _dm_version_minor = v[1];
+       _dm_version_patchlevel = v[2];
+
+       return 1;
+}
+
+static int _check_version(char *version, size_t size, int log_suppress)
+{
+       struct dm_task *task;
+       int r;
+
+       if (!(task = dm_task_create(DM_DEVICE_VERSION))) {
+               log_error("Failed to get device-mapper version");
+               version[0] = '\0';
+               return 0;
+       }
+
+       if (log_suppress)
+               _log_suppress = 1;
+
+       r = dm_task_run(task);
+       dm_task_get_driver_version(task, version, size);
+       dm_task_destroy(task);
+       _log_suppress = 0;
+
+       return r;
+}
+
+/*
+ * Find out device-mapper's major version number the first time 
+ * this is called and whether or not we support it.
+ */
+int dm_check_version(void)
+{
+       char libversion[64], dmversion[64];
+       const char *compat = "";
+
+       if (_version_checked)
+               return _version_ok;
+
+       _version_checked = 1;
+
+       if (_check_version(dmversion, sizeof(dmversion), _dm_compat))
+               return 1;
+
+       if (!_dm_compat)
+               goto bad;
+
+       log_verbose("device-mapper ioctl protocol version %u failed. "
+                   "Trying protocol version 1.", _dm_version);
+       _dm_version = 1;
+       if (_check_version(dmversion, sizeof(dmversion), 0)) {
+               log_verbose("Using device-mapper ioctl protocol version 1");
+               return 1;
+       }
+
+       compat = "(compat)";
+
+       dm_get_library_version(libversion, sizeof(libversion));
+
+       log_error("Incompatible libdevmapper %s%s and kernel driver %s",
+                 libversion, compat, dmversion);
+
+      bad:
+       _version_ok = 0;
+       return 0;
+}
+
+int dm_cookie_supported(void)
+{
+       return (dm_check_version() &&
+               _dm_version >= 4 &&
+               _dm_version_minor >= 15);
+}
+
+void *dm_get_next_target(struct dm_task *dmt, void *next,
+                        uint64_t *start, uint64_t *length,
+                        char **target_type, char **params)
+{
+       struct target *t = (struct target *) next;
+
+       if (!t)
+               t = dmt->head;
+
+       if (!t)
+               return NULL;
+
+       *start = t->start;
+       *length = t->length;
+       *target_type = t->type;
+       *params = t->params;
+
+       return t->next;
+}
+
+/* Unmarshall the target info returned from a status call */
+static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi)
+{
+       char *outbuf = (char *) dmi + dmi->data_start;
+       char *outptr = outbuf;
+       uint32_t i;
+       struct dm_target_spec *spec;
+
+       for (i = 0; i < dmi->target_count; i++) {
+               spec = (struct dm_target_spec *) outptr;
+               if (!dm_task_add_target(dmt, spec->sector_start,
+                                       spec->length,
+                                       spec->target_type,
+                                       outptr + sizeof(*spec))) {
+                       return 0;
+               }
+
+               outptr = outbuf + spec->next;
+       }
+
+       return 1;
+}
+
+int dm_format_dev(char *buf, int bufsize, uint32_t dev_major,
+                 uint32_t dev_minor)
+{
+       int r;
+
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_format_dev_v1(buf, bufsize, dev_major, dev_minor);
+#endif
+
+       if (bufsize < 8)
+               return 0;
+
+       r = snprintf(buf, (size_t) bufsize, "%u:%u", dev_major, dev_minor);
+       if (r < 0 || r > bufsize - 1)
+               return 0;
+
+       return 1;
+}
+
+int dm_task_get_info(struct dm_task *dmt, struct dm_info *info)
+{
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_get_info_v1(dmt, info);
+#endif
+
+       if (!dmt->dmi.v4)
+               return 0;
+
+       memset(info, 0, sizeof(*info));
+
+       info->exists = dmt->dmi.v4->flags & DM_EXISTS_FLAG ? 1 : 0;
+       if (!info->exists)
+               return 1;
+
+       info->suspended = dmt->dmi.v4->flags & DM_SUSPEND_FLAG ? 1 : 0;
+       info->read_only = dmt->dmi.v4->flags & DM_READONLY_FLAG ? 1 : 0;
+       info->live_table = dmt->dmi.v4->flags & DM_ACTIVE_PRESENT_FLAG ? 1 : 0;
+       info->inactive_table = dmt->dmi.v4->flags & DM_INACTIVE_PRESENT_FLAG ?
+           1 : 0;
+       info->target_count = dmt->dmi.v4->target_count;
+       info->open_count = dmt->dmi.v4->open_count;
+       info->event_nr = dmt->dmi.v4->event_nr;
+       info->major = MAJOR(dmt->dmi.v4->dev);
+       info->minor = MINOR(dmt->dmi.v4->dev);
+
+       return 1;
+}
+
+uint32_t dm_task_get_read_ahead(const struct dm_task *dmt, uint32_t *read_ahead)
+{              
+       const char *dev_name;
+
+       *read_ahead = 0;
+
+#ifdef DM_COMPAT
+       /* Not supporting this */
+        if (_dm_version == 1)
+                return 1;
+#endif  
+
+        if (!dmt->dmi.v4 || !(dmt->dmi.v4->flags & DM_EXISTS_FLAG))
+               return 0;
+
+       if (*dmt->dmi.v4->name)
+               dev_name = dmt->dmi.v4->name;
+       else if (dmt->dev_name)
+               dev_name = dmt->dev_name;
+       else {
+               log_error("Get read ahead request failed: device name unrecorded.");
+               return 0;
+       }
+
+       return get_dev_node_read_ahead(dev_name, read_ahead);
+}
+
+const char *dm_task_get_name(const struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_get_name_v1(dmt);
+#endif
+
+       return (dmt->dmi.v4->name);
+}
+
+const char *dm_task_get_uuid(const struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_get_uuid_v1(dmt);
+#endif
+
+       return (dmt->dmi.v4->uuid);
+}
+
+struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_get_deps_v1(dmt);
+#endif
+
+       return (struct dm_deps *) (((char *) dmt->dmi.v4) +
+                                  dmt->dmi.v4->data_start);
+}
+
+struct dm_names *dm_task_get_names(struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_get_names_v1(dmt);
+#endif
+
+       return (struct dm_names *) (((char *) dmt->dmi.v4) +
+                                   dmt->dmi.v4->data_start);
+}
+
+struct dm_versions *dm_task_get_versions(struct dm_task *dmt)
+{
+       return (struct dm_versions *) (((char *) dmt->dmi.v4) +
+                                      dmt->dmi.v4->data_start);
+}
+
+int dm_task_set_ro(struct dm_task *dmt)
+{
+       dmt->read_only = 1;
+       return 1;
+}
+
+int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
+                          uint32_t read_ahead_flags)
+{
+       dmt->read_ahead = read_ahead;
+       dmt->read_ahead_flags = read_ahead_flags;
+
+       return 1;
+}
+
+int dm_task_suppress_identical_reload(struct dm_task *dmt)
+{
+       dmt->suppress_identical_reload = 1;
+       return 1;
+}
+
+int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid)
+{
+       if (strlen(newuuid) >= DM_UUID_LEN) {
+               log_error("Uuid \"%s\" too long", newuuid);
+               return 0;
+       }
+
+       if (!(dmt->newname = dm_strdup(newuuid))) {
+               log_error("dm_task_set_newuuid: strdup(%s) failed", newuuid);
+               return 0;
+       }
+       dmt->new_uuid = 1;
+
+       return 1;
+}
+
+int dm_task_set_newname(struct dm_task *dmt, const char *newname)
+{
+       if (strchr(newname, '/')) {
+               log_error("Name \"%s\" invalid. It contains \"/\".", newname);
+               return 0;
+       }
+
+       if (strlen(newname) >= DM_NAME_LEN) {
+               log_error("Name \"%s\" too long", newname);
+               return 0;
+       }
+
+       if (!(dmt->newname = dm_strdup(newname))) {
+               log_error("dm_task_set_newname: strdup(%s) failed", newname);
+               return 0;
+       }
+       dmt->new_uuid = 0;
+
+       return 1;
+}
+
+int dm_task_set_message(struct dm_task *dmt, const char *message)
+{
+       if (!(dmt->message = dm_strdup(message))) {
+               log_error("dm_task_set_message: strdup failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+int dm_task_set_sector(struct dm_task *dmt, uint64_t sector)
+{
+       dmt->sector = sector;
+
+       return 1;
+}
+
+int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start)
+{
+       size_t len = strlen(cylinders) + 1 + strlen(heads) + 1 + strlen(sectors) + 1 + strlen(start) + 1;
+
+       if (!(dmt->geometry = dm_malloc(len))) {
+               log_error("dm_task_set_geometry: dm_malloc failed");
+               return 0;
+       }
+
+       if (sprintf(dmt->geometry, "%s %s %s %s", cylinders, heads, sectors, start) < 0) {
+               log_error("dm_task_set_geometry: sprintf failed");
+               return 0;
+       }
+
+       return 1;
+}
+
+int dm_task_no_flush(struct dm_task *dmt)
+{
+       dmt->no_flush = 1;
+
+       return 1;
+}
+
+int dm_task_no_open_count(struct dm_task *dmt)
+{
+       dmt->no_open_count = 1;
+
+       return 1;
+}
+
+int dm_task_skip_lockfs(struct dm_task *dmt)
+{
+       dmt->skip_lockfs = 1;
+
+       return 1;
+}
+
+int dm_task_query_inactive_table(struct dm_task *dmt)
+{
+       dmt->query_inactive_table = 1;
+
+       return 1;
+}
+
+int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
+{
+       dmt->event_nr = event_nr;
+
+       return 1;
+}
+
+struct target *create_target(uint64_t start, uint64_t len, const char *type,
+                            const char *params)
+{
+       struct target *t = dm_malloc(sizeof(*t));
+
+       if (!t) {
+               log_error("create_target: malloc(%" PRIsize_t ") failed",
+                         sizeof(*t));
+               return NULL;
+       }
+
+       memset(t, 0, sizeof(*t));
+
+       if (!(t->params = dm_strdup(params))) {
+               log_error("create_target: strdup(params) failed");
+               goto bad;
+       }
+
+       if (!(t->type = dm_strdup(type))) {
+               log_error("create_target: strdup(type) failed");
+               goto bad;
+       }
+
+       t->start = start;
+       t->length = len;
+       return t;
+
+      bad:
+       _dm_zfree_string(t->params);
+       dm_free(t->type);
+       dm_free(t);
+       return NULL;
+}
+
+static void *_add_target(struct target *t, void *out, void *end)
+{
+       void *out_sp = out;
+       struct dm_target_spec sp;
+       size_t sp_size = sizeof(struct dm_target_spec);
+       int len;
+
+       out += sp_size;
+       if (out >= end)
+               return_NULL;
+
+       sp.status = 0;
+       sp.sector_start = t->start;
+       sp.length = t->length;
+       strncpy(sp.target_type, t->type, sizeof(sp.target_type));
+
+       len = strlen(t->params);
+
+       if ((out + len + 1) >= end)
+               return_NULL;
+
+       strcpy((char *) out, t->params);
+       out += len + 1;
+
+       /* align next block */
+       out = _align(out, ALIGNMENT);
+
+       sp.next = out - out_sp;
+       memcpy(out_sp, &sp, sp_size);
+
+       return out;
+}
+
+static int _lookup_dev_name(uint64_t dev, char *buf, size_t len)
+{
+       struct dm_names *names;
+       unsigned next = 0;
+       struct dm_task *dmt;
+       int r = 0;
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+               return 0;
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!(names = dm_task_get_names(dmt)))
+               goto out;
+       if (!names->dev)
+               goto out;
+       do {
+               names = (void *) names + next;
+               if (names->dev == dev) {
+                       strncpy(buf, names->name, len);
+                       r = 1;
+                       break;
+               }
+               next = names->next;
+       } while (next);
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
+{
+       const size_t min_size = 16 * 1024;
+       const int (*version)[3];
+
+       struct dm_ioctl *dmi;
+       struct target *t;
+       struct dm_target_msg *tmsg;
+       size_t len = sizeof(struct dm_ioctl);
+       void *b, *e;
+       int count = 0;
+
+       for (t = dmt->head; t; t = t->next) {
+               len += sizeof(struct dm_target_spec);
+               len += strlen(t->params) + 1 + ALIGNMENT;
+               count++;
+       }
+
+       if (count && (dmt->sector || dmt->message)) {
+               log_error("targets and message are incompatible");
+               return NULL;
+       }
+
+       if (count && dmt->newname) {
+               log_error("targets and rename are incompatible");
+               return NULL;
+       }
+
+       if (count && dmt->geometry) {
+               log_error("targets and geometry are incompatible");
+               return NULL;
+       }
+
+       if (dmt->newname && (dmt->sector || dmt->message)) {
+               log_error("message and rename are incompatible");
+               return NULL;
+       }
+
+       if (dmt->newname && dmt->geometry) {
+               log_error("geometry and rename are incompatible");
+               return NULL;
+       }
+
+       if (dmt->geometry && (dmt->sector || dmt->message)) {
+               log_error("geometry and message are incompatible");
+               return NULL;
+       }
+
+       if (dmt->sector && !dmt->message) {
+               log_error("message is required with sector");
+               return NULL;
+       }
+
+       if (dmt->newname)
+               len += strlen(dmt->newname) + 1;
+
+       if (dmt->message)
+               len += sizeof(struct dm_target_msg) + strlen(dmt->message) + 1;
+
+       if (dmt->geometry)
+               len += strlen(dmt->geometry) + 1;
+
+       /*
+        * Give len a minimum size so that we have space to store
+        * dependencies or status information.
+        */
+       if (len < min_size)
+               len = min_size;
+
+       /* Increase buffer size if repeating because buffer was too small */
+       while (repeat_count--)
+               len *= 2;
+
+       if (!(dmi = dm_malloc(len)))
+               return NULL;
+
+       memset(dmi, 0, len);
+
+       version = &_cmd_data_v4[dmt->type].version;
+
+       dmi->version[0] = (*version)[0];
+       dmi->version[1] = (*version)[1];
+       dmi->version[2] = (*version)[2];
+
+       dmi->data_size = len;
+       dmi->data_start = sizeof(struct dm_ioctl);
+
+       if (dmt->minor >= 0) {
+               if (dmt->major <= 0) {
+                       log_error("Missing major number for persistent device.");
+                       goto bad;
+               }
+
+               if (!_dm_multiple_major_support && dmt->allow_default_major_fallback &&
+                   dmt->major != _dm_device_major) {
+                       log_verbose("Overriding major number of %" PRIu32 
+                                   " with %" PRIu32 " for persistent device.",
+                                   dmt->major, _dm_device_major);
+                       dmt->major = _dm_device_major;
+               }
+
+               dmi->flags |= DM_PERSISTENT_DEV_FLAG;
+               dmi->dev = MKDEV(dmt->major, dmt->minor);
+       }
+
+       /* Does driver support device number referencing? */
+       if (_dm_version_minor < 3 && !dmt->dev_name && !dmt->uuid && dmi->dev) {
+               if (!_lookup_dev_name(dmi->dev, dmi->name, sizeof(dmi->name))) {
+                       log_error("Unable to find name for device (%" PRIu32
+                                 ":%" PRIu32 ")", dmt->major, dmt->minor);
+                       goto bad;
+               }
+               log_verbose("device (%" PRIu32 ":%" PRIu32 ") is %s "
+                           "for compatibility with old kernel",
+                           dmt->major, dmt->minor, dmi->name);
+       }
+
+       /* FIXME Until resume ioctl supplies name, use dev_name for readahead */
+       if (dmt->dev_name && (dmt->type != DM_DEVICE_RESUME || dmt->minor < 0 ||
+                             dmt->major < 0))
+               strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name));
+
+       if (dmt->uuid)
+               strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid));
+
+       if (dmt->type == DM_DEVICE_SUSPEND)
+               dmi->flags |= DM_SUSPEND_FLAG;
+       if (dmt->no_flush)
+               dmi->flags |= DM_NOFLUSH_FLAG;
+       if (dmt->read_only)
+               dmi->flags |= DM_READONLY_FLAG;
+       if (dmt->skip_lockfs)
+               dmi->flags |= DM_SKIP_LOCKFS_FLAG;
+       if (dmt->query_inactive_table) {
+               if (_dm_version_minor < 16)
+                       log_warn("WARNING: Inactive table query unsupported "
+                                "by kernel.  It will use live table.");
+               dmi->flags |= DM_QUERY_INACTIVE_TABLE_FLAG;
+       }
+       if (dmt->new_uuid) {
+               if (_dm_version_minor < 19) {
+                       log_error("WARNING: Setting UUID unsupported by "
+                                 "kernel.  Aborting operation.");
+                       goto bad;
+               }
+               dmi->flags |= DM_UUID_FLAG;
+       }
+
+       dmi->target_count = count;
+       dmi->event_nr = dmt->event_nr;
+
+       b = (void *) (dmi + 1);
+       e = (void *) ((char *) dmi + len);
+
+       for (t = dmt->head; t; t = t->next)
+               if (!(b = _add_target(t, b, e))) {
+                       log_error("Ran out of memory building ioctl parameter");
+                       goto bad;
+               }
+
+       if (dmt->newname)
+               strcpy(b, dmt->newname);
+
+       if (dmt->message) {
+               tmsg = (struct dm_target_msg *) b;
+               tmsg->sector = dmt->sector;
+               strcpy(tmsg->message, dmt->message);
+       }
+
+       if (dmt->geometry)
+               strcpy(b, dmt->geometry);
+
+       return dmi;
+
+      bad:
+       _dm_zfree_dmi(dmi);
+       return NULL;
+}
+
+static int _process_mapper_dir(struct dm_task *dmt)
+{
+       struct dirent *dirent;
+       DIR *d;
+       const char *dir;
+       int r = 1;
+
+       dir = dm_dir();
+       if (!(d = opendir(dir))) {
+               log_sys_error("opendir", dir);
+               return 0;
+       }
+
+       while ((dirent = readdir(d))) {
+               if (!strcmp(dirent->d_name, ".") ||
+                   !strcmp(dirent->d_name, "..") ||
+                   !strcmp(dirent->d_name, "control"))
+                       continue;
+               if (!dm_task_set_name(dmt, dirent->d_name)) {
+                       r = 0;
+                       stack;
+                       continue; /* try next name */
+               }
+               if (!dm_task_run(dmt)) {
+                       r = 0;
+                       stack;  /* keep going */
+               }
+       }
+
+       if (closedir(d))
+               log_sys_error("closedir", dir);
+
+       return r;
+}
+
+static int _process_all_v4(struct dm_task *dmt)
+{
+       struct dm_task *task;
+       struct dm_names *names;
+       unsigned next = 0;
+       int r = 1;
+
+       if (!(task = dm_task_create(DM_DEVICE_LIST)))
+               return 0;
+
+       if (!dm_task_run(task)) {
+               r = 0;
+               goto out;
+       }
+
+       if (!(names = dm_task_get_names(task))) {
+               r = 0;
+               goto out;
+       }
+
+       if (!names->dev)
+               goto out;
+
+       do {
+               names = (void *) names + next;
+               if (!dm_task_set_name(dmt, names->name)) {
+                       r = 0;
+                       goto out;
+               }
+               if (!dm_task_run(dmt))
+                       r = 0;
+               next = names->next;
+       } while (next);
+
+      out:
+       dm_task_destroy(task);
+       return r;
+}
+
+static int _mknodes_v4(struct dm_task *dmt)
+{
+       (void) _process_mapper_dir(dmt);
+
+       return _process_all_v4(dmt);
+}
+
+/*
+ * If an operation that uses a cookie fails, decrement the
+ * semaphore instead of udev.
+ */
+static int _udev_complete(struct dm_task *dmt)
+{
+       uint16_t base;
+
+       if (dmt->cookie_set &&
+           (base = dmt->event_nr & ~DM_UDEV_FLAGS_MASK))
+               /* strip flags from the cookie and use cookie magic instead */
+               return dm_udev_complete(base | (DM_COOKIE_MAGIC <<
+                                               DM_UDEV_FLAGS_SHIFT));
+
+       return 1;
+}
+
+static int _check_uevent_generated(struct dm_ioctl *dmi)
+{
+       if (!dm_check_version() ||
+           _dm_version < 4 ||
+           _dm_version_minor < 17)
+               /* can't check, assume uevent is generated */
+               return 1;
+
+       return dmi->flags & DM_UEVENT_GENERATED_FLAG;
+}
+
+static int _create_and_load_v4(struct dm_task *dmt)
+{
+       struct dm_task *task;
+       int r;
+       uint32_t cookie;
+
+       /* Use new task struct to create the device */
+       if (!(task = dm_task_create(DM_DEVICE_CREATE))) {
+               log_error("Failed to create device-mapper task struct");
+               _udev_complete(dmt);
+               return 0;
+       }
+
+       /* Copy across relevant fields */
+       if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+               dm_task_destroy(task);
+               _udev_complete(dmt);
+               return 0;
+       }
+
+       if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
+               dm_task_destroy(task);
+               _udev_complete(dmt);
+               return 0;
+       }
+
+       task->major = dmt->major;
+       task->minor = dmt->minor;
+       task->uid = dmt->uid;
+       task->gid = dmt->gid;
+       task->mode = dmt->mode;
+       /* FIXME: Just for udev_check in dm_task_run. Can we avoid this? */
+       task->event_nr = dmt->event_nr & DM_UDEV_FLAGS_MASK;
+       task->cookie_set = dmt->cookie_set;
+
+       r = dm_task_run(task);
+       dm_task_destroy(task);
+       if (!r) {
+               _udev_complete(dmt);
+               return 0;
+       }
+
+       /* Next load the table */
+       if (!(task = dm_task_create(DM_DEVICE_RELOAD))) {
+               log_error("Failed to create device-mapper task struct");
+               _udev_complete(dmt);
+               r = 0;
+               goto revert;
+       }
+
+       /* Copy across relevant fields */
+       if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+               dm_task_destroy(task);
+               _udev_complete(dmt);
+               r = 0;
+               goto revert;
+       }
+
+       task->read_only = dmt->read_only;
+       task->head = dmt->head;
+       task->tail = dmt->tail;
+
+       r = dm_task_run(task);
+
+       task->head = NULL;
+       task->tail = NULL;
+       dm_task_destroy(task);
+       if (!r) {
+               _udev_complete(dmt);
+               goto revert;
+       }
+
+       /* Use the original structure last so the info will be correct */
+       dmt->type = DM_DEVICE_RESUME;
+       dm_free(dmt->uuid);
+       dmt->uuid = NULL;
+
+       r = dm_task_run(dmt);
+
+       if (r)
+               return r;
+
+      revert:
+       dmt->type = DM_DEVICE_REMOVE;
+       dm_free(dmt->uuid);
+       dmt->uuid = NULL;
+
+       /*
+        * Also udev-synchronize "remove" dm task that is a part of this revert!
+        * But only if the original dm task was supposed to be synchronized.
+        */
+       if (dmt->cookie_set) {
+               cookie = (dmt->event_nr & ~DM_UDEV_FLAGS_MASK) |
+                        (DM_COOKIE_MAGIC << DM_UDEV_FLAGS_SHIFT);
+               if (!dm_task_set_cookie(dmt, &cookie,
+                                       (dmt->event_nr & DM_UDEV_FLAGS_MASK) >>
+                                       DM_UDEV_FLAGS_SHIFT))
+                        stack; /* keep going */
+       }
+
+       if (!dm_task_run(dmt))
+               log_error("Failed to revert device creation.");
+
+       return r;
+}
+
+uint64_t dm_task_get_existing_table_size(struct dm_task *dmt)
+{
+       return dmt->existing_table_size;
+}
+
+static int _reload_with_suppression_v4(struct dm_task *dmt)
+{
+       struct dm_task *task;
+       struct target *t1, *t2;
+       int r;
+
+       /* New task to get existing table information */
+       if (!(task = dm_task_create(DM_DEVICE_TABLE))) {
+               log_error("Failed to create device-mapper task struct");
+               return 0;
+       }
+
+       /* Copy across relevant fields */
+       if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+               dm_task_destroy(task);
+               return 0;
+       }
+
+       if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
+               dm_task_destroy(task);
+               return 0;
+       }
+
+       task->major = dmt->major;
+       task->minor = dmt->minor;
+
+       r = dm_task_run(task);
+
+       if (!r) {
+               dm_task_destroy(task);
+               return r;
+       }
+
+       /* Store existing table size */
+       t2 = task->head;
+       while (t2 && t2->next)
+               t2 = t2->next;
+       dmt->existing_table_size = t2 ? t2->start + t2->length : 0;
+
+       if ((task->dmi.v4->flags & DM_READONLY_FLAG) ? 1 : 0 != dmt->read_only)
+               goto no_match;
+
+       t1 = dmt->head;
+       t2 = task->head;
+
+       while (t1 && t2) {
+               while (t2->params[strlen(t2->params) - 1] == ' ')
+                       t2->params[strlen(t2->params) - 1] = '\0';
+               if ((t1->start != t2->start) ||
+                   (t1->length != t2->length) ||
+                   (strcmp(t1->type, t2->type)) ||
+                   (strcmp(t1->params, t2->params)))
+                       goto no_match;
+               t1 = t1->next;
+               t2 = t2->next;
+       }
+       
+       if (!t1 && !t2) {
+               dmt->dmi.v4 = task->dmi.v4;
+               task->dmi.v4 = NULL;
+               dm_task_destroy(task);
+               return 1;
+       }
+
+no_match:
+       dm_task_destroy(task);
+
+       /* Now do the original reload */
+       dmt->suppress_identical_reload = 0;
+       r = dm_task_run(dmt);
+
+       return r;
+}
+
+static const char *_sanitise_message(char *message)
+{
+       const char *sanitised_message = message ?: "";
+
+       /* FIXME: Check for whitespace variations. */
+       /* This traps what cryptsetup sends us. */
+       if (message && !strncasecmp(message, "key set", 7))
+               sanitised_message = "key set";
+
+       return sanitised_message;
+}
+
+static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
+                                    unsigned repeat_count)
+{
+       struct dm_ioctl *dmi;
+       int ioctl_with_uevent;
+
+       dmi = _flatten(dmt, repeat_count);
+       if (!dmi) {
+               log_error("Couldn't create ioctl argument.");
+               return NULL;
+       }
+
+       if (dmt->type == DM_DEVICE_TABLE)
+               dmi->flags |= DM_STATUS_TABLE_FLAG;
+
+       dmi->flags |= DM_EXISTS_FLAG;   /* FIXME */
+
+       if (dmt->no_open_count)
+               dmi->flags |= DM_SKIP_BDGET_FLAG;
+
+       ioctl_with_uevent = dmt->type == DM_DEVICE_RESUME ||
+                           dmt->type == DM_DEVICE_REMOVE ||
+                           dmt->type == DM_DEVICE_RENAME;
+
+       if (ioctl_with_uevent && dm_cookie_supported()) {
+               /*
+                * Always mark events coming from libdevmapper as
+                * "primary sourced". This is needed to distinguish
+                * any spurious events so we can act appropriately.
+                * This needs to be applied even when udev_sync is
+                * not used because udev flags could be used alone.
+                */
+               dmi->event_nr |= DM_UDEV_PRIMARY_SOURCE_FLAG <<
+                                DM_UDEV_FLAGS_SHIFT;
+
+               /*
+                * Prevent udev vs. libdevmapper race when processing nodes
+                * and symlinks. This can happen when the udev rules are
+                * installed and udev synchronisation code is enabled in
+                * libdevmapper but the software using libdevmapper does not
+                * make use of it (by not calling dm_task_set_cookie before).
+                * We need to instruct the udev rules not to be applied at
+                * all in this situation so we can gracefully fallback to
+                * libdevmapper's node and symlink creation code.
+                */
+               if (!dmt->cookie_set && dm_udev_get_sync_support()) {
+                       log_debug("Cookie value is not set while trying to call %s "
+                                 "ioctl. Please, consider using libdevmapper's udev "
+                                 "synchronisation interface or disable it explicitly "
+                                 "by calling dm_udev_set_sync_support(0).",
+                                 dmt->type == DM_DEVICE_RESUME ? "DM_DEVICE_RESUME" :
+                                 dmt->type == DM_DEVICE_REMOVE ? "DM_DEVICE_REMOVE" :
+                                                                 "DM_DEVICE_RENAME");
+                       log_debug("Switching off device-mapper and all subsystem related "
+                                 "udev rules. Falling back to libdevmapper node creation.");
+                       /*
+                        * Disable general dm and subsystem rules but keep
+                        * dm disk rules if not flagged out explicitly before.
+                        * We need /dev/disk content for the software that expects it.
+                       */
+                       dmi->event_nr |= (DM_UDEV_DISABLE_DM_RULES_FLAG |
+                                         DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG) <<
+                                        DM_UDEV_FLAGS_SHIFT;
+               }
+       }
+
+       log_debug("dm %s %s%s %s%s%s %s%.0d%s%.0d%s"
+                 "%s%c%c%s%s %.0" PRIu64 " %s [%u]",
+                 _cmd_data_v4[dmt->type].name,
+                 dmt->new_uuid ? "UUID " : "",
+                 dmi->name, dmi->uuid, dmt->newname ? " " : "",
+                 dmt->newname ? dmt->newname : "",
+                 dmt->major > 0 ? "(" : "",
+                 dmt->major > 0 ? dmt->major : 0,
+                 dmt->major > 0 ? ":" : "",
+                 dmt->minor > 0 ? dmt->minor : 0,
+                 dmt->major > 0 && dmt->minor == 0 ? "0" : "",
+                 dmt->major > 0 ? ") " : "",
+                 dmt->no_open_count ? 'N' : 'O',
+                 dmt->no_flush ? 'N' : 'F',
+                 dmt->skip_lockfs ? "S " : "",
+                 dmt->query_inactive_table ? "I " : "",
+                 dmt->sector, _sanitise_message(dmt->message),
+                 dmi->data_size);
+#ifdef DM_IOCTLS
+       if (ioctl(_control_fd, command, dmi) < 0) {
+               if (errno == ENXIO && ((dmt->type == DM_DEVICE_INFO) ||
+                                      (dmt->type == DM_DEVICE_MKNODES) ||
+                                      (dmt->type == DM_DEVICE_STATUS)))
+                       dmi->flags &= ~DM_EXISTS_FLAG;  /* FIXME */
+               else {
+                       if (_log_suppress)
+                               log_verbose("device-mapper: %s ioctl "
+                                           "failed: %s",
+                                           _cmd_data_v4[dmt->type].name,
+                                           strerror(errno));
+                       else
+                               log_error("device-mapper: %s ioctl "
+                                         "failed: %s",
+                                          _cmd_data_v4[dmt->type].name,
+                                         strerror(errno));
+                       _dm_zfree_dmi(dmi);
+                       return NULL;
+               }
+       }
+
+       if (ioctl_with_uevent && !_check_uevent_generated(dmi))
+               _udev_complete(dmt);
+
+#else /* Userspace alternative for testing */
+#endif
+       return dmi;
+}
+
+void dm_task_update_nodes(void)
+{
+       update_devs();
+}
+
+int dm_task_run(struct dm_task *dmt)
+{
+       struct dm_ioctl *dmi;
+       unsigned command;
+       int check_udev;
+       int udev_only;
+
+#ifdef DM_COMPAT
+       if (_dm_version == 1)
+               return _dm_task_run_v1(dmt);
+#endif
+
+       if ((unsigned) dmt->type >=
+           (sizeof(_cmd_data_v4) / sizeof(*_cmd_data_v4))) {
+               log_error(INTERNAL_ERROR "unknown device-mapper task %d",
+                         dmt->type);
+               return 0;
+       }
+
+       command = _cmd_data_v4[dmt->type].cmd;
+
+       /* Old-style creation had a table supplied */
+       if (dmt->type == DM_DEVICE_CREATE && dmt->head)
+               return _create_and_load_v4(dmt);
+
+       if (dmt->type == DM_DEVICE_MKNODES && !dmt->dev_name &&
+           !dmt->uuid && dmt->major <= 0)
+               return _mknodes_v4(dmt);
+
+       if ((dmt->type == DM_DEVICE_RELOAD) && dmt->suppress_identical_reload)
+               return _reload_with_suppression_v4(dmt);
+
+       if (!_open_control()) {
+               _udev_complete(dmt);
+               return 0;
+       }
+
+       /* FIXME Detect and warn if cookie set but should not be. */
+repeat_ioctl:
+       if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor))) {
+               _udev_complete(dmt);
+               return 0;
+       }
+
+       if (dmi->flags & DM_BUFFER_FULL_FLAG) {
+               switch (dmt->type) {
+               case DM_DEVICE_LIST_VERSIONS:
+               case DM_DEVICE_LIST:
+               case DM_DEVICE_DEPS:
+               case DM_DEVICE_STATUS:
+               case DM_DEVICE_TABLE:
+               case DM_DEVICE_WAITEVENT:
+                       _ioctl_buffer_double_factor++;
+                       _dm_zfree_dmi(dmi);
+                       goto repeat_ioctl;
+               default:
+                       log_error("WARNING: libdevmapper buffer too small for data");
+               }
+       }
+
+       check_udev = dmt->cookie_set &&
+                    !(dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
+                      DM_UDEV_DISABLE_DM_RULES_FLAG);
+
+       udev_only = dmt->cookie_set ? (dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
+                                       DM_UDEV_DISABLE_LIBRARY_FALLBACK) : 0;
+
+       switch (dmt->type) {
+       case DM_DEVICE_CREATE:
+               if (dmt->dev_name && *dmt->dev_name && !udev_only)
+                       add_dev_node(dmt->dev_name, MAJOR(dmi->dev),
+                                    MINOR(dmi->dev), dmt->uid, dmt->gid,
+                                    dmt->mode, check_udev);
+               break;
+       case DM_DEVICE_REMOVE:
+               /* FIXME Kernel needs to fill in dmi->name */
+               if (dmt->dev_name && !udev_only)
+                       rm_dev_node(dmt->dev_name, check_udev);
+               break;
+
+       case DM_DEVICE_RENAME:
+               /* FIXME Kernel needs to fill in dmi->name */
+               if (!dmt->new_uuid && dmt->dev_name && !udev_only)
+                       rename_dev_node(dmt->dev_name, dmt->newname,
+                                       check_udev);
+               break;
+
+       case DM_DEVICE_RESUME:
+               /* FIXME Kernel needs to fill in dmi->name */
+               set_dev_node_read_ahead(dmt->dev_name, dmt->read_ahead,
+                                       dmt->read_ahead_flags);
+               break;
+       
+       case DM_DEVICE_MKNODES:
+               if (dmi->flags & DM_EXISTS_FLAG)
+                       add_dev_node(dmi->name, MAJOR(dmi->dev),
+                                    MINOR(dmi->dev), dmt->uid,
+                                    dmt->gid, dmt->mode, 0);
+               else if (dmt->dev_name)
+                       rm_dev_node(dmt->dev_name, 0);
+               break;
+
+       case DM_DEVICE_STATUS:
+       case DM_DEVICE_TABLE:
+       case DM_DEVICE_WAITEVENT:
+               if (!_unmarshal_status(dmt, dmi))
+                       goto bad;
+               break;
+       }
+
+       /* Was structure reused? */
+       _dm_zfree_dmi(dmt->dmi.v4);
+       dmt->dmi.v4 = dmi;
+       return 1;
+
+      bad:
+       _dm_zfree_dmi(dmi);
+       return 0;
+}
+
+void dm_lib_release(void)
+{
+       _close_control_fd();
+       update_devs();
+}
+
+void dm_pools_check_leaks(void);
+
+void dm_lib_exit(void)
+{
+       dm_lib_release();
+       selinux_release();
+       if (_dm_bitset)
+               dm_bitset_destroy(_dm_bitset);
+       _dm_bitset = NULL;
+       dm_pools_check_leaks();
+       dm_dump_memory();
+       _version_ok = 1;
+       _version_checked = 0;
+}
diff --git a/libdm/ioctl/libdm-targets.h b/libdm/ioctl/libdm-targets.h
new file mode 100644 (file)
index 0000000..d8cee45
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef LIB_DMTARGETS_H
+#define LIB_DMTARGETS_H
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+struct dm_ioctl;
+struct dm_ioctl_v1;
+
+struct target {
+       uint64_t start;
+       uint64_t length;
+       char *type;
+       char *params;
+
+       struct target *next;
+};
+
+struct dm_task {
+       int type;
+       char *dev_name;
+
+       struct target *head, *tail;
+
+       int read_only;
+       uint32_t event_nr;
+       int major;
+       int minor;
+       int allow_default_major_fallback;
+       uid_t uid;
+       gid_t gid;
+       mode_t mode;
+       uint32_t read_ahead;
+       uint32_t read_ahead_flags;
+       union {
+               struct dm_ioctl *v4;
+               struct dm_ioctl_v1 *v1;
+       } dmi;
+       char *newname;
+       char *message;
+       char *geometry;
+       uint64_t sector;
+       int no_flush;
+       int no_open_count;
+       int skip_lockfs;
+       int query_inactive_table;
+       int suppress_identical_reload;
+       uint64_t existing_table_size;
+       int cookie_set;
+       int new_uuid;
+
+       char *uuid;
+};
+
+struct cmd_data {
+       const char *name;
+       const int cmd;
+       const int version[3];
+};
+
+int dm_check_version(void);
+uint64_t dm_task_get_existing_table_size(struct dm_task *dmt);
+
+#endif
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
new file mode 100644 (file)
index 0000000..4aa9991
--- /dev/null
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef LIB_DEVICE_MAPPER_H
+#define LIB_DEVICE_MAPPER_H
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+#ifdef linux
+#  include <linux/types.h>
+#endif
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef __GNUC__
+# define __typeof__ typeof
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************
+ * The first section of this file provides direct access to the
+ * individual device-mapper ioctls.  Since it is quite laborious to
+ * build the ioctl arguments for the device-mapper, people are
+ * encouraged to use this library.
+ ****************************************************************/
+
+/*
+ * The library user may wish to register their own
+ * logging function.  By default errors go to stderr.
+ * Use dm_log_with_errno_init(NULL) to restore the default log fn.
+ */
+
+typedef void (*dm_log_with_errno_fn) (int level, const char *file, int line,
+                                     int dm_errno, const char *f, ...)
+    __attribute__ ((format(printf, 5, 6)));
+
+void dm_log_with_errno_init(dm_log_with_errno_fn fn);
+void dm_log_init_verbose(int level);
+
+/*
+ * Original version of this function.
+ * dm_errno is set to 0.
+ *
+ * Deprecated: Use the _with_errno_ versions above instead.
+ */
+typedef void (*dm_log_fn) (int level, const char *file, int line,
+                          const char *f, ...)
+    __attribute__ ((format(printf, 4, 5)));
+
+void dm_log_init(dm_log_fn fn);
+/*
+ * For backward-compatibility, indicate that dm_log_init() was used
+ * to set a non-default value of dm_log().
+ */
+int dm_log_is_non_default(void);
+
+enum {
+       DM_DEVICE_CREATE,
+       DM_DEVICE_RELOAD,
+       DM_DEVICE_REMOVE,
+       DM_DEVICE_REMOVE_ALL,
+
+       DM_DEVICE_SUSPEND,
+       DM_DEVICE_RESUME,
+
+       DM_DEVICE_INFO,
+       DM_DEVICE_DEPS,
+       DM_DEVICE_RENAME,
+
+       DM_DEVICE_VERSION,
+
+       DM_DEVICE_STATUS,
+       DM_DEVICE_TABLE,
+       DM_DEVICE_WAITEVENT,
+
+       DM_DEVICE_LIST,
+
+       DM_DEVICE_CLEAR,
+
+       DM_DEVICE_MKNODES,
+
+       DM_DEVICE_LIST_VERSIONS,
+       
+       DM_DEVICE_TARGET_MSG,
+
+       DM_DEVICE_SET_GEOMETRY
+};
+
+/*
+ * You will need to build a struct dm_task for
+ * each ioctl command you want to execute.
+ */
+
+struct dm_task;
+
+struct dm_task *dm_task_create(int type);
+void dm_task_destroy(struct dm_task *dmt);
+
+int dm_task_set_name(struct dm_task *dmt, const char *name);
+int dm_task_set_uuid(struct dm_task *dmt, const char *uuid);
+
+/*
+ * Retrieve attributes after an info.
+ */
+struct dm_info {
+       int exists;
+       int suspended;
+       int live_table;
+       int inactive_table;
+       int32_t open_count;
+       uint32_t event_nr;
+       uint32_t major;
+       uint32_t minor;         /* minor device number */
+       int read_only;          /* 0:read-write; 1:read-only */
+
+       int32_t target_count;
+};
+
+struct dm_deps {
+       uint32_t count;
+       uint32_t filler;
+       uint64_t device[0];
+};
+
+struct dm_names {
+       uint64_t dev;
+       uint32_t next;          /* Offset to next struct from start of this struct */
+       char name[0];
+};
+
+struct dm_versions {
+       uint32_t next;          /* Offset to next struct from start of this struct */
+       uint32_t version[3];
+
+       char name[0];
+};
+
+int dm_get_library_version(char *version, size_t size);
+int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size);
+int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi);
+const char *dm_task_get_name(const struct dm_task *dmt);
+const char *dm_task_get_uuid(const struct dm_task *dmt);
+
+struct dm_deps *dm_task_get_deps(struct dm_task *dmt);
+struct dm_names *dm_task_get_names(struct dm_task *dmt);
+struct dm_versions *dm_task_get_versions(struct dm_task *dmt);
+
+int dm_task_set_ro(struct dm_task *dmt);
+int dm_task_set_newname(struct dm_task *dmt, const char *newname);
+int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid);
+int dm_task_set_minor(struct dm_task *dmt, int minor);
+int dm_task_set_major(struct dm_task *dmt, int major);
+int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor, int allow_default_major_fallback);
+int dm_task_set_uid(struct dm_task *dmt, uid_t uid);
+int dm_task_set_gid(struct dm_task *dmt, gid_t gid);
+int dm_task_set_mode(struct dm_task *dmt, mode_t mode);
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags);
+int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr);
+int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start);
+int dm_task_set_message(struct dm_task *dmt, const char *message);
+int dm_task_set_sector(struct dm_task *dmt, uint64_t sector);
+int dm_task_no_flush(struct dm_task *dmt);
+int dm_task_no_open_count(struct dm_task *dmt);
+int dm_task_skip_lockfs(struct dm_task *dmt);
+int dm_task_query_inactive_table(struct dm_task *dmt);
+int dm_task_suppress_identical_reload(struct dm_task *dmt);
+
+/*
+ * Control read_ahead.
+ */
+#define DM_READ_AHEAD_AUTO UINT32_MAX  /* Use kernel default readahead */
+#define DM_READ_AHEAD_NONE 0           /* Disable readahead */
+
+#define DM_READ_AHEAD_MINIMUM_FLAG     0x1     /* Value supplied is minimum */
+
+/*
+ * Read ahead is set with DM_DEVICE_CREATE with a table or DM_DEVICE_RESUME.
+ */
+int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
+                          uint32_t read_ahead_flags);
+uint32_t dm_task_get_read_ahead(const struct dm_task *dmt,
+                               uint32_t *read_ahead);
+
+/*
+ * Use these to prepare for a create or reload.
+ */
+int dm_task_add_target(struct dm_task *dmt,
+                      uint64_t start,
+                      uint64_t size, const char *ttype, const char *params);
+
+/*
+ * Format major/minor numbers correctly for input to driver.
+ */
+#define DM_FORMAT_DEV_BUFSIZE  13      /* Minimum bufsize to handle worst case. */
+int dm_format_dev(char *buf, int bufsize, uint32_t dev_major, uint32_t dev_minor);
+
+/* Use this to retrive target information returned from a STATUS call */
+void *dm_get_next_target(struct dm_task *dmt,
+                        void *next, uint64_t *start, uint64_t *length,
+                        char **target_type, char **params);
+
+/*
+ * Call this to actually run the ioctl.
+ */
+int dm_task_run(struct dm_task *dmt);
+
+/*
+ * Call this to make or remove the device nodes associated with previously
+ * issued commands.
+ */
+void dm_task_update_nodes(void);
+
+/*
+ * Configure the device-mapper directory
+ */
+int dm_set_dev_dir(const char *dir);
+const char *dm_dir(void);
+
+/*
+ * Determine whether a major number belongs to device-mapper or not.
+ */
+int dm_is_dm_major(uint32_t major);
+
+/*
+ * Release library resources
+ */
+void dm_lib_release(void);
+void dm_lib_exit(void) __attribute__((destructor));
+
+/*
+ * Use NULL for all devices.
+ */
+int dm_mknodes(const char *name);
+int dm_driver_version(char *version, size_t size);
+
+/******************************************************
+ * Functions to build and manipulate trees of devices *
+ ******************************************************/
+struct dm_tree;
+struct dm_tree_node;
+
+/*
+ * Initialise an empty dependency tree.
+ *
+ * The tree consists of a root node together with one node for each mapped 
+ * device which has child nodes for each device referenced in its table.
+ *
+ * Every node in the tree has one or more children and one or more parents.
+ *
+ * The root node is the parent/child of every node that doesn't have other 
+ * parents/children.
+ */
+struct dm_tree *dm_tree_create(void);
+void dm_tree_free(struct dm_tree *tree);
+
+/*
+ * Add nodes to the tree for a given device and all the devices it uses.
+ */
+int dm_tree_add_dev(struct dm_tree *tree, uint32_t major, uint32_t minor);
+int dm_tree_add_dev_with_udev_flags(struct dm_tree *tree, uint32_t major,
+                                   uint32_t minor, uint16_t udev_flags);
+
+/*
+ * Add a new node to the tree if it doesn't already exist.
+ */
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *tree,
+                                        const char *name,
+                                        const char *uuid,
+                                        uint32_t major, uint32_t minor,
+                                        int read_only,
+                                        int clear_inactive,
+                                        void *context);
+struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *tree,
+                                                        const char *name,
+                                                        const char *uuid,
+                                                        uint32_t major,
+                                                        uint32_t minor,
+                                                        int read_only,
+                                                        int clear_inactive,
+                                                        void *context,
+                                                        uint16_t udev_flags);
+
+/*
+ * Search for a node in the tree.
+ * Set major and minor to 0 or uuid to NULL to get the root node.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *tree,
+                                         uint32_t major,
+                                         uint32_t minor);
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *tree,
+                                                 const char *uuid);
+
+/*
+ * Use this to walk through all children of a given node.
+ * Set handle to NULL in first call.
+ * Returns NULL after the last child.
+ * Set inverted to use inverted tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+                                       const struct dm_tree_node *parent,
+                                       uint32_t inverted);
+
+/*
+ * Get properties of a node.
+ */
+const char *dm_tree_node_get_name(const struct dm_tree_node *node);
+const char *dm_tree_node_get_uuid(const struct dm_tree_node *node);
+const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node);
+void *dm_tree_node_get_context(const struct dm_tree_node *node);
+int dm_tree_node_size_changed(const struct dm_tree_node *dnode);
+
+/*
+ * Returns the number of children of the given node (excluding the root node).
+ * Set inverted for the number of parents.
+ */
+int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted);
+
+/*
+ * Deactivate a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+                                  const char *uuid_prefix,
+                                  size_t uuid_prefix_len);
+/*
+ * Preload/create a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+                            const char *uuid_prefix,
+                            size_t uuid_prefix_len);
+
+/*
+ * Resume a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+                             const char *uuid_prefix,
+                             size_t uuid_prefix_len);
+
+/*
+ * Suspend a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+                                  const char *uuid_prefix,
+                                  size_t uuid_prefix_len);
+
+/*
+ * Skip the filesystem sync when suspending.
+ * Does nothing with other functions.
+ * Use this when no snapshots are involved.
+ */ 
+void dm_tree_skip_lockfs(struct dm_tree_node *dnode);
+
+/*
+ * Set the 'noflush' flag when suspending devices.
+ * If the kernel supports it, instead of erroring outstanding I/O that
+ * cannot be completed, the I/O is queued and resubmitted when the
+ * device is resumed.  This affects multipath devices when all paths
+ * have failed and queue_if_no_path is set, and mirror devices when
+ * block_on_error is set and the mirror log has failed.
+ */
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode);
+
+/*
+ * Is the uuid prefix present in the tree?
+ * Only returns 0 if every node was checked successfully.
+ * Returns 1 if the tree walk has to be aborted.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+                                const char *uuid_prefix,
+                                size_t uuid_prefix_len);
+
+/*
+ * Construct tables for new nodes before activating them.
+ */
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+                                              uint64_t size,
+                                              const char *origin_uuid);
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+                                       uint64_t size,
+                                       const char *origin_uuid,
+                                       const char *cow_uuid,
+                                       int persistent,
+                                       uint32_t chunk_size);
+int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
+                                            uint64_t size,
+                                            const char *origin_uuid,
+                                            const char *cow_uuid,
+                                            const char *merge_uuid,
+                                            uint32_t chunk_size);
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+                                    uint64_t size);
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+                                   uint64_t size);
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+                                     uint64_t size);
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+                                      uint64_t size,
+                                      uint32_t stripe_size);
+
+#define DM_CRYPT_IV_DEFAULT    UINT64_C(-1)    /* iv_offset == seg offset */
+/*
+ * Function accepts one string in cipher specification
+ * (chainmode and iv should be NULL because included in cipher string)
+ *   or
+ * separate arguments which will be joined to "cipher-chainmode-iv"
+ */
+int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
+                                 uint64_t size,
+                                 const char *cipher,
+                                 const char *chainmode,
+                                 const char *iv,
+                                 uint64_t iv_offset,
+                                 const char *key);
+int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
+                                     uint64_t size);
+/* Mirror log flags */
+#define DM_NOSYNC              0x00000001      /* Known already in sync */
+#define DM_FORCESYNC           0x00000002      /* Force resync */
+#define DM_BLOCK_ON_ERROR      0x00000004      /* On error, suspend I/O */
+#define DM_CORELOG             0x00000008      /* In-memory log */
+
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+                                         uint32_t region_size,
+                                         unsigned clustered,
+                                         const char *log_uuid,
+                                         unsigned area_count,
+                                         uint32_t flags);
+
+/*
+ * Replicator operation mode
+ * Note: API for Replicator is not yet stable
+ */
+typedef enum {
+       DM_REPLICATOR_SYNC,                     /* Synchronous replication */
+       DM_REPLICATOR_ASYNC_WARN,               /* Warn if async replicator is slow */
+       DM_REPLICATOR_ASYNC_STALL,              /* Stall replicator if not fast enough */
+       DM_REPLICATOR_ASYNC_DROP,               /* Drop sites out of sync */
+       DM_REPLICATOR_ASYNC_FAIL,               /* Fail replicator if slow */
+       NUM_DM_REPLICATOR_MODES
+} dm_replicator_mode_t;
+
+int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
+                                      uint64_t size,
+                                      const char *rlog_uuid,
+                                      const char *rlog_type,
+                                      unsigned rsite_index,
+                                      dm_replicator_mode_t mode,
+                                      uint32_t async_timeout,
+                                      uint64_t fall_behind_data,
+                                      uint32_t fall_behind_ios);
+
+int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
+                                          uint64_t size,
+                                          const char *replicator_uuid, /* Replicator control device */
+                                          uint64_t rdevice_index,
+                                          const char *rdev_uuid,       /* Rimage device name/uuid */
+                                          unsigned rsite_index,
+                                          const char *slog_uuid,
+                                          uint32_t slog_flags,         /* Mirror log flags */
+                                          uint32_t slog_region_size);
+/* End of Replicator API */
+
+void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
+                                     struct dm_tree_node *presuspend_node);
+
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+                                   const char *dev_name,
+                                   const char *dlid,
+                                   uint64_t offset);
+
+/*
+ * Set readahead (in sectors) after loading the node.
+ */
+void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
+                                uint32_t read_ahead,
+                                uint32_t read_ahead_flags);
+
+void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie);
+uint32_t dm_tree_get_cookie(struct dm_tree_node *node);
+
+/*****************************************************************************
+ * Library functions
+ *****************************************************************************/
+
+/*******************
+ * Memory management
+ *******************/
+
+void *dm_malloc_aux(size_t s, const char *file, int line);
+void *dm_malloc_aux_debug(size_t s, const char *file, int line);
+void *dm_zalloc_aux(size_t s, const char *file, int line);
+void *dm_zalloc_aux_debug(size_t s, const char *file, int line);
+char *dm_strdup_aux(const char *str, const char *file, int line);
+void dm_free_aux(void *p);
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line);
+int dm_dump_memory_debug(void);
+void dm_bounds_check_debug(void);
+
+#ifdef DEBUG_MEM
+
+#  define dm_malloc(s) dm_malloc_aux_debug((s), __FILE__, __LINE__)
+#  define dm_zalloc(s) dm_zalloc_aux_debug((s), __FILE__, __LINE__)
+#  define dm_strdup(s) dm_strdup_aux((s), __FILE__, __LINE__)
+#  define dm_free(p) dm_free_aux(p)
+#  define dm_realloc(p, s) dm_realloc_aux(p, s, __FILE__, __LINE__)
+#  define dm_dump_memory() dm_dump_memory_debug()
+#  define dm_bounds_check() dm_bounds_check_debug()
+
+#else
+
+#  define dm_malloc(s) dm_malloc_aux((s), __FILE__, __LINE__)
+#  define dm_zalloc(s) dm_zalloc_aux((s), __FILE__, __LINE__)
+#  define dm_strdup(s) strdup(s)
+#  define dm_free(p) free(p)
+#  define dm_realloc(p, s) realloc(p, s)
+#  define dm_dump_memory() {}
+#  define dm_bounds_check() {}
+
+#endif
+
+
+/*
+ * The pool allocator is useful when you are going to allocate
+ * lots of memory, use the memory for a bit, and then free the
+ * memory in one go.  A surprising amount of code has this usage
+ * profile.
+ *
+ * You should think of the pool as an infinite, contiguous chunk
+ * of memory.  The front of this chunk of memory contains
+ * allocated objects, the second half is free.  dm_pool_alloc grabs
+ * the next 'size' bytes from the free half, in effect moving it
+ * into the allocated half.  This operation is very efficient.
+ *
+ * dm_pool_free frees the allocated object *and* all objects
+ * allocated after it.  It is important to note this semantic
+ * difference from malloc/free.  This is also extremely
+ * efficient, since a single dm_pool_free can dispose of a large
+ * complex object.
+ *
+ * dm_pool_destroy frees all allocated memory.
+ *
+ * eg, If you are building a binary tree in your program, and
+ * know that you are only ever going to insert into your tree,
+ * and not delete (eg, maintaining a symbol table for a
+ * compiler).  You can create yourself a pool, allocate the nodes
+ * from it, and when the tree becomes redundant call dm_pool_destroy
+ * (no nasty iterating through the tree to free nodes).
+ *
+ * eg, On the other hand if you wanted to repeatedly insert and
+ * remove objects into the tree, you would be better off
+ * allocating the nodes from a free list; you cannot free a
+ * single arbitrary node with pool.
+ */
+
+struct dm_pool;
+
+/* constructor and destructor */
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint);
+void dm_pool_destroy(struct dm_pool *p);
+
+/* simple allocation/free routines */
+void *dm_pool_alloc(struct dm_pool *p, size_t s);
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment);
+void dm_pool_empty(struct dm_pool *p);
+void dm_pool_free(struct dm_pool *p, void *ptr);
+
+/*
+ * Object building routines:
+ *
+ * These allow you to 'grow' an object, useful for
+ * building strings, or filling in dynamic
+ * arrays.
+ *
+ * It's probably best explained with an example:
+ *
+ * char *build_string(struct dm_pool *mem)
+ * {
+ *      int i;
+ *      char buffer[16];
+ *
+ *      if (!dm_pool_begin_object(mem, 128))
+ *              return NULL;
+ *
+ *      for (i = 0; i < 50; i++) {
+ *              snprintf(buffer, sizeof(buffer), "%d, ", i);
+ *              if (!dm_pool_grow_object(mem, buffer, 0))
+ *                      goto bad;
+ *      }
+ *
+ *     // add null
+ *      if (!dm_pool_grow_object(mem, "\0", 1))
+ *              goto bad;
+ *
+ *      return dm_pool_end_object(mem);
+ *
+ * bad:
+ *
+ *      dm_pool_abandon_object(mem);
+ *      return NULL;
+ *}
+ *
+ * So start an object by calling dm_pool_begin_object
+ * with a guess at the final object size - if in
+ * doubt make the guess too small.
+ *
+ * Then append chunks of data to your object with
+ * dm_pool_grow_object.  Finally get your object with
+ * a call to dm_pool_end_object.
+ *
+ * Setting delta to 0 means it will use strlen(extra).
+ */
+int dm_pool_begin_object(struct dm_pool *p, size_t hint);
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta);
+void *dm_pool_end_object(struct dm_pool *p);
+void dm_pool_abandon_object(struct dm_pool *p);
+
+/* utilities */
+char *dm_pool_strdup(struct dm_pool *p, const char *str);
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n);
+void *dm_pool_zalloc(struct dm_pool *p, size_t s);
+
+/******************
+ * bitset functions
+ ******************/
+
+typedef uint32_t *dm_bitset_t;
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits);
+void dm_bitset_destroy(dm_bitset_t bs);
+
+int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2);
+
+void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+int dm_bit_get_first(dm_bitset_t bs);
+int dm_bit_get_next(dm_bitset_t bs, int last_bit);
+
+#define DM_BITS_PER_INT (sizeof(int) * CHAR_BIT)
+
+#define dm_bit(bs, i) \
+   ((bs)[((i) / DM_BITS_PER_INT) + 1] & (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set(bs, i) \
+   ((bs)[((i) / DM_BITS_PER_INT) + 1] |= (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_clear(bs, i) \
+   ((bs)[((i) / DM_BITS_PER_INT) + 1] &= ~(0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set_all(bs) \
+   memset((bs) + 1, -1, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_clear_all(bs) \
+   memset((bs) + 1, 0, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_copy(bs1, bs2) \
+   memcpy((bs1) + 1, (bs2) + 1, ((*(bs1) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+/* Returns number of set bits */
+static inline unsigned hweight32(uint32_t i)
+{
+       unsigned r = (i & 0x55555555) + ((i >> 1) & 0x55555555);
+
+       r =    (r & 0x33333333) + ((r >>  2) & 0x33333333);
+       r =    (r & 0x0F0F0F0F) + ((r >>  4) & 0x0F0F0F0F);
+       r =    (r & 0x00FF00FF) + ((r >>  8) & 0x00FF00FF);
+       return (r & 0x0000FFFF) + ((r >> 16) & 0x0000FFFF);
+}
+
+/****************
+ * hash functions
+ ****************/
+
+struct dm_hash_table;
+struct dm_hash_node;
+
+typedef void (*dm_hash_iterate_fn) (void *data);
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint);
+void dm_hash_destroy(struct dm_hash_table *t);
+void dm_hash_wipe(struct dm_hash_table *t);
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key);
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data);
+void dm_hash_remove(struct dm_hash_table *t, const char *key);
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *key, uint32_t len);
+int dm_hash_insert_binary(struct dm_hash_table *t, const char *key, uint32_t len,
+                      void *data);
+void dm_hash_remove_binary(struct dm_hash_table *t, const char *key, uint32_t len);
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t);
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f);
+
+char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n);
+void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n);
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t);
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n);
+
+#define dm_hash_iterate(v, h) \
+       for (v = dm_hash_get_first((h)); v; \
+            v = dm_hash_get_next((h), v))
+
+/****************
+ * list functions
+ ****************/
+
+/*
+ * A list consists of a list head plus elements.
+ * Each element has 'next' and 'previous' pointers.
+ * The list head's pointers point to the first and the last element.
+ */
+
+struct dm_list {
+       struct dm_list *n, *p;
+};
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+#define DM_LIST_INIT(name)     struct dm_list name = { &(name), &(name) }
+void dm_list_init(struct dm_list *head);
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem);
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Join 'head1' to the of 'head'.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1);
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head);
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head);
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head);
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Given the address v of an instance of 'struct dm_list' called 'head' 
+ * contained in a structure of type t, return the containing structure.
+ */
+#define dm_list_struct_base(v, t, head) \
+    ((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
+
+/*
+ * Given the address v of an instance of 'struct dm_list list' contained in
+ * a structure of type t, return the containing structure.
+ */
+#define dm_list_item(v, t) dm_list_struct_base((v), t, list)
+
+/*
+ * Given the address v of one known element e in a known structure of type t,
+ * return another element f.
+ */
+#define dm_struct_field(v, t, e, f) \
+    (((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->f)
+
+/*
+ * Given the address v of a known element e in a known structure of type t,
+ * return the list head 'list'
+ */
+#define dm_list_head(v, t, e) dm_struct_field(v, t, e, list)
+
+/*
+ * Set v to each element of a list in turn.
+ */
+#define dm_list_iterate(v, head) \
+       for (v = (head)->n; v != head; v = v->n)
+
+/*
+ * Set v to each element in a list in turn, starting from the element 
+ * in front of 'start'.
+ * You can use this to 'unwind' a list_iterate and back out actions on
+ * already-processed elements.
+ * If 'start' is 'head' it walks the list backwards.
+ */
+#define dm_list_uniterate(v, head, start) \
+       for (v = (start)->p; v != head; v = v->p)
+
+/*
+ * A safe way to walk a list and delete and free some elements along
+ * the way.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_safe(v, t, head) \
+       for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
+
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The 'struct dm_list' variable within the containing structure is 'field'.
+ */
+#define dm_list_iterate_items_gen(v, head, field) \
+       for (v = dm_list_struct_base((head)->n, __typeof__(*v), field); \
+            &v->field != (head); \
+            v = dm_list_struct_base(v->field.n, __typeof__(*v), field))
+
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The list should be 'struct dm_list list' within the containing structure.
+ */
+#define dm_list_iterate_items(v, head) dm_list_iterate_items_gen(v, (head), list)
+
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The 'struct dm_list' variable within the containing structure is 'field'.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_items_gen_safe(v, t, head, field) \
+       for (v = dm_list_struct_base((head)->n, __typeof__(*v), field), \
+            t = dm_list_struct_base(v->field.n, __typeof__(*v), field); \
+            &v->field != (head); \
+            v = t, t = dm_list_struct_base(v->field.n, __typeof__(*v), field))
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The list should be 'struct dm_list list' within the containing structure.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_items_safe(v, t, head) \
+       dm_list_iterate_items_gen_safe(v, t, (head), list)
+
+/*
+ * Walk a list backwards, setting 'v' in turn to the containing structure 
+ * of each item.
+ * The containing structure should be the same type as 'v'.
+ * The 'struct dm_list' variable within the containing structure is 'field'.
+ */
+#define dm_list_iterate_back_items_gen(v, head, field) \
+       for (v = dm_list_struct_base((head)->p, __typeof__(*v), field); \
+            &v->field != (head); \
+            v = dm_list_struct_base(v->field.p, __typeof__(*v), field))
+
+/*
+ * Walk a list backwards, setting 'v' in turn to the containing structure 
+ * of each item.
+ * The containing structure should be the same type as 'v'.
+ * The list should be 'struct dm_list list' within the containing structure.
+ */
+#define dm_list_iterate_back_items(v, head) dm_list_iterate_back_items_gen(v, (head), list)
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head);
+
+/*********
+ * selinux
+ *********/
+
+/*
+ * Obtain SELinux security context assigned for the path and set this
+ * context for creating a new file system object. This security context
+ * is global and it is used until reset to default policy behaviour
+ * by calling 'dm_prepare_selinux_context(NULL, 0)'.
+ */
+int dm_prepare_selinux_context(const char *path, mode_t mode);
+/*
+ * Set SELinux context for existing file system object.
+ */
+int dm_set_selinux_context(const char *path, mode_t mode);
+
+/*********************
+ * string manipulation
+ *********************/
+
+/*
+ * Break up the name of a mapped device into its constituent
+ * Volume Group, Logical Volume and Layer (if present).
+ * If mem is supplied, the result is allocated from the mempool.
+ * Otherwise the strings are changed in situ.
+ */
+int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
+                     char **vgname, char **lvname, char **layer);
+
+/*
+ * Destructively split buffer into NULL-separated words in argv.
+ * Returns number of words.
+ */
+int dm_split_words(char *buffer, unsigned max,
+                  unsigned ignore_comments, /* Not implemented */
+                  char **argv);
+
+/* 
+ * Returns -1 if buffer too small
+ */
+int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
+    __attribute__ ((format(printf, 3, 4)));
+
+/*
+ * Returns pointer to the last component of the path.
+ */
+const char *dm_basename(const char *path);
+
+/**************************
+ * file/stream manipulation
+ **************************/
+
+/*
+ * Create a directory (with parent directories if necessary).
+ * Returns 1 on success, 0 on failure.
+ */
+int dm_create_dir(const char *dir);
+
+/*
+ * Close a stream, with nicer error checking than fclose's.
+ * Derived from gnulib's close-stream.c.
+ *
+ * Close "stream".  Return 0 if successful, and EOF (setting errno)
+ * otherwise.  Upon failure, set errno to 0 if the error number
+ * cannot be determined.  Useful mainly for writable streams.
+ */
+int dm_fclose(FILE *stream);
+
+/*
+ * Returns size of a buffer which is allocated with dm_malloc.
+ * Pointer to the buffer is stored in *buf.
+ * Returns -1 on failure leaving buf undefined.
+ */
+int dm_asprintf(char **buf, const char *format, ...)
+    __attribute__ ((format(printf, 2, 3)));
+
+/*
+ * create lockfile (pidfile) - create and lock a lock file
+ * @lockfile: location of lock file
+ *
+ * Returns: 1 on success, 0 otherwise, errno is handled internally
+ */
+int dm_create_lockfile(const char* lockfile);
+
+/*
+ * Query whether a daemon is running based on its lockfile
+ *
+ * Returns: 1 if running, 0 if not
+ */
+int dm_daemon_is_running(const char* lockfile);
+
+/*********************
+ * regular expressions
+ *********************/
+struct dm_regex;
+
+/*
+ * Initialise an array of num patterns for matching.
+ * Uses memory from mem.
+ */
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
+                                unsigned num_patterns);
+
+/*
+ * Match string s against the patterns.
+ * Returns the index of the highest pattern in the array that matches,
+ * or -1 if none match.
+ */
+int dm_regex_match(struct dm_regex *regex, const char *s);
+
+/*
+ * This is useful for regression testing only.  The idea is if two
+ * fingerprints are different, then the two dfas are certainly not
+ * isomorphic.  If two fingerprints _are_ the same then it's very likely
+ * that the dfas are isomorphic.
+ *
+ * This function must be called before any matching is done.
+ */
+uint32_t dm_regex_fingerprint(struct dm_regex *regex);
+
+/*********************
+ * reporting functions
+ *********************/
+
+struct dm_report_object_type {
+       uint32_t id;                    /* Powers of 2 */
+       const char *desc;
+       const char *prefix;             /* field id string prefix (optional) */
+       void *(*data_fn)(void *object); /* callback from report_object() */
+};
+
+struct dm_report_field;
+
+/*
+ * dm_report_field_type flags
+ */
+#define DM_REPORT_FIELD_MASK           0x000000FF
+#define DM_REPORT_FIELD_ALIGN_MASK     0x0000000F
+#define DM_REPORT_FIELD_ALIGN_LEFT     0x00000001
+#define DM_REPORT_FIELD_ALIGN_RIGHT    0x00000002
+#define DM_REPORT_FIELD_TYPE_MASK      0x000000F0
+#define DM_REPORT_FIELD_TYPE_STRING    0x00000010
+#define DM_REPORT_FIELD_TYPE_NUMBER    0x00000020
+
+#define DM_REPORT_FIELD_TYPE_ID_LEN 32
+#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
+
+struct dm_report;
+struct dm_report_field_type {
+       uint32_t type;          /* object type id */
+       uint32_t flags;         /* DM_REPORT_FIELD_* */
+       uint32_t offset;        /* byte offset in the object */
+       int32_t width;          /* default width */
+       /* string used to specify the field */
+       const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
+       /* string printed in header */
+       const char heading[DM_REPORT_FIELD_TYPE_HEADING_LEN];
+       int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
+                        struct dm_report_field *field, const void *data,
+                        void *private_data);
+       const char *desc;       /* description of the field */
+};
+
+/*
+ * dm_report_init output_flags
+ */
+#define DM_REPORT_OUTPUT_MASK                  0x000000FF
+#define DM_REPORT_OUTPUT_ALIGNED               0x00000001
+#define DM_REPORT_OUTPUT_BUFFERED              0x00000002
+#define DM_REPORT_OUTPUT_HEADINGS              0x00000004
+#define DM_REPORT_OUTPUT_FIELD_NAME_PREFIX     0x00000008
+#define DM_REPORT_OUTPUT_FIELD_UNQUOTED                0x00000010
+#define DM_REPORT_OUTPUT_COLUMNS_AS_ROWS       0x00000020
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+                                const struct dm_report_object_type *types,
+                                const struct dm_report_field_type *fields,
+                                const char *output_fields,
+                                const char *output_separator,
+                                uint32_t output_flags,
+                                const char *sort_keys,
+                                void *private_data);
+int dm_report_object(struct dm_report *rh, void *object);
+int dm_report_output(struct dm_report *rh);
+void dm_report_free(struct dm_report *rh);
+
+/*
+ * Prefix added to each field name with DM_REPORT_OUTPUT_FIELD_NAME_PREFIX
+ */
+int dm_report_set_output_field_name_prefix(struct dm_report *rh,
+                                          const char *report_prefix);
+
+/*
+ * Report functions are provided for simple data types.
+ * They take care of allocating copies of the data.
+ */
+int dm_report_field_string(struct dm_report *rh, struct dm_report_field *field,
+                          const char **data);
+int dm_report_field_int32(struct dm_report *rh, struct dm_report_field *field,
+                         const int32_t *data);
+int dm_report_field_uint32(struct dm_report *rh, struct dm_report_field *field,
+                          const uint32_t *data);
+int dm_report_field_int(struct dm_report *rh, struct dm_report_field *field,
+                       const int *data);
+int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field,
+                          const uint64_t *data);
+
+/*
+ * For custom fields, allocate the data in 'mem' and use
+ * dm_report_field_set_value().
+ * 'sortvalue' may be NULL if it matches 'value'
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value,
+                              const void *sortvalue);
+
+/* Cookie prefixes.
+ * The cookie value consists of a prefix (16 bits) and a base (16 bits).
+ * We can use the prefix to store the flags. These flags are sent to
+ * kernel within given dm task. When returned back to userspace in
+ * DM_COOKIE udev environment variable, we can control several aspects
+ * of udev rules we use by decoding the cookie prefix. When doing the
+ * notification, we replace the cookie prefix with DM_COOKIE_MAGIC,
+ * so we notify the right semaphore.
+ * It is still possible to use cookies for passing the flags to udev
+ * rules even when udev_sync is disabled. The base part of the cookie
+ * will be zero (there's no notification semaphore) and prefix will be
+ * set then. However, having udev_sync enabled is highly recommended.
+ */
+#define DM_COOKIE_MAGIC 0x0D4D
+#define DM_UDEV_FLAGS_MASK 0xFFFF0000
+#define DM_UDEV_FLAGS_SHIFT 16
+
+/*
+ * DM_UDEV_DISABLE_DM_RULES_FLAG is set in case we need to disable
+ * basic device-mapper udev rules that create symlinks in /dev/<DM_DIR>
+ * directory. However, we can't reliably prevent creating default
+ * nodes by udev (commonly /dev/dm-X, where X is a number).
+ */
+#define DM_UDEV_DISABLE_DM_RULES_FLAG 0x0001
+/*
+ * DM_UDEV_DISABLE_SUBSYTEM_RULES_FLAG is set in case we need to disable
+ * subsystem udev rules, but still we need the general DM udev rules to
+ * be applied (to create the nodes and symlinks under /dev and /dev/disk).
+ */
+#define DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG 0x0002
+/*
+ * DM_UDEV_DISABLE_DISK_RULES_FLAG is set in case we need to disable
+ * general DM rules that set symlinks in /dev/disk directory.
+ */
+#define DM_UDEV_DISABLE_DISK_RULES_FLAG 0x0004
+/*
+ * DM_UDEV_DISABLE_OTHER_RULES_FLAG is set in case we need to disable
+ * all the other rules that are not general device-mapper nor subsystem
+ * related (the rules belong to other software or packages). All foreign
+ * rules should check this flag directly and they should ignore further
+ * rule processing for such event.
+ */
+#define DM_UDEV_DISABLE_OTHER_RULES_FLAG 0x0008
+/*
+ * DM_UDEV_LOW_PRIORITY_FLAG is set in case we need to instruct the
+ * udev rules to give low priority to the device that is currently
+ * processed. For example, this provides a way to select which symlinks
+ * could be overwritten by high priority ones if their names are equal.
+ * Common situation is a name based on FS UUID while using origin and
+ * snapshot devices.
+ */
+#define DM_UDEV_LOW_PRIORITY_FLAG 0x0010
+/*
+ * DM_UDEV_DISABLE_LIBRARY_FALLBACK is set in case we need to disable
+ * libdevmapper's node management. We will rely on udev completely
+ * and there will be no fallback action provided by libdevmapper if
+ * udev does something improperly.
+ */
+#define DM_UDEV_DISABLE_LIBRARY_FALLBACK 0x0020
+/*
+ * DM_UDEV_PRIMARY_SOURCE_FLAG is automatically appended by
+ * libdevmapper for all ioctls generating udev uevents. Once used in
+ * udev rules, we know if this is a real "primary sourced" event or not.
+ * We need to distinguish real events originated in libdevmapper from
+ * any spurious events to gather all missing information (e.g. events
+ * generated as a result of "udevadm trigger" command or as a result
+ * of the "watch" udev rule).
+ */
+#define DM_UDEV_PRIMARY_SOURCE_FLAG 0x0040
+
+int dm_cookie_supported(void);
+
+/*
+ * Udev synchronisation functions.
+ */
+void dm_udev_set_sync_support(int sync_with_udev);
+int dm_udev_get_sync_support(void);
+void dm_udev_set_checking(int checking);
+int dm_udev_get_checking(void);
+int dm_udev_create_cookie(uint32_t *cookie);
+int dm_udev_complete(uint32_t cookie);
+int dm_udev_wait(uint32_t cookie);
+
+#define DM_DEV_DIR_UMASK 0022
+
+#ifdef __cplusplus
+}
+#endif
+#endif                         /* LIB_DEVICE_MAPPER_H */
diff --git a/libdm/libdevmapper.pc.in b/libdm/libdevmapper.pc.in
new file mode 100644 (file)
index 0000000..eb7071d
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: devmapper
+Description: device-mapper library
+Version: @DM_LIB_PATCHLEVEL@
+Cflags: -I${includedir} 
+Libs: -L${libdir} -ldevmapper
+Requires.private: @SELINUX_PC@ @UDEV_PC@
diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c
new file mode 100644 (file)
index 0000000..6d8bcbd
--- /dev/null
@@ -0,0 +1,1363 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+#include "kdev_t.h"
+#include "dm-ioctl.h"
+
+#include <stdarg.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#ifdef UDEV_SYNC_SUPPORT
+#  include <sys/types.h>
+#  include <sys/ipc.h>
+#  include <sys/sem.h>
+#  define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#  include <libudev.h>
+#endif
+
+#ifdef linux
+#  include <linux/fs.h>
+#endif
+
+#ifdef HAVE_SELINUX
+#  include <selinux/selinux.h>
+#endif
+#ifdef HAVE_SELINUX_LABEL_H
+#  include <selinux/label.h>
+#endif
+
+#define DEV_DIR "/dev/"
+
+#ifdef UDEV_SYNC_SUPPORT
+#ifdef _SEM_SEMUN_UNDEFINED
+union semun
+{
+       int val;                        /* value for SETVAL */
+       struct semid_ds *buf;           /* buffer for IPC_STAT & IPC_SET */
+       unsigned short int *array;      /* array for GETALL & SETALL */
+       struct seminfo *__buf;          /* buffer for IPC_INFO */
+};
+#endif
+#endif
+
+static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR;
+
+static int _verbose = 0;
+
+#ifdef HAVE_SELINUX_LABEL_H
+static struct selabel_handle *_selabel_handle = NULL;
+#endif
+
+#ifdef UDEV_SYNC_SUPPORT
+static int _semaphore_supported = -1;
+static int _udev_running = -1;
+static int _sync_with_udev = 1;
+static int _udev_checking = 1;
+#endif
+
+/*
+ * Library users can provide their own logging
+ * function.
+ */
+
+static void _default_log_line(int level,
+           const char *file __attribute__((unused)),
+           int line __attribute__((unused)), int dm_errno, 
+           const char *f, va_list ap)
+{
+       int use_stderr = level & _LOG_STDERR;
+
+       level &= ~_LOG_STDERR;
+
+       if (level > _LOG_WARN && !_verbose)
+               return;
+
+       if (level < _LOG_WARN)
+               vfprintf(stderr, f, ap);
+       else
+               vfprintf(use_stderr ? stderr : stdout, f, ap);
+
+       if (level < _LOG_WARN)
+               fprintf(stderr, "\n");
+       else
+               fprintf(use_stderr ? stderr : stdout, "\n");
+}
+
+static void _default_log_with_errno(int level,
+           const char *file __attribute__((unused)),
+           int line __attribute__((unused)), int dm_errno, 
+           const char *f, ...)
+{
+       va_list ap;
+
+       va_start(ap, f);
+       _default_log_line(level, file, line, dm_errno, f, ap);
+       va_end(ap);
+}
+
+static void _default_log(int level, const char *file,
+                        int line, const char *f, ...)
+{
+       va_list ap;
+
+       va_start(ap, f);
+       _default_log_line(level, file, line, 0, f, ap);
+       va_end(ap);
+}
+
+dm_log_fn dm_log = _default_log;
+dm_log_with_errno_fn dm_log_with_errno = _default_log_with_errno;
+
+void dm_log_init(dm_log_fn fn)
+{
+       if (fn)
+               dm_log = fn;
+       else
+               dm_log = _default_log;
+
+       dm_log_with_errno = _default_log_with_errno;
+}
+
+int dm_log_is_non_default(void)
+{
+       return (dm_log == _default_log) ? 0 : 1;
+}
+
+void dm_log_with_errno_init(dm_log_with_errno_fn fn)
+{
+       if (fn)
+               dm_log_with_errno = fn;
+       else
+               dm_log_with_errno = _default_log_with_errno;
+
+       dm_log = _default_log;
+}
+
+void dm_log_init_verbose(int level)
+{
+       _verbose = level;
+}
+
+static void _build_dev_path(char *buffer, size_t len, const char *dev_name)
+{
+       /* If there's a /, assume caller knows what they're doing */
+       if (strchr(dev_name, '/'))
+               snprintf(buffer, len, "%s", dev_name);
+       else
+               snprintf(buffer, len, "%s/%s", _dm_dir, dev_name);
+}
+
+int dm_get_library_version(char *version, size_t size)
+{
+       strncpy(version, DM_LIB_VERSION, size);
+       return 1;
+}
+
+struct dm_task *dm_task_create(int type)
+{
+       struct dm_task *dmt = dm_zalloc(sizeof(*dmt));
+
+       if (!dmt) {
+               log_error("dm_task_create: malloc(%" PRIsize_t ") failed",
+                         sizeof(*dmt));
+               return NULL;
+       }
+
+       if (!dm_check_version()) {
+               dm_free(dmt);
+               return NULL;
+       }
+
+       dmt->type = type;
+       dmt->minor = -1;
+       dmt->major = -1;
+       dmt->allow_default_major_fallback = 1;
+       dmt->uid = DM_DEVICE_UID;
+       dmt->gid = DM_DEVICE_GID;
+       dmt->mode = DM_DEVICE_MODE;
+       dmt->no_open_count = 0;
+       dmt->read_ahead = DM_READ_AHEAD_AUTO;
+       dmt->read_ahead_flags = 0;
+       dmt->event_nr = 0;
+       dmt->cookie_set = 0;
+       dmt->query_inactive_table = 0;
+       dmt->new_uuid = 0;
+
+       return dmt;
+}
+
+/*
+ * Find the name associated with a given device number by scanning _dm_dir.
+ */
+static char *_find_dm_name_of_device(dev_t st_rdev)
+{
+       const char *name;
+       char path[PATH_MAX];
+       struct dirent *dirent;
+       DIR *d;
+       struct stat buf;
+       char *new_name = NULL;
+
+       if (!(d = opendir(_dm_dir))) {
+               log_sys_error("opendir", _dm_dir);
+               return NULL;
+       }
+
+       while ((dirent = readdir(d))) {
+               name = dirent->d_name;
+
+               if (!strcmp(name, ".") || !strcmp(name, ".."))
+                       continue;
+
+               if (dm_snprintf(path, sizeof(path), "%s/%s", _dm_dir,
+                               name) == -1) {
+                       log_error("Couldn't create path for %s", name);
+                       continue;
+               }
+
+               if (stat(path, &buf))
+                       continue;
+
+               if (buf.st_rdev == st_rdev) {
+                       if (!(new_name = dm_strdup(name)))
+                               log_error("dm_task_set_name: strdup(%s) failed",
+                                         name);
+                       break;
+               }
+       }
+
+       if (closedir(d))
+               log_sys_error("closedir", _dm_dir);
+
+       return new_name;
+}
+
+int dm_task_set_name(struct dm_task *dmt, const char *name)
+{
+       char *pos;
+       char *new_name = NULL;
+       char path[PATH_MAX];
+       struct stat st1, st2;
+
+       dm_free(dmt->dev_name);
+       dmt->dev_name = NULL;
+
+       /*
+        * Path supplied for existing device?
+        */
+       if ((pos = strrchr(name, '/'))) {
+               if (dmt->type == DM_DEVICE_CREATE) {
+                       log_error("Name \"%s\" invalid. It contains \"/\".", name);
+                       return 0;
+               }
+
+               if (stat(name, &st1)) {
+                       log_error("Device %s not found", name);
+                       return 0;
+               }
+
+               /*
+                * If supplied path points to same device as last component
+                * under /dev/mapper, use that name directly.  Otherwise call
+                * _find_dm_name_of_device() to scan _dm_dir for a match.
+                */
+               if (dm_snprintf(path, sizeof(path), "%s/%s", _dm_dir,
+                               pos + 1) == -1) {
+                       log_error("Couldn't create path for %s", pos + 1);
+                       return 0;
+               }
+
+               if (!stat(path, &st2) && (st1.st_rdev == st2.st_rdev))
+                       name = pos + 1;
+               else if ((new_name = _find_dm_name_of_device(st1.st_rdev)))
+                       name = new_name;
+               else {
+                       log_error("Device %s not found", name);
+                       return 0;
+               }
+       }
+
+       if (strlen(name) >= DM_NAME_LEN) {
+               log_error("Name \"%s\" too long", name);
+               dm_free(new_name);
+               return 0;
+       }
+
+       if (new_name)
+               dmt->dev_name = new_name;
+       else if (!(dmt->dev_name = dm_strdup(name))) {
+               log_error("dm_task_set_name: strdup(%s) failed", name);
+               return 0;
+       }
+
+       return 1;
+}
+
+int dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
+{
+       dm_free(dmt->uuid);
+
+       if (!(dmt->uuid = dm_strdup(uuid))) {
+               log_error("dm_task_set_uuid: strdup(%s) failed", uuid);
+               return 0;
+       }
+
+       return 1;
+}
+
+int dm_task_set_major(struct dm_task *dmt, int major)
+{
+       dmt->major = major;
+       dmt->allow_default_major_fallback = 0;
+
+       return 1;
+}
+
+int dm_task_set_minor(struct dm_task *dmt, int minor)
+{
+       dmt->minor = minor;
+
+       return 1;
+}
+
+int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor,
+                           int allow_default_major_fallback)
+{
+       dmt->major = major;
+       dmt->minor = minor;
+       dmt->allow_default_major_fallback = allow_default_major_fallback;
+
+       return 1;
+}
+
+int dm_task_set_uid(struct dm_task *dmt, uid_t uid)
+{
+       dmt->uid = uid;
+
+       return 1;
+}
+
+int dm_task_set_gid(struct dm_task *dmt, gid_t gid)
+{
+       dmt->gid = gid;
+
+       return 1;
+}
+
+int dm_task_set_mode(struct dm_task *dmt, mode_t mode)
+{
+       dmt->mode = mode;
+
+       return 1;
+}
+
+int dm_task_add_target(struct dm_task *dmt, uint64_t start, uint64_t size,
+                      const char *ttype, const char *params)
+{
+       struct target *t = create_target(start, size, ttype, params);
+
+       if (!t)
+               return 0;
+
+       if (!dmt->head)
+               dmt->head = dmt->tail = t;
+       else {
+               dmt->tail->next = t;
+               dmt->tail = t;
+       }
+
+       return 1;
+}
+
+#ifdef HAVE_SELINUX
+static int _selabel_lookup(const char *path, mode_t mode,
+                          security_context_t *scontext)
+{
+#ifdef HAVE_SELINUX_LABEL_H
+       if (!_selabel_handle &&
+           !(_selabel_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0))) {
+               log_error("selabel_open failed: %s", strerror(errno));
+               return 0;
+       }
+
+       if (selabel_lookup(_selabel_handle, scontext, path, mode)) {
+               log_error("selabel_lookup failed: %s", strerror(errno));
+               return 0;
+       }
+#else
+       if (matchpathcon(path, mode, scontext)) {
+               log_error("matchpathcon failed: %s", strerror(errno));
+               return 0;
+       }
+#endif
+       return 1;
+}
+#endif
+
+int dm_prepare_selinux_context(const char *path, mode_t mode)
+{
+#ifdef HAVE_SELINUX
+       security_context_t scontext = NULL;
+
+       if (is_selinux_enabled() <= 0)
+               return 1;
+
+       if (path) {
+               if (!_selabel_lookup(path, mode, &scontext))
+                       return_0;
+
+               log_debug("Preparing SELinux context for %s to %s.", path, scontext);
+       }
+       else
+               log_debug("Resetting SELinux context to default value.");
+
+       if (setfscreatecon(scontext) < 0) {
+               log_sys_error("setfscreatecon", path);
+               freecon(scontext);
+               return 0;
+       }
+
+       freecon(scontext);
+#endif
+       return 1;
+}
+
+int dm_set_selinux_context(const char *path, mode_t mode)
+{
+#ifdef HAVE_SELINUX
+       security_context_t scontext;
+
+       if (is_selinux_enabled() <= 0)
+               return 1;
+
+       if (!_selabel_lookup(path, mode, &scontext))
+               return_0;
+
+       log_debug("Setting SELinux context for %s to %s.", path, scontext);
+
+       if ((lsetfilecon(path, scontext) < 0) && (errno != ENOTSUP)) {
+               log_sys_error("lsetfilecon", path);
+               freecon(scontext);
+               return 0;
+       }
+
+       freecon(scontext);
+#endif
+       return 1;
+}
+
+void selinux_release(void)
+{
+#ifdef HAVE_SELINUX_LABEL_H
+       if (_selabel_handle)
+               selabel_close(_selabel_handle);
+       _selabel_handle = NULL;
+#endif
+}
+
+static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
+                        uid_t uid, gid_t gid, mode_t mode, int check_udev)
+{
+       char path[PATH_MAX];
+       struct stat info;
+       dev_t dev = MKDEV(major, minor);
+       mode_t old_mask;
+
+       _build_dev_path(path, sizeof(path), dev_name);
+
+       if (stat(path, &info) >= 0) {
+               if (!S_ISBLK(info.st_mode)) {
+                       log_error("A non-block device file at '%s' "
+                                 "is already present", path);
+                       return 0;
+               }
+
+               /* If right inode already exists we don't touch uid etc. */
+               if (info.st_rdev == dev)
+                       return 1;
+
+               if (unlink(path) < 0) {
+                       log_error("Unable to unlink device node for '%s'",
+                                 dev_name);
+                       return 0;
+               }
+       } else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+                  check_udev)
+               log_warn("%s not set up by udev: Falling back to direct "
+                        "node creation.", path);
+
+       (void) dm_prepare_selinux_context(path, S_IFBLK);
+       old_mask = umask(0);
+       if (mknod(path, S_IFBLK | mode, dev) < 0) {
+               log_error("Unable to make device node for '%s'", dev_name);
+               umask(old_mask);
+               (void) dm_prepare_selinux_context(NULL, 0);
+               return 0;
+       }
+       umask(old_mask);
+       (void) dm_prepare_selinux_context(NULL, 0);
+
+       if (chown(path, uid, gid) < 0) {
+               log_sys_error("chown", path);
+               return 0;
+       }
+
+       log_debug("Created %s", path);
+
+       return 1;
+}
+
+static int _rm_dev_node(const char *dev_name, int check_udev)
+{
+       char path[PATH_MAX];
+       struct stat info;
+
+       _build_dev_path(path, sizeof(path), dev_name);
+
+       if (stat(path, &info) < 0)
+               return 1;
+       else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+                check_udev)
+               log_warn("Node %s was not removed by udev. "
+                        "Falling back to direct node removal.", path);
+
+       if (unlink(path) < 0) {
+               log_error("Unable to unlink device node for '%s'", dev_name);
+               return 0;
+       }
+
+       log_debug("Removed %s", path);
+
+       return 1;
+}
+
+static int _rename_dev_node(const char *old_name, const char *new_name,
+                           int check_udev)
+{
+       char oldpath[PATH_MAX];
+       char newpath[PATH_MAX];
+       struct stat info;
+
+       _build_dev_path(oldpath, sizeof(oldpath), old_name);
+       _build_dev_path(newpath, sizeof(newpath), new_name);
+
+       if (stat(newpath, &info) == 0) {
+               if (!S_ISBLK(info.st_mode)) {
+                       log_error("A non-block device file at '%s' "
+                                 "is already present", newpath);
+                       return 0;
+               }
+               else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+                        check_udev) {
+                       if (stat(oldpath, &info) < 0 &&
+                                errno == ENOENT)
+                               /* assume udev already deleted this */
+                               return 1;
+                       else {
+                               log_warn("The node %s should have been renamed to %s "
+                                        "by udev but old node is still present. "
+                                        "Falling back to direct old node removal.",
+                                        oldpath, newpath);
+                               return _rm_dev_node(old_name, 0);
+                       }
+               }
+
+               if (unlink(newpath) < 0) {
+                       if (errno == EPERM) {
+                               /* devfs, entry has already been renamed */
+                               return 1;
+                       }
+                       log_error("Unable to unlink device node for '%s'",
+                                 new_name);
+                       return 0;
+               }
+       }
+       else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+                check_udev)
+               log_warn("The node %s should have been renamed to %s "
+                        "by udev but new node is not present. "
+                        "Falling back to direct node rename.",
+                        oldpath, newpath);
+
+       if (rename(oldpath, newpath) < 0) {
+               log_error("Unable to rename device node from '%s' to '%s'",
+                         old_name, new_name);
+               return 0;
+       }
+
+       log_debug("Renamed %s to %s", oldpath, newpath);
+
+       return 1;
+}
+
+#ifdef linux
+static int _open_dev_node(const char *dev_name)
+{
+       int fd = -1;
+       char path[PATH_MAX];
+
+       _build_dev_path(path, sizeof(path), dev_name);
+
+       if ((fd = open(path, O_RDONLY, 0)) < 0)
+               log_sys_error("open", path);
+
+       return fd;
+}
+
+int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
+{
+       int r = 1;
+       int fd;
+       long read_ahead_long;
+
+       if (!*dev_name) {
+               log_error("Empty device name passed to BLKRAGET");
+               return 0;
+       }
+
+       if ((fd = _open_dev_node(dev_name)) < 0)
+               return_0;
+
+       if (ioctl(fd, BLKRAGET, &read_ahead_long)) {
+               log_sys_error("BLKRAGET", dev_name);
+               *read_ahead = 0;
+               r = 0;
+       }  else {
+               *read_ahead = (uint32_t) read_ahead_long;
+               log_debug("%s: read ahead is %" PRIu32, dev_name, *read_ahead);
+       }
+
+       if (close(fd))
+               stack;
+
+       return r;
+}
+
+static int _set_read_ahead(const char *dev_name, uint32_t read_ahead)
+{
+       int r = 1;
+       int fd;
+       long read_ahead_long = (long) read_ahead;
+
+       if (!*dev_name) {
+               log_error("Empty device name passed to BLKRAGET");
+               return 0;
+       }
+
+       if ((fd = _open_dev_node(dev_name)) < 0)
+               return_0;
+
+       log_debug("%s: Setting read ahead to %" PRIu32, dev_name, read_ahead);
+
+       if (ioctl(fd, BLKRASET, read_ahead_long)) {
+               log_sys_error("BLKRASET", dev_name);
+               r = 0;
+       }
+
+       if (close(fd))
+               stack;
+
+       return r;
+}
+
+static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+                                   uint32_t read_ahead_flags)
+{
+       uint32_t current_read_ahead;
+
+       if (read_ahead == DM_READ_AHEAD_AUTO)
+               return 1;
+
+       if (read_ahead == DM_READ_AHEAD_NONE)
+               read_ahead = 0;
+
+       if (read_ahead_flags & DM_READ_AHEAD_MINIMUM_FLAG) {
+               if (!get_dev_node_read_ahead(dev_name, &current_read_ahead))
+                       return_0;
+
+               if (current_read_ahead > read_ahead) {
+                       log_debug("%s: retaining kernel read ahead of %" PRIu32
+                                 " (requested %" PRIu32 ")",           
+                                 dev_name, current_read_ahead, read_ahead);
+                       return 1;
+               }
+       }
+
+       return _set_read_ahead(dev_name, read_ahead);
+}
+
+#else
+
+int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
+{
+       *read_ahead = 0;
+
+       return 1;
+}
+
+static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+                                   uint32_t read_ahead_flags)
+{
+       return 1;
+}
+#endif
+
+typedef enum {
+       NODE_ADD,
+       NODE_DEL,
+       NODE_RENAME,
+       NODE_READ_AHEAD
+} node_op_t;
+
+static int _do_node_op(node_op_t type, const char *dev_name, uint32_t major,
+                      uint32_t minor, uid_t uid, gid_t gid, mode_t mode,
+                      const char *old_name, uint32_t read_ahead,
+                      uint32_t read_ahead_flags, int check_udev)
+{
+       switch (type) {
+       case NODE_ADD:
+               return _add_dev_node(dev_name, major, minor, uid, gid,
+                                    mode, check_udev);
+       case NODE_DEL:
+               return _rm_dev_node(dev_name, check_udev);
+       case NODE_RENAME:
+               return _rename_dev_node(old_name, dev_name, check_udev);
+       case NODE_READ_AHEAD:
+               return _set_dev_node_read_ahead(dev_name, read_ahead,
+                                               read_ahead_flags);
+       }
+
+       return 1;
+}
+
+static DM_LIST_INIT(_node_ops);
+
+struct node_op_parms {
+       struct dm_list list;
+       node_op_t type;
+       char *dev_name;
+       uint32_t major;
+       uint32_t minor;
+       uid_t uid;
+       gid_t gid;
+       mode_t mode;
+       uint32_t read_ahead;
+       uint32_t read_ahead_flags;
+       char *old_name;
+       int check_udev;
+       char names[0];
+};
+
+static void _store_str(char **pos, char **ptr, const char *str)
+{
+       strcpy(*pos, str);
+       *ptr = *pos;
+       *pos += strlen(*ptr) + 1;
+}
+
+static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
+                         uint32_t minor, uid_t uid, gid_t gid, mode_t mode,
+                         const char *old_name, uint32_t read_ahead,
+                         uint32_t read_ahead_flags, int check_udev)
+{
+       struct node_op_parms *nop;
+       struct dm_list *noph, *nopht;
+       size_t len = strlen(dev_name) + strlen(old_name) + 2;
+       char *pos;
+
+       /*
+        * Ignore any outstanding operations on the node if deleting it
+        */
+       if (type == NODE_DEL) {
+               dm_list_iterate_safe(noph, nopht, &_node_ops) {
+                       nop = dm_list_item(noph, struct node_op_parms);
+                       if (!strcmp(dev_name, nop->dev_name)) {
+                               dm_list_del(&nop->list);
+                               dm_free(nop);
+                       }
+               }
+       }
+
+       if (!(nop = dm_malloc(sizeof(*nop) + len))) {
+               log_error("Insufficient memory to stack mknod operation");
+               return 0;
+       }
+
+       pos = nop->names;
+       nop->type = type;
+       nop->major = major;
+       nop->minor = minor;
+       nop->uid = uid;
+       nop->gid = gid;
+       nop->mode = mode;
+       nop->read_ahead = read_ahead;
+       nop->read_ahead_flags = read_ahead_flags;
+       nop->check_udev = check_udev;
+
+       _store_str(&pos, &nop->dev_name, dev_name);
+       _store_str(&pos, &nop->old_name, old_name);
+
+       dm_list_add(&_node_ops, &nop->list);
+
+       return 1;
+}
+
+static void _pop_node_ops(void)
+{
+       struct dm_list *noph, *nopht;
+       struct node_op_parms *nop;
+
+       dm_list_iterate_safe(noph, nopht, &_node_ops) {
+               nop = dm_list_item(noph, struct node_op_parms);
+               _do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
+                           nop->uid, nop->gid, nop->mode, nop->old_name,
+                           nop->read_ahead, nop->read_ahead_flags,
+                           nop->check_udev);
+               dm_list_del(&nop->list);
+               dm_free(nop);
+       }
+}
+
+int add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
+                uid_t uid, gid_t gid, mode_t mode, int check_udev)
+{
+       log_debug("%s: Stacking NODE_ADD (%" PRIu32 ",%" PRIu32 ") %u:%u 0%o",
+                 dev_name, major, minor, uid, gid, mode);
+
+       return _stack_node_op(NODE_ADD, dev_name, major, minor, uid,
+                             gid, mode, "", 0, 0, check_udev);
+}
+
+int rename_dev_node(const char *old_name, const char *new_name, int check_udev)
+{
+       log_debug("%s: Stacking NODE_RENAME to %s", old_name, new_name);
+
+       return _stack_node_op(NODE_RENAME, new_name, 0, 0, 0,
+                             0, 0, old_name, 0, 0, check_udev);
+}
+
+int rm_dev_node(const char *dev_name, int check_udev)
+{
+       log_debug("%s: Stacking NODE_DEL (replaces other stacked ops)", dev_name);
+
+       return _stack_node_op(NODE_DEL, dev_name, 0, 0, 0,
+                             0, 0, "", 0, 0, check_udev);
+}
+
+int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+                           uint32_t read_ahead_flags)
+{
+       if (read_ahead == DM_READ_AHEAD_AUTO)
+               return 1;
+
+       log_debug("%s: Stacking NODE_READ_AHEAD %" PRIu32 " (flags=%" PRIu32
+                 ")", dev_name, read_ahead, read_ahead_flags);
+
+       return _stack_node_op(NODE_READ_AHEAD, dev_name, 0, 0, 0, 0,
+                              0, "", read_ahead, read_ahead_flags, 0);
+}
+
+void update_devs(void)
+{
+       _pop_node_ops();
+}
+
+int dm_set_dev_dir(const char *dev_dir)
+{
+       size_t len;
+       const char *slash;
+       if (*dev_dir != '/') {
+               log_debug("Invalid dev_dir value, %s: "
+                         "not an absolute name.", dev_dir);
+               return 0;
+       }
+
+       len = strlen(dev_dir);
+       slash = dev_dir[len-1] == '/' ? "" : "/";
+
+       if (snprintf(_dm_dir, sizeof _dm_dir, "%s%s%s", dev_dir, slash, DM_DIR)
+           >= sizeof _dm_dir) {
+               log_debug("Invalid dev_dir value, %s: name too long.", dev_dir);
+               return 0;
+       }
+
+       return 1;
+}
+
+const char *dm_dir(void)
+{
+       return _dm_dir;
+}
+
+int dm_mknodes(const char *name)
+{
+       struct dm_task *dmt;
+       int r = 0;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_MKNODES)))
+               return 0;
+
+       if (name && !dm_task_set_name(dmt, name))
+               goto out;
+
+       if (!dm_task_no_open_count(dmt))
+               goto out;
+
+       r = dm_task_run(dmt);
+
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+int dm_driver_version(char *version, size_t size)
+{
+       struct dm_task *dmt;
+       int r = 0;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_VERSION)))
+               return 0;
+
+       if (!dm_task_run(dmt))
+               log_error("Failed to get driver version");
+
+       if (!dm_task_get_driver_version(dmt, version, size))
+               goto out;
+
+       r = 1;
+
+out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+#ifndef UDEV_SYNC_SUPPORT
+void dm_udev_set_sync_support(int sync_with_udev)
+{
+}
+
+int dm_udev_get_sync_support(void)
+{
+       return 0;
+}
+
+void dm_udev_set_checking(int checking)
+{
+}
+
+int dm_udev_get_checking(void)
+{
+       return 0;
+}
+
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags)
+{
+       if (dm_cookie_supported())
+               dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT;
+       *cookie = 0;
+
+       return 1;
+}
+
+int dm_udev_complete(uint32_t cookie)
+{
+       return 1;
+}
+
+int dm_udev_wait(uint32_t cookie)
+{
+       return 1;
+}
+
+#else          /* UDEV_SYNC_SUPPORT */
+
+static int _check_semaphore_is_supported(void)
+{
+       int maxid;
+       union semun arg;
+       struct seminfo seminfo;
+
+       arg.__buf = &seminfo;
+       maxid = semctl(0, 0, SEM_INFO, arg);
+
+       if (maxid < 0) {
+               log_warn("Kernel not configured for semaphores (System V IPC). "
+                        "Not using udev synchronisation code.");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _check_udev_is_running(void)
+{
+       struct udev *udev;
+       struct udev_queue *udev_queue;
+       int r;
+
+       if (!(udev = udev_new()))
+               goto_bad;
+
+       if (!(udev_queue = udev_queue_new(udev))) {
+               udev_unref(udev);
+               goto_bad;
+       }
+
+       if (!(r = udev_queue_get_udev_is_active(udev_queue)))
+               log_debug("Udev is not running. "
+                         "Not using udev synchronisation code.");
+
+       udev_queue_unref(udev_queue);
+       udev_unref(udev);
+
+       return r;
+
+bad:
+       log_error("Could not get udev state. Assuming udev is not running.");
+       return 0;
+}
+
+static void _check_udev_sync_requirements_once(void)
+{
+       if (_semaphore_supported < 0)
+               _semaphore_supported = _check_semaphore_is_supported();
+
+       if (_udev_running < 0)
+               _udev_running = _check_udev_is_running();
+}
+
+void dm_udev_set_sync_support(int sync_with_udev)
+{
+       _check_udev_sync_requirements_once();
+       _sync_with_udev = sync_with_udev;
+}
+
+int dm_udev_get_sync_support(void)
+{
+       _check_udev_sync_requirements_once();
+
+       return _semaphore_supported && dm_cookie_supported() &&
+               _udev_running && _sync_with_udev;
+}
+
+void dm_udev_set_checking(int checking)
+{
+       if ((_udev_checking = checking))
+               log_debug("DM udev checking enabled");
+       else
+               log_debug("DM udev checking disabled");
+}
+
+int dm_udev_get_checking(void)
+{
+       return _udev_checking;
+}
+
+static int _get_cookie_sem(uint32_t cookie, int *semid)
+{
+       if (cookie >> 16 != DM_COOKIE_MAGIC) {
+               log_error("Could not continue to access notification "
+                         "semaphore identified by cookie value %"
+                         PRIu32 " (0x%x). Incorrect cookie prefix.",
+                         cookie, cookie);
+               return 0;
+       }
+
+       if ((*semid = semget((key_t) cookie, 1, 0)) >= 0)
+               return 1;
+
+       switch (errno) {
+               case ENOENT:
+                       log_error("Could not find notification "
+                                 "semaphore identified by cookie "
+                                 "value %" PRIu32 " (0x%x)",
+                                 cookie, cookie);
+                       break;
+               case EACCES:
+                       log_error("No permission to access "
+                                 "notificaton semaphore identified "
+                                 "by cookie value %" PRIu32 " (0x%x)",
+                                 cookie, cookie);
+                       break;
+               default:
+                       log_error("Failed to access notification "
+                                  "semaphore identified by cookie "
+                                  "value %" PRIu32 " (0x%x): %s",
+                                 cookie, cookie, strerror(errno));
+                       break;
+       }
+
+       return 0;
+}
+
+static int _udev_notify_sem_inc(uint32_t cookie, int semid)
+{
+       struct sembuf sb = {0, 1, 0};
+
+       if (semop(semid, &sb, 1) < 0) {
+               log_error("semid %d: semop failed for cookie 0x%" PRIx32 ": %s",
+                         semid, cookie, strerror(errno));
+               return 0;
+       }
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented",
+                 cookie, semid);
+
+       return 1;
+}
+
+static int _udev_notify_sem_dec(uint32_t cookie, int semid)
+{
+       struct sembuf sb = {0, -1, IPC_NOWAIT};
+
+       if (semop(semid, &sb, 1) < 0) {
+               switch (errno) {
+                       case EAGAIN:
+                               log_error("semid %d: semop failed for cookie "
+                                         "0x%" PRIx32 ": "
+                                         "incorrect semaphore state",
+                                         semid, cookie);
+                               break;
+                       default:
+                               log_error("semid %d: semop failed for cookie "
+                                         "0x%" PRIx32 ": %s",
+                                         semid, cookie, strerror(errno));
+                               break;
+               }
+               return 0;
+       }
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d) decremented",
+                 cookie, semid);
+
+       return 1;
+}
+
+static int _udev_notify_sem_destroy(uint32_t cookie, int semid)
+{
+       if (semctl(semid, 0, IPC_RMID, 0) < 0) {
+               log_error("Could not cleanup notification semaphore "
+                         "identified by cookie value %" PRIu32 " (0x%x): %s",
+                         cookie, cookie, strerror(errno));
+               return 0;
+       }
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d) destroyed", cookie,
+                 semid);
+
+       return 1;
+}
+
+static int _udev_notify_sem_create(uint32_t *cookie, int *semid)
+{
+       int fd;
+       int gen_semid;
+       uint16_t base_cookie;
+       uint32_t gen_cookie;
+       union semun sem_arg;
+
+       if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
+               log_error("Failed to open /dev/urandom "
+                         "to create random cookie value");
+               *cookie = 0;
+               return 0;
+       }
+
+       /* Generate random cookie value. Be sure it is unique and non-zero. */
+       do {
+               /* FIXME Handle non-error returns from read(). Move _io() into libdm? */
+               if (read(fd, &base_cookie, sizeof(base_cookie)) != sizeof(base_cookie)) {
+                       log_error("Failed to initialize notification cookie");
+                       goto bad;
+               }
+
+               gen_cookie = DM_COOKIE_MAGIC << 16 | base_cookie;
+
+               if (base_cookie && (gen_semid = semget((key_t) gen_cookie,
+                                   1, 0600 | IPC_CREAT | IPC_EXCL)) < 0) {
+                       switch (errno) {
+                               case EEXIST:
+                                       /* if the semaphore key exists, we
+                                        * simply generate another random one */
+                                       base_cookie = 0;
+                                       break;
+                               case ENOMEM:
+                                       log_error("Not enough memory to create "
+                                                 "notification semaphore");
+                                       goto bad;
+                               case ENOSPC:
+                                       log_error("Limit for the maximum number "
+                                                 "of semaphores reached. You can "
+                                                 "check and set the limits in "
+                                                 "/proc/sys/kernel/sem.");
+                                       goto bad;
+                               default:
+                                       log_error("Failed to create notification "
+                                                 "semaphore: %s", strerror(errno));
+                                       goto bad;
+                       }
+               }
+       } while (!base_cookie);
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d) created",
+                 gen_cookie, gen_semid);
+
+       sem_arg.val = 1;
+
+       if (semctl(gen_semid, 0, SETVAL, sem_arg) < 0) {
+               log_error("semid %d: semctl failed: %s", gen_semid, strerror(errno));
+               /* We have to destroy just created semaphore
+                * so it won't stay in the system. */
+               (void) _udev_notify_sem_destroy(gen_cookie, gen_semid);
+               goto bad;
+       }
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented",
+                 gen_cookie, gen_semid);
+
+       if (close(fd))
+               stack;
+
+       *semid = gen_semid;
+       *cookie = gen_cookie;
+
+       return 1;
+
+bad:
+       if (close(fd))
+               stack;
+
+       *cookie = 0;
+
+       return 0;
+}
+
+int dm_udev_create_cookie(uint32_t *cookie)
+{
+       int semid;
+
+       if (!dm_udev_get_sync_support()) {
+               *cookie = 0;
+               return 1;
+       }
+
+       return _udev_notify_sem_create(cookie, &semid);
+}
+
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags)
+{
+       int semid;
+
+       if (dm_cookie_supported())
+               dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT;
+
+       if (!dm_udev_get_sync_support()) {
+               *cookie = 0;
+               return 1;
+       }
+
+       if (*cookie) {
+               if (!_get_cookie_sem(*cookie, &semid))
+                       goto_bad;
+       } else if (!_udev_notify_sem_create(cookie, &semid))
+               goto_bad;
+
+       if (!_udev_notify_sem_inc(*cookie, semid)) {
+               log_error("Could not set notification semaphore "
+                         "identified by cookie value %" PRIu32 " (0x%x)",
+                         *cookie, *cookie);
+               goto bad;
+       }
+
+       dmt->event_nr |= ~DM_UDEV_FLAGS_MASK & *cookie;
+       dmt->cookie_set = 1;
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d) assigned to dm_task "
+                 "type %d with flags 0x%" PRIx16, *cookie, semid, dmt->type, flags);
+
+       return 1;
+
+bad:
+       dmt->event_nr = 0;
+       return 0;
+}
+
+int dm_udev_complete(uint32_t cookie)
+{
+       int semid;
+
+       if (!cookie || !dm_udev_get_sync_support())
+               return 1;
+
+       if (!_get_cookie_sem(cookie, &semid))
+               return_0;
+
+       if (!_udev_notify_sem_dec(cookie, semid)) {
+               log_error("Could not signal waiting process using notification "
+                         "semaphore identified by cookie value %" PRIu32 " (0x%x)",
+                         cookie, cookie);
+               return 0;
+       }
+
+       return 1;
+}
+
+int dm_udev_wait(uint32_t cookie)
+{
+       int semid;
+       struct sembuf sb = {0, 0, 0};
+
+       if (!cookie || !dm_udev_get_sync_support())
+               return 1;
+
+       if (!_get_cookie_sem(cookie, &semid))
+               return_0;
+
+       if (!_udev_notify_sem_dec(cookie, semid)) {
+               log_error("Failed to set a proper state for notification "
+                         "semaphore identified by cookie value %" PRIu32 " (0x%x) "
+                         "to initialize waiting for incoming notifications.",
+                         cookie, cookie);
+               (void) _udev_notify_sem_destroy(cookie, semid);
+               return 0;
+       }
+
+       log_debug("Udev cookie 0x%" PRIx32 " (semid %d): Waiting for zero",
+                 cookie, semid);
+
+repeat_wait:
+       if (semop(semid, &sb, 1) < 0) {
+               if (errno == EINTR)
+                       goto repeat_wait;
+               else if (errno == EIDRM)
+                       return 1;
+
+               log_error("Could not set wait state for notification semaphore "
+                         "identified by cookie value %" PRIu32 " (0x%x): %s",
+                         cookie, cookie, strerror(errno));
+               (void) _udev_notify_sem_destroy(cookie, semid);
+               return 0;
+       }
+
+       return _udev_notify_sem_destroy(cookie, semid);
+}
+
+#endif         /* UDEV_SYNC_SUPPORT */
diff --git a/libdm/libdm-common.h b/libdm/libdm-common.h
new file mode 100644 (file)
index 0000000..3267cfc
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef LIB_DMCOMMON_H
+#define LIB_DMCOMMON_H
+
+#include "libdevmapper.h"
+
+struct target *create_target(uint64_t start,
+                            uint64_t len,
+                            const char *type, const char *params);
+
+int add_dev_node(const char *dev_name, uint32_t minor, uint32_t major,
+                uid_t uid, gid_t gid, mode_t mode, int check_udev);
+int rm_dev_node(const char *dev_name, int check_udev);
+int rename_dev_node(const char *old_name, const char *new_name,
+                   int check_udev);
+int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead);
+int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+                           uint32_t read_ahead_flags);
+void update_devs(void);
+void selinux_release(void);
+
+#endif
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
new file mode 100644 (file)
index 0000000..8d00514
--- /dev/null
@@ -0,0 +1,2449 @@
+/*
+ * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+#include "kdev_t.h"
+#include "dm-ioctl.h"
+
+#include <stdarg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+
+#define MAX_TARGET_PARAMSIZE 500000
+
+/* FIXME Fix interface so this is used only by LVM */
+#define UUID_PREFIX "LVM-"
+
+#define REPLICATOR_LOCAL_SITE 0
+
+/* Supported segment types */
+enum {
+       SEG_CRYPT,
+       SEG_ERROR,
+       SEG_LINEAR,
+       SEG_MIRRORED,
+       SEG_REPLICATOR,
+       SEG_REPLICATOR_DEV,
+       SEG_SNAPSHOT,
+       SEG_SNAPSHOT_ORIGIN,
+       SEG_SNAPSHOT_MERGE,
+       SEG_STRIPED,
+       SEG_ZERO,
+};
+
+/* FIXME Add crypt and multipath support */
+
+struct {
+       unsigned type;
+       const char *target;
+} dm_segtypes[] = {
+       { SEG_CRYPT, "crypt" },
+       { SEG_ERROR, "error" },
+       { SEG_LINEAR, "linear" },
+       { SEG_MIRRORED, "mirror" },
+       { SEG_REPLICATOR, "replicator" },
+       { SEG_REPLICATOR_DEV, "replicator-dev" },
+       { SEG_SNAPSHOT, "snapshot" },
+       { SEG_SNAPSHOT_ORIGIN, "snapshot-origin" },
+       { SEG_SNAPSHOT_MERGE, "snapshot-merge" },
+       { SEG_STRIPED, "striped" },
+       { SEG_ZERO, "zero"},
+};
+
+/* Some segment types have a list of areas of other devices attached */
+struct seg_area {
+       struct dm_list list;
+
+       struct dm_tree_node *dev_node;
+
+       uint64_t offset;
+
+       unsigned rsite_index;           /* Replicator site index */
+       struct dm_tree_node *slog;      /* Replicator sync log node */
+       uint64_t region_size;           /* Replicator sync log size */
+       uint32_t flags;                 /* Replicator sync log flags */
+};
+
+/* Replicator-log has a list of sites */
+/* FIXME: maybe move to seg_area too? */
+struct replicator_site {
+       struct dm_list list;
+
+       unsigned rsite_index;
+       dm_replicator_mode_t mode;
+       uint32_t async_timeout;
+       uint32_t fall_behind_ios;
+       uint64_t fall_behind_data;
+};
+
+/* Per-segment properties */
+struct load_segment {
+       struct dm_list list;
+
+       unsigned type;
+
+       uint64_t size;
+
+       unsigned area_count;            /* Linear + Striped + Mirrored + Crypt + Replicator */
+       struct dm_list areas;           /* Linear + Striped + Mirrored + Crypt + Replicator */
+
+       uint32_t stripe_size;           /* Striped */
+
+       int persistent;                 /* Snapshot */
+       uint32_t chunk_size;            /* Snapshot */
+       struct dm_tree_node *cow;       /* Snapshot */
+       struct dm_tree_node *origin;    /* Snapshot + Snapshot origin */
+       struct dm_tree_node *merge;     /* Snapshot */
+
+       struct dm_tree_node *log;       /* Mirror + Replicator */
+       uint32_t region_size;           /* Mirror */
+       unsigned clustered;             /* Mirror */
+       unsigned mirror_area_count;     /* Mirror */
+       uint32_t flags;                 /* Mirror log */
+       char *uuid;                     /* Clustered mirror log */
+
+       const char *cipher;             /* Crypt */
+       const char *chainmode;          /* Crypt */
+       const char *iv;                 /* Crypt */
+       uint64_t iv_offset;             /* Crypt */
+       const char *key;                /* Crypt */
+
+       const char *rlog_type;          /* Replicator */
+       struct dm_list rsites;          /* Replicator */
+       unsigned rsite_count;           /* Replicator */
+       unsigned rdevice_count;         /* Replicator */
+       struct dm_tree_node *replicator;/* Replicator-dev */
+       uint64_t rdevice_index;         /* Replicator-dev */
+};
+
+/* Per-device properties */
+struct load_properties {
+       int read_only;
+       uint32_t major;
+       uint32_t minor;
+
+       uint32_t read_ahead;
+       uint32_t read_ahead_flags;
+
+       unsigned segment_count;
+       unsigned size_changed;
+       struct dm_list segs;
+
+       const char *new_name;
+
+       /* If immediate_dev_node is set to 1, try to create the dev node
+        * as soon as possible (e.g. in preload stage even during traversal
+        * and processing of dm tree). This will also flush all stacked dev
+        * node operations, synchronizing with udev.
+        */
+       int immediate_dev_node;
+};
+
+/* Two of these used to join two nodes with uses and used_by. */
+struct dm_tree_link {
+       struct dm_list list;
+       struct dm_tree_node *node;
+};
+
+struct dm_tree_node {
+       struct dm_tree *dtree;
+
+        const char *name;
+        const char *uuid;
+        struct dm_info info;
+
+        struct dm_list uses;           /* Nodes this node uses */
+        struct dm_list used_by;        /* Nodes that use this node */
+
+       int activation_priority;        /* 0 gets activated first */
+
+       uint16_t udev_flags;            /* Udev control flags */
+
+       void *context;                  /* External supplied context */
+
+       struct load_properties props;   /* For creation/table (re)load */
+
+       /*
+        * If presuspend of child node is needed
+        * Note: only direct child is allowed
+        */
+       struct dm_tree_node *presuspend_node;
+};
+
+struct dm_tree {
+       struct dm_pool *mem;
+       struct dm_hash_table *devs;
+       struct dm_hash_table *uuids;
+       struct dm_tree_node root;
+       int skip_lockfs;                /* 1 skips lockfs (for non-snapshots) */
+       int no_flush;           /* 1 sets noflush (mirrors/multipath) */
+       uint32_t cookie;
+};
+
+struct dm_tree *dm_tree_create(void)
+{
+       struct dm_tree *dtree;
+
+       if (!(dtree = dm_zalloc(sizeof(*dtree)))) {
+               log_error("dm_tree_create malloc failed");
+               return NULL;
+       }
+
+       dtree->root.dtree = dtree;
+       dm_list_init(&dtree->root.uses);
+       dm_list_init(&dtree->root.used_by);
+       dtree->skip_lockfs = 0;
+       dtree->no_flush = 0;
+
+       if (!(dtree->mem = dm_pool_create("dtree", 1024))) {
+               log_error("dtree pool creation failed");
+               dm_free(dtree);
+               return NULL;
+       }
+
+       if (!(dtree->devs = dm_hash_create(8))) {
+               log_error("dtree hash creation failed");
+               dm_pool_destroy(dtree->mem);
+               dm_free(dtree);
+               return NULL;
+       }
+
+       if (!(dtree->uuids = dm_hash_create(32))) {
+               log_error("dtree uuid hash creation failed");
+               dm_hash_destroy(dtree->devs);
+               dm_pool_destroy(dtree->mem);
+               dm_free(dtree);
+               return NULL;
+       }
+
+       return dtree;
+}
+
+void dm_tree_free(struct dm_tree *dtree)
+{
+       if (!dtree)
+               return;
+
+       dm_hash_destroy(dtree->uuids);
+       dm_hash_destroy(dtree->devs);
+       dm_pool_destroy(dtree->mem);
+       dm_free(dtree);
+}
+
+static int _nodes_are_linked(const struct dm_tree_node *parent,
+                            const struct dm_tree_node *child)
+{
+       struct dm_tree_link *dlink;
+
+       dm_list_iterate_items(dlink, &parent->uses)
+               if (dlink->node == child)
+                       return 1;
+
+       return 0;
+}
+
+static int _link(struct dm_list *list, struct dm_tree_node *node)
+{
+       struct dm_tree_link *dlink;
+
+       if (!(dlink = dm_pool_alloc(node->dtree->mem, sizeof(*dlink)))) {
+               log_error("dtree link allocation failed");
+               return 0;
+       }
+
+       dlink->node = node;
+       dm_list_add(list, &dlink->list);
+
+       return 1;
+}
+
+static int _link_nodes(struct dm_tree_node *parent,
+                      struct dm_tree_node *child)
+{
+       if (_nodes_are_linked(parent, child))
+               return 1;
+
+       if (!_link(&parent->uses, child))
+               return 0;
+
+       if (!_link(&child->used_by, parent))
+               return 0;
+
+       return 1;
+}
+
+static void _unlink(struct dm_list *list, struct dm_tree_node *node)
+{
+       struct dm_tree_link *dlink;
+
+       dm_list_iterate_items(dlink, list)
+               if (dlink->node == node) {
+                       dm_list_del(&dlink->list);
+                       break;
+               }
+}
+
+static void _unlink_nodes(struct dm_tree_node *parent,
+                         struct dm_tree_node *child)
+{
+       if (!_nodes_are_linked(parent, child))
+               return;
+
+       _unlink(&parent->uses, child);
+       _unlink(&child->used_by, parent);
+}
+
+static int _add_to_toplevel(struct dm_tree_node *node)
+{
+       return _link_nodes(&node->dtree->root, node);
+}
+
+static void _remove_from_toplevel(struct dm_tree_node *node)
+{
+       _unlink_nodes(&node->dtree->root, node);
+}
+
+static int _add_to_bottomlevel(struct dm_tree_node *node)
+{
+       return _link_nodes(node, &node->dtree->root);
+}
+
+static void _remove_from_bottomlevel(struct dm_tree_node *node)
+{
+       _unlink_nodes(node, &node->dtree->root);
+}
+
+static int _link_tree_nodes(struct dm_tree_node *parent, struct dm_tree_node *child)
+{
+       /* Don't link to root node if child already has a parent */
+       if ((parent == &parent->dtree->root)) {
+               if (dm_tree_node_num_children(child, 1))
+                       return 1;
+       } else
+               _remove_from_toplevel(child);
+
+       if ((child == &child->dtree->root)) {
+               if (dm_tree_node_num_children(parent, 0))
+                       return 1;
+       } else
+               _remove_from_bottomlevel(parent);
+
+       return _link_nodes(parent, child);
+}
+
+static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree,
+                                                const char *name,
+                                                const char *uuid,
+                                                struct dm_info *info,
+                                                void *context,
+                                                uint16_t udev_flags)
+{
+       struct dm_tree_node *node;
+       uint64_t dev;
+
+       if (!(node = dm_pool_zalloc(dtree->mem, sizeof(*node)))) {
+               log_error("_create_dm_tree_node alloc failed");
+               return NULL;
+       }
+
+       node->dtree = dtree;
+
+       node->name = name;
+       node->uuid = uuid;
+       node->info = *info;
+       node->context = context;
+       node->udev_flags = udev_flags;
+       node->activation_priority = 0;
+
+       dm_list_init(&node->uses);
+       dm_list_init(&node->used_by);
+       dm_list_init(&node->props.segs);
+
+       dev = MKDEV(info->major, info->minor);
+
+       if (!dm_hash_insert_binary(dtree->devs, (const char *) &dev,
+                               sizeof(dev), node)) {
+               log_error("dtree node hash insertion failed");
+               dm_pool_free(dtree->mem, node);
+               return NULL;
+       }
+
+       if (uuid && *uuid &&
+           !dm_hash_insert(dtree->uuids, uuid, node)) {
+               log_error("dtree uuid hash insertion failed");
+               dm_hash_remove_binary(dtree->devs, (const char *) &dev,
+                                     sizeof(dev));
+               dm_pool_free(dtree->mem, node);
+               return NULL;
+       }
+
+       return node;
+}
+
+static struct dm_tree_node *_find_dm_tree_node(struct dm_tree *dtree,
+                                              uint32_t major, uint32_t minor)
+{
+       uint64_t dev = MKDEV(major, minor);
+
+       return dm_hash_lookup_binary(dtree->devs, (const char *) &dev,
+                                 sizeof(dev));
+}
+
+static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree,
+                                                      const char *uuid)
+{
+       struct dm_tree_node *node;
+
+       if ((node = dm_hash_lookup(dtree->uuids, uuid)))
+               return node;
+
+       if (strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+               return NULL;
+
+       return dm_hash_lookup(dtree->uuids, uuid + sizeof(UUID_PREFIX) - 1);
+}
+
+static int _deps(struct dm_task **dmt, struct dm_pool *mem, uint32_t major, uint32_t minor,
+                const char **name, const char **uuid,
+                struct dm_info *info, struct dm_deps **deps)
+{
+       memset(info, 0, sizeof(*info));
+
+       if (!dm_is_dm_major(major)) {
+               *name = "";
+               *uuid = "";
+               *deps = NULL;
+               info->major = major;
+               info->minor = minor;
+               info->exists = 0;
+               info->live_table = 0;
+               info->inactive_table = 0;
+               info->read_only = 0;
+               return 1;
+       }
+
+       if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) {
+               log_error("deps dm_task creation failed");
+               return 0;
+       }
+
+       if (!dm_task_set_major(*dmt, major)) {
+               log_error("_deps: failed to set major for (%" PRIu32 ":%" PRIu32 ")",
+                         major, minor);
+               goto failed;
+       }
+
+       if (!dm_task_set_minor(*dmt, minor)) {
+               log_error("_deps: failed to set minor for (%" PRIu32 ":%" PRIu32 ")",
+                         major, minor);
+               goto failed;
+       }
+
+       if (!dm_task_run(*dmt)) {
+               log_error("_deps: task run failed for (%" PRIu32 ":%" PRIu32 ")",
+                         major, minor);
+               goto failed;
+       }
+
+       if (!dm_task_get_info(*dmt, info)) {
+               log_error("_deps: failed to get info for (%" PRIu32 ":%" PRIu32 ")",
+                         major, minor);
+               goto failed;
+       }
+
+       if (!info->exists) {
+               *name = "";
+               *uuid = "";
+               *deps = NULL;
+       } else {
+               if (info->major != major) {
+                       log_error("Inconsistent dtree major number: %u != %u",
+                                 major, info->major);
+                       goto failed;
+               }
+               if (info->minor != minor) {
+                       log_error("Inconsistent dtree minor number: %u != %u",
+                                 minor, info->minor);
+                       goto failed;
+               }
+               if (!(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) {
+                       log_error("name pool_strdup failed");
+                       goto failed;
+               }
+               if (!(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(*dmt)))) {
+                       log_error("uuid pool_strdup failed");
+                       goto failed;
+               }
+               *deps = dm_task_get_deps(*dmt);
+       }
+
+       return 1;
+
+failed:
+       dm_task_destroy(*dmt);
+       return 0;
+}
+
+static struct dm_tree_node *_add_dev(struct dm_tree *dtree,
+                                    struct dm_tree_node *parent,
+                                    uint32_t major, uint32_t minor,
+                                    uint16_t udev_flags)
+{
+       struct dm_task *dmt = NULL;
+       struct dm_info info;
+       struct dm_deps *deps = NULL;
+       const char *name = NULL;
+       const char *uuid = NULL;
+       struct dm_tree_node *node = NULL;
+       uint32_t i;
+       int new = 0;
+
+       /* Already in tree? */
+       if (!(node = _find_dm_tree_node(dtree, major, minor))) {
+               if (!_deps(&dmt, dtree->mem, major, minor, &name, &uuid, &info, &deps))
+                       return_NULL;
+
+               if (!(node = _create_dm_tree_node(dtree, name, uuid, &info,
+                                                 NULL, udev_flags)))
+                       goto_out;
+               new = 1;
+       }
+
+       if (!_link_tree_nodes(parent, node)) {
+               node = NULL;
+               goto_out;
+       }
+
+       /* If node was already in tree, no need to recurse. */
+       if (!new)
+               goto out;
+
+       /* Can't recurse if not a mapped device or there are no dependencies */
+       if (!node->info.exists || !deps->count) {
+               if (!_add_to_bottomlevel(node)) {
+                       stack;
+                       node = NULL;
+               }
+               goto out;
+       }
+
+       /* Add dependencies to tree */
+       for (i = 0; i < deps->count; i++)
+               if (!_add_dev(dtree, node, MAJOR(deps->device[i]),
+                             MINOR(deps->device[i]), udev_flags)) {
+                       node = NULL;
+                       goto_out;
+               }
+
+out:
+       if (dmt)
+               dm_task_destroy(dmt);
+
+       return node;
+}
+
+static int _node_clear_table(struct dm_tree_node *dnode)
+{
+       struct dm_task *dmt;
+       struct dm_info *info;
+       const char *name;
+       int r;
+
+       if (!(info = &dnode->info)) {
+               log_error("_node_clear_table failed: missing info");
+               return 0;
+       }
+
+       if (!(name = dm_tree_node_get_name(dnode))) {
+               log_error("_node_clear_table failed: missing name");
+               return 0;
+       }
+
+       /* Is there a table? */
+       if (!info->exists || !info->inactive_table)
+               return 1;
+
+       log_verbose("Clearing inactive table %s (%" PRIu32 ":%" PRIu32 ")",
+                   name, info->major, info->minor);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_CLEAR))) {
+               log_error("Table clear dm_task creation failed for %s", name);
+               return 0;
+       }
+
+       if (!dm_task_set_major(dmt, info->major) ||
+           !dm_task_set_minor(dmt, info->minor)) {
+               log_error("Failed to set device number for %s table clear", name);
+               dm_task_destroy(dmt);
+               return 0;
+       }
+
+       r = dm_task_run(dmt);
+
+       if (!dm_task_get_info(dmt, info)) {
+               log_error("_node_clear_table failed: info missing after running task for %s", name);
+               r = 0;
+       }
+
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *dtree,
+                                           const char *name,
+                                           const char *uuid,
+                                           uint32_t major, uint32_t minor,
+                                           int read_only,
+                                           int clear_inactive,
+                                           void *context)
+{
+       struct dm_tree_node *dnode;
+       struct dm_info info;
+       const char *name2;
+       const char *uuid2;
+
+       /* Do we need to add node to tree? */
+       if (!(dnode = dm_tree_find_node_by_uuid(dtree, uuid))) {
+               if (!(name2 = dm_pool_strdup(dtree->mem, name))) {
+                       log_error("name pool_strdup failed");
+                       return NULL;
+               }
+               if (!(uuid2 = dm_pool_strdup(dtree->mem, uuid))) {
+                       log_error("uuid pool_strdup failed");
+                       return NULL;
+               }
+
+               info.major = 0;
+               info.minor = 0;
+               info.exists = 0;
+               info.live_table = 0;
+               info.inactive_table = 0;
+               info.read_only = 0;
+
+               if (!(dnode = _create_dm_tree_node(dtree, name2, uuid2, &info,
+                                                  context, 0)))
+                       return_NULL;
+
+               /* Attach to root node until a table is supplied */
+               if (!_add_to_toplevel(dnode) || !_add_to_bottomlevel(dnode))
+                       return_NULL;
+
+               dnode->props.major = major;
+               dnode->props.minor = minor;
+               dnode->props.new_name = NULL;
+               dnode->props.size_changed = 0;
+       } else if (strcmp(name, dnode->name)) {
+               /* Do we need to rename node? */
+               if (!(dnode->props.new_name = dm_pool_strdup(dtree->mem, name))) {
+                       log_error("name pool_strdup failed");
+                       return 0;
+               }
+       }
+
+       dnode->props.read_only = read_only ? 1 : 0;
+       dnode->props.read_ahead = DM_READ_AHEAD_AUTO;
+       dnode->props.read_ahead_flags = 0;
+
+       if (clear_inactive && !_node_clear_table(dnode))
+               return_NULL;
+
+       dnode->context = context;
+       dnode->udev_flags = 0;
+
+       return dnode;
+}
+
+struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *dtree,
+                                                        const char *name,
+                                                        const char *uuid,
+                                                        uint32_t major,
+                                                        uint32_t minor,
+                                                        int read_only,
+                                                        int clear_inactive,
+                                                        void *context,
+                                                        uint16_t udev_flags)
+{
+       struct dm_tree_node *node;
+
+       if ((node = dm_tree_add_new_dev(dtree, name, uuid, major, minor, read_only,
+                                      clear_inactive, context)))
+               node->udev_flags = udev_flags;
+
+       return node;
+}
+
+
+void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
+                                uint32_t read_ahead,
+                                uint32_t read_ahead_flags)
+{
+       dnode->props.read_ahead = read_ahead;
+       dnode->props.read_ahead_flags = read_ahead_flags;
+}
+
+void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
+                                     struct dm_tree_node *presuspend_node)
+{
+       node->presuspend_node = presuspend_node;
+}
+
+int dm_tree_add_dev(struct dm_tree *dtree, uint32_t major, uint32_t minor)
+{
+       return _add_dev(dtree, &dtree->root, major, minor, 0) ? 1 : 0;
+}
+
+int dm_tree_add_dev_with_udev_flags(struct dm_tree *dtree, uint32_t major,
+                                   uint32_t minor, uint16_t udev_flags)
+{
+       return _add_dev(dtree, &dtree->root, major, minor, udev_flags) ? 1 : 0;
+}
+
+const char *dm_tree_node_get_name(const struct dm_tree_node *node)
+{
+       return node->info.exists ? node->name : "";
+}
+
+const char *dm_tree_node_get_uuid(const struct dm_tree_node *node)
+{
+       return node->info.exists ? node->uuid : "";
+}
+
+const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node)
+{
+       return &node->info;
+}
+
+void *dm_tree_node_get_context(const struct dm_tree_node *node)
+{
+       return node->context;
+}
+
+int dm_tree_node_size_changed(const struct dm_tree_node *dnode)
+{
+       return dnode->props.size_changed;
+}
+
+int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted)
+{
+       if (inverted) {
+               if (_nodes_are_linked(&node->dtree->root, node))
+                       return 0;
+               return dm_list_size(&node->used_by);
+       }
+
+       if (_nodes_are_linked(node, &node->dtree->root))
+               return 0;
+
+       return dm_list_size(&node->uses);
+}
+
+/*
+ * Returns 1 if no prefix supplied
+ */
+static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_t uuid_prefix_len)
+{
+       if (!uuid_prefix)
+               return 1;
+
+       if (!strncmp(uuid, uuid_prefix, uuid_prefix_len))
+               return 1;
+
+       /* Handle transition: active device uuids might be missing the prefix */
+       if (uuid_prefix_len <= 4)
+               return 0;
+
+       if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+               return 0;
+
+       if (strncmp(uuid_prefix, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+               return 0;
+
+       if (!strncmp(uuid, uuid_prefix + sizeof(UUID_PREFIX) - 1, uuid_prefix_len - (sizeof(UUID_PREFIX) - 1)))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Returns 1 if no children.
+ */
+static int _children_suspended(struct dm_tree_node *node,
+                              uint32_t inverted,
+                              const char *uuid_prefix,
+                              size_t uuid_prefix_len)
+{
+       struct dm_list *list;
+       struct dm_tree_link *dlink;
+       const struct dm_info *dinfo;
+       const char *uuid;
+
+       if (inverted) {
+               if (_nodes_are_linked(&node->dtree->root, node))
+                       return 1;
+               list = &node->used_by;
+       } else {
+               if (_nodes_are_linked(node, &node->dtree->root))
+                       return 1;
+               list = &node->uses;
+       }
+
+       dm_list_iterate_items(dlink, list) {
+               if (!(uuid = dm_tree_node_get_uuid(dlink->node))) {
+                       stack;
+                       continue;
+               }
+
+               /* Ignore if it doesn't belong to this VG */
+               if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               /* Ignore if parent node wants to presuspend this node */
+               if (dlink->node->presuspend_node == node)
+                       continue;
+
+               if (!(dinfo = dm_tree_node_get_info(dlink->node))) {
+                       stack;  /* FIXME Is this normal? */
+                       return 0;
+               }
+
+               if (!dinfo->suspended)
+                       return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * Set major and minor to zero for root of tree.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *dtree,
+                                         uint32_t major,
+                                         uint32_t minor)
+{
+       if (!major && !minor)
+               return &dtree->root;
+
+       return _find_dm_tree_node(dtree, major, minor);
+}
+
+/*
+ * Set uuid to NULL for root of tree.
+ */
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *dtree,
+                                                 const char *uuid)
+{
+       if (!uuid || !*uuid)
+               return &dtree->root;
+
+       return _find_dm_tree_node_by_uuid(dtree, uuid);
+}
+
+/*
+ * First time set *handle to NULL.
+ * Set inverted to invert the tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+                                       const struct dm_tree_node *parent,
+                                       uint32_t inverted)
+{
+       struct dm_list **dlink = (struct dm_list **) handle;
+       const struct dm_list *use_list;
+
+       if (inverted)
+               use_list = &parent->used_by;
+       else
+               use_list = &parent->uses;
+
+       if (!*dlink)
+               *dlink = dm_list_first(use_list);
+       else
+               *dlink = dm_list_next(use_list, *dlink);
+
+       return (*dlink) ? dm_list_item(*dlink, struct dm_tree_link)->node : NULL;
+}
+
+/*
+ * Deactivate a device with its dependencies if the uuid prefix matches.
+ */
+static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count,
+                       struct dm_info *info)
+{
+       struct dm_task *dmt;
+       int r;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+               log_error("_info_by_dev: dm_task creation failed");
+               return 0;
+       }
+
+       if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+               log_error("_info_by_dev: Failed to set device number");
+               dm_task_destroy(dmt);
+               return 0;
+       }
+
+       if (!with_open_count && !dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if ((r = dm_task_run(dmt)))
+               r = dm_task_get_info(dmt, info);
+
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+/* Check if all parent nodes of given node have open_count == 0 */
+static int _node_has_closed_parents(struct dm_tree_node *node,
+                                   const char *uuid_prefix,
+                                   size_t uuid_prefix_len)
+{
+       struct dm_tree_link *dlink;
+       const struct dm_info *dinfo;
+       struct dm_info info;
+       const char *uuid;
+
+       /* Iterate through parents of this node */
+       dm_list_iterate_items(dlink, &node->used_by) {
+               if (!(uuid = dm_tree_node_get_uuid(dlink->node))) {
+                       stack;
+                       continue;
+               }
+
+               /* Ignore if it doesn't belong to this VG */
+               if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               if (!(dinfo = dm_tree_node_get_info(dlink->node))) {
+                       stack;  /* FIXME Is this normal? */
+                       return 0;
+               }
+
+               /* Refresh open_count */
+               if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+                   !info.exists)
+                       continue;
+
+               if (info.open_count)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int _deactivate_node(const char *name, uint32_t major, uint32_t minor,
+                           uint32_t *cookie, uint16_t udev_flags)
+{
+       struct dm_task *dmt;
+       int r = 0;
+
+       log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) {
+               log_error("Deactivation dm_task creation failed for %s", name);
+               return 0;
+       }
+
+       if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+               log_error("Failed to set device number for %s deactivation", name);
+               goto out;
+       }
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+               goto out;
+
+       r = dm_task_run(dmt);
+
+       /* FIXME Until kernel returns actual name so dm-ioctl.c can handle it */
+       rm_dev_node(name, dmt->cookie_set &&
+                         !(udev_flags & DM_UDEV_DISABLE_DM_RULES_FLAG));
+
+       /* FIXME Remove node from tree or mark invalid? */
+
+out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _rename_node(const char *old_name, const char *new_name, uint32_t major,
+                       uint32_t minor, uint32_t *cookie, uint16_t udev_flags)
+{
+       struct dm_task *dmt;
+       int r = 0;
+
+       log_verbose("Renaming %s (%" PRIu32 ":%" PRIu32 ") to %s", old_name, major, minor, new_name);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_RENAME))) {
+               log_error("Rename dm_task creation failed for %s", old_name);
+               return 0;
+       }
+
+       if (!dm_task_set_name(dmt, old_name)) {
+               log_error("Failed to set name for %s rename.", old_name);
+               goto out;
+       }
+
+       if (!dm_task_set_newname(dmt, new_name))
+                goto_out;
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+               goto out;
+
+       r = dm_task_run(dmt);
+
+out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+/* FIXME Merge with _suspend_node? */
+static int _resume_node(const char *name, uint32_t major, uint32_t minor,
+                       uint32_t read_ahead, uint32_t read_ahead_flags,
+                       struct dm_info *newinfo, uint32_t *cookie,
+                       uint16_t udev_flags)
+{
+       struct dm_task *dmt;
+       int r = 0;
+
+       log_verbose("Resuming %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_RESUME))) {
+               log_error("Suspend dm_task creation failed for %s", name);
+               return 0;
+       }
+
+       /* FIXME Kernel should fill in name on return instead */
+       if (!dm_task_set_name(dmt, name)) {
+               log_error("Failed to set readahead device name for %s", name);
+               goto out;
+       }
+
+       if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+               log_error("Failed to set device number for %s resumption.", name);
+               goto out;
+       }
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (!dm_task_set_read_ahead(dmt, read_ahead, read_ahead_flags))
+               log_error("Failed to set read ahead");
+
+       if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+               goto out;
+
+       if ((r = dm_task_run(dmt)))
+               r = dm_task_get_info(dmt, newinfo);
+
+out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
+                        int skip_lockfs, int no_flush, struct dm_info *newinfo)
+{
+       struct dm_task *dmt;
+       int r;
+
+       log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")%s%s",
+                   name, major, minor,
+                   skip_lockfs ? "" : " with filesystem sync",
+                   no_flush ? "" : " with device flush");
+
+       if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) {
+               log_error("Suspend dm_task creation failed for %s", name);
+               return 0;
+       }
+
+       if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+               log_error("Failed to set device number for %s suspension.", name);
+               dm_task_destroy(dmt);
+               return 0;
+       }
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if (skip_lockfs && !dm_task_skip_lockfs(dmt))
+               log_error("Failed to set skip_lockfs flag.");
+
+       if (no_flush && !dm_task_no_flush(dmt))
+               log_error("Failed to set no_flush flag.");
+
+       if ((r = dm_task_run(dmt)))
+               r = dm_task_get_info(dmt, newinfo);
+
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+/*
+ * FIXME Don't attempt to deactivate known internal dependencies.
+ */
+static int _dm_tree_deactivate_children(struct dm_tree_node *dnode,
+                                       const char *uuid_prefix,
+                                       size_t uuid_prefix_len,
+                                       unsigned level)
+{
+       int r = 1;
+       void *handle = NULL;
+       struct dm_tree_node *child = dnode;
+       struct dm_info info;
+       const struct dm_info *dinfo;
+       const char *name;
+       const char *uuid;
+
+       while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+               if (!(dinfo = dm_tree_node_get_info(child))) {
+                       stack;
+                       continue;
+               }
+
+               if (!(name = dm_tree_node_get_name(child))) {
+                       stack;
+                       continue;
+               }
+
+               if (!(uuid = dm_tree_node_get_uuid(child))) {
+                       stack;
+                       continue;
+               }
+
+               /* Ignore if it doesn't belong to this VG */
+               if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               /* Refresh open_count */
+               if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+                   !info.exists)
+                       continue;
+
+               /* Also checking open_count in parent nodes of presuspend_node */
+               if (info.open_count ||
+                   (child->presuspend_node &&
+                    !_node_has_closed_parents(child->presuspend_node,
+                                              uuid_prefix, uuid_prefix_len))) {
+                       /* Only report error from (likely non-internal) dependency at top level */
+                       if (!level) {
+                               log_error("Unable to deactivate open %s (%" PRIu32
+                                         ":%" PRIu32 ")", name, info.major,
+                                       info.minor);
+                               r = 0;
+                       }
+                       continue;
+               }
+
+               /* Suspend child node first if requested */
+               if (child->presuspend_node &&
+                   !dm_tree_suspend_children(child, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               if (!_deactivate_node(name, info.major, info.minor,
+                                     &child->dtree->cookie, child->udev_flags)) {
+                       log_error("Unable to deactivate %s (%" PRIu32
+                                 ":%" PRIu32 ")", name, info.major,
+                                 info.minor);
+                       r = 0;
+                       continue;
+               }
+
+               if (dm_tree_node_num_children(child, 0)) {
+                       if (!_dm_tree_deactivate_children(child, uuid_prefix, uuid_prefix_len, level + 1))
+                               return_0;
+               }
+       }
+
+       return r;
+}
+
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+                                  const char *uuid_prefix,
+                                  size_t uuid_prefix_len)
+{
+       return _dm_tree_deactivate_children(dnode, uuid_prefix, uuid_prefix_len, 0);
+}
+
+void dm_tree_skip_lockfs(struct dm_tree_node *dnode)
+{
+       dnode->dtree->skip_lockfs = 1;
+}
+
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode)
+{
+       dnode->dtree->no_flush = 1;
+}
+
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+                            const char *uuid_prefix,
+                            size_t uuid_prefix_len)
+{
+       int r = 1;
+       void *handle = NULL;
+       struct dm_tree_node *child = dnode;
+       struct dm_info info, newinfo;
+       const struct dm_info *dinfo;
+       const char *name;
+       const char *uuid;
+
+       /* Suspend nodes at this level of the tree */
+       while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+               if (!(dinfo = dm_tree_node_get_info(child))) {
+                       stack;
+                       continue;
+               }
+
+               if (!(name = dm_tree_node_get_name(child))) {
+                       stack;
+                       continue;
+               }
+
+               if (!(uuid = dm_tree_node_get_uuid(child))) {
+                       stack;
+                       continue;
+               }
+
+               /* Ignore if it doesn't belong to this VG */
+               if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               /* Ensure immediate parents are already suspended */
+               if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) ||
+                   !info.exists || info.suspended)
+                       continue;
+
+               if (!_suspend_node(name, info.major, info.minor,
+                                  child->dtree->skip_lockfs,
+                                  child->dtree->no_flush, &newinfo)) {
+                       log_error("Unable to suspend %s (%" PRIu32
+                                 ":%" PRIu32 ")", name, info.major,
+                                 info.minor);
+                       r = 0;
+                       continue;
+               }
+
+               /* Update cached info */
+               child->info = newinfo;
+       }
+
+       /* Then suspend any child nodes */
+       handle = NULL;
+
+       while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+               if (!(uuid = dm_tree_node_get_uuid(child))) {
+                       stack;
+                       continue;
+               }
+
+               /* Ignore if it doesn't belong to this VG */
+               if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               if (dm_tree_node_num_children(child, 0))
+                       if (!dm_tree_suspend_children(child, uuid_prefix, uuid_prefix_len))
+                               return_0;
+       }
+
+       return r;
+}
+
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+                                const char *uuid_prefix,
+                                size_t uuid_prefix_len)
+{
+       int r = 1;
+       void *handle = NULL;
+       struct dm_tree_node *child = dnode;
+       struct dm_info newinfo;
+       const char *name;
+       const char *uuid;
+       int priority;
+
+       /* Activate children first */
+       while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+               if (!(uuid = dm_tree_node_get_uuid(child))) {
+                       stack;
+                       continue;
+               }
+
+               if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               if (dm_tree_node_num_children(child, 0))
+                       if (!dm_tree_activate_children(child, uuid_prefix, uuid_prefix_len))
+                               return_0;
+       }
+
+       handle = NULL;
+
+       for (priority = 0; priority < 3; priority++) {
+               while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+                       if (!(uuid = dm_tree_node_get_uuid(child))) {
+                               stack;
+                               continue;
+                       }
+
+                       if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                               continue;
+
+                       if (priority != child->activation_priority)
+                               continue;
+
+                       if (!(name = dm_tree_node_get_name(child))) {
+                               stack;
+                               continue;
+                       }
+
+                       /* Rename? */
+                       if (child->props.new_name) {
+                               if (!_rename_node(name, child->props.new_name, child->info.major,
+                                                 child->info.minor, &child->dtree->cookie,
+                                                 child->udev_flags)) {
+                                       log_error("Failed to rename %s (%" PRIu32
+                                                 ":%" PRIu32 ") to %s", name, child->info.major,
+                                                 child->info.minor, child->props.new_name);
+                                       return 0;
+                               }
+                               child->name = child->props.new_name;
+                               child->props.new_name = NULL;
+                       }
+
+                       if (!child->info.inactive_table && !child->info.suspended)
+                               continue;
+
+                       if (!_resume_node(child->name, child->info.major, child->info.minor,
+                                         child->props.read_ahead, child->props.read_ahead_flags,
+                                         &newinfo, &child->dtree->cookie, child->udev_flags)) {
+                               log_error("Unable to resume %s (%" PRIu32
+                                         ":%" PRIu32 ")", child->name, child->info.major,
+                                         child->info.minor);
+                               r = 0;
+                               continue;
+                       }
+
+                       /* Update cached info */
+                       child->info = newinfo;
+               }
+       }
+
+       handle = NULL;
+
+       return r;
+}
+
+static int _create_node(struct dm_tree_node *dnode)
+{
+       int r = 0;
+       struct dm_task *dmt;
+
+       log_verbose("Creating %s", dnode->name);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_CREATE))) {
+               log_error("Create dm_task creation failed for %s", dnode->name);
+               return 0;
+       }
+
+       if (!dm_task_set_name(dmt, dnode->name)) {
+               log_error("Failed to set device name for %s", dnode->name);
+               goto out;
+       }
+
+       if (!dm_task_set_uuid(dmt, dnode->uuid)) {
+               log_error("Failed to set uuid for %s", dnode->name);
+               goto out;
+       }
+
+       if (dnode->props.major &&
+           (!dm_task_set_major(dmt, dnode->props.major) ||
+            !dm_task_set_minor(dmt, dnode->props.minor))) {
+               log_error("Failed to set device number for %s creation.", dnode->name);
+               goto out;
+       }
+
+       if (dnode->props.read_only && !dm_task_set_ro(dmt)) {
+               log_error("Failed to set read only flag for %s", dnode->name);
+               goto out;
+       }
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       if ((r = dm_task_run(dmt)))
+               r = dm_task_get_info(dmt, &dnode->info);
+
+out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+
+static int _build_dev_string(char *devbuf, size_t bufsize, struct dm_tree_node *node)
+{
+       if (!dm_format_dev(devbuf, bufsize, node->info.major, node->info.minor)) {
+                log_error("Failed to format %s device number for %s as dm "
+                          "target (%u,%u)",
+                          node->name, node->uuid, node->info.major, node->info.minor);
+                return 0;
+       }
+
+       return 1;
+}
+
+/* simplify string emiting code */
+#define EMIT_PARAMS(p, str...)\
+do {\
+       int w;\
+       if ((w = dm_snprintf(params + p, paramsize - (size_t) p, str)) < 0) {\
+               stack; /* Out of space */\
+               return -1;\
+       }\
+       p += w;\
+} while (0)
+
+/*
+ * _emit_areas_line
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+static int _emit_areas_line(struct dm_task *dmt __attribute__((unused)),
+                           struct load_segment *seg, char *params,
+                           size_t paramsize, int *pos)
+{
+       struct seg_area *area;
+       char devbuf[DM_FORMAT_DEV_BUFSIZE];
+       unsigned first_time = 1;
+       const char *logtype, *synctype;
+       unsigned log_parm_count;
+
+       dm_list_iterate_items(area, &seg->areas) {
+               if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
+                       return_0;
+
+               switch (seg->type) {
+               case SEG_REPLICATOR_DEV:
+                       EMIT_PARAMS(*pos, " %d 1 %s", area->rsite_index, devbuf);
+                       if (first_time)
+                               EMIT_PARAMS(*pos, " nolog 0");
+                       else {
+                               /* Remote devices */
+                               log_parm_count = (area->flags &
+                                                 (DM_NOSYNC | DM_FORCESYNC)) ? 2 : 1;
+
+                               if (!area->slog) {
+                                       devbuf[0] = 0;          /* Only core log parameters */
+                                       logtype = "core";
+                               } else {
+                                       devbuf[0] = ' ';        /* Extra space before device name */
+                                       if (!_build_dev_string(devbuf + 1,
+                                                              sizeof(devbuf) - 1,
+                                                              area->slog))
+                                               return_0;
+                                       logtype = "disk";
+                                       log_parm_count++;       /* Extra sync log device name parameter */
+                               }
+
+                               EMIT_PARAMS(*pos, " %s %u%s %" PRIu64, logtype,
+                                           log_parm_count, devbuf, area->region_size);
+
+                               synctype = (area->flags & DM_NOSYNC) ?
+                                               " nosync" : (area->flags & DM_FORCESYNC) ?
+                                                               " sync" : NULL;
+
+                               if (synctype)
+                                       EMIT_PARAMS(*pos, "%s", synctype);
+                       }
+                       break;
+               default:
+                       EMIT_PARAMS(*pos, "%s%s %" PRIu64, first_time ? "" : " ",
+                                   devbuf, area->offset);
+               }
+
+               first_time = 0;
+       }
+
+       return 1;
+}
+
+static int _replicator_emit_segment_line(const struct load_segment *seg, char *params,
+                                        size_t paramsize, int *pos)
+{
+       const struct load_segment *rlog_seg;
+       struct replicator_site *rsite;
+       char rlogbuf[DM_FORMAT_DEV_BUFSIZE];
+       unsigned parm_count;
+
+       if (!seg->log || !_build_dev_string(rlogbuf, sizeof(rlogbuf), seg->log))
+               return_0;
+
+       rlog_seg = dm_list_item(dm_list_last(&seg->log->props.segs),
+                               struct load_segment);
+
+       EMIT_PARAMS(*pos, "%s 4 %s 0 auto %" PRIu64,
+                   seg->rlog_type, rlogbuf, rlog_seg->size);
+
+       dm_list_iterate_items(rsite, &seg->rsites) {
+               parm_count = (rsite->fall_behind_data
+                             || rsite->fall_behind_ios
+                             || rsite->async_timeout) ? 4 : 2;
+
+               EMIT_PARAMS(*pos, " blockdev %u %u %s", parm_count, rsite->rsite_index,
+                           (rsite->mode == DM_REPLICATOR_SYNC) ? "synchronous" : "asynchronous");
+
+               if (rsite->fall_behind_data)
+                       EMIT_PARAMS(*pos, " data %" PRIu64, rsite->fall_behind_data);
+               else if (rsite->fall_behind_ios)
+                       EMIT_PARAMS(*pos, " ios %" PRIu32, rsite->fall_behind_ios);
+               else if (rsite->async_timeout)
+                       EMIT_PARAMS(*pos, " timeout %" PRIu32, rsite->async_timeout);
+       }
+
+       return 1;
+}
+
+/*
+ * Returns: 1 on success, 0 on failure
+ */
+static int _mirror_emit_segment_line(struct dm_task *dmt, uint32_t major,
+                                    uint32_t minor, struct load_segment *seg,
+                                    uint64_t *seg_start, char *params,
+                                    size_t paramsize)
+{
+       int block_on_error = 0;
+       int handle_errors = 0;
+       int dm_log_userspace = 0;
+       struct utsname uts;
+       unsigned log_parm_count;
+       int pos = 0;
+       char logbuf[DM_FORMAT_DEV_BUFSIZE];
+       const char *logtype;
+       unsigned kmaj, kmin, krel;
+
+       if (uname(&uts) == -1 || sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel) != 3) {
+               log_error("Cannot read kernel release version");
+               return 0;
+       }
+
+       if ((seg->flags & DM_BLOCK_ON_ERROR)) {
+               /*
+                * Originally, block_on_error was an argument to the log
+                * portion of the mirror CTR table.  It was renamed to
+                * "handle_errors" and now resides in the 'features'
+                * section of the mirror CTR table (i.e. at the end).
+                *
+                * We can identify whether to use "block_on_error" or
+                * "handle_errors" by the dm-mirror module's version
+                * number (>= 1.12) or by the kernel version (>= 2.6.22).
+                */
+               if (KERNEL_VERSION(kmaj, kmin, krel) >= KERNEL_VERSION(2, 6, 22))
+                       handle_errors = 1;
+               else
+                       block_on_error = 1;
+       }
+
+       if (seg->clustered) {
+               /* Cluster mirrors require a UUID */
+               if (!seg->uuid)
+                       return_0;
+
+               /*
+                * Cluster mirrors used to have their own log
+                * types.  Now they are accessed through the
+                * userspace log type.
+                *
+                * The dm-log-userspace module was added to the
+                * 2.6.31 kernel.
+                */
+               if (KERNEL_VERSION(kmaj, kmin, krel) >= KERNEL_VERSION(2, 6, 31))
+                       dm_log_userspace = 1;
+       }
+
+       /* Region size */
+       log_parm_count = 1;
+
+       /* [no]sync, block_on_error etc. */
+       log_parm_count += hweight32(seg->flags);
+
+       /* "handle_errors" is a feature arg now */
+       if (handle_errors)
+               log_parm_count--;
+
+       /* DM_CORELOG does not count in the param list */
+       if (seg->flags & DM_CORELOG)
+               log_parm_count--;
+
+       if (seg->clustered) {
+               log_parm_count++; /* For UUID */
+
+               if (!dm_log_userspace)
+                       EMIT_PARAMS(pos, "clustered-");
+               else
+                       /* For clustered-* type field inserted later */
+                       log_parm_count++;
+       }
+
+       if (!seg->log)
+               logtype = "core";
+       else {
+               logtype = "disk";
+               log_parm_count++;
+               if (!_build_dev_string(logbuf, sizeof(logbuf), seg->log))
+                       return_0;
+       }
+
+       if (dm_log_userspace)
+               EMIT_PARAMS(pos, "userspace %u %s clustered-%s",
+                           log_parm_count, seg->uuid, logtype);
+       else
+               EMIT_PARAMS(pos, "%s %u", logtype, log_parm_count);
+
+       if (seg->log)
+               EMIT_PARAMS(pos, " %s", logbuf);
+
+       EMIT_PARAMS(pos, " %u", seg->region_size);
+
+       if (seg->clustered && !dm_log_userspace)
+               EMIT_PARAMS(pos, " %s", seg->uuid);
+
+       if ((seg->flags & DM_NOSYNC))
+               EMIT_PARAMS(pos, " nosync");
+       else if ((seg->flags & DM_FORCESYNC))
+               EMIT_PARAMS(pos, " sync");
+
+       if (block_on_error)
+               EMIT_PARAMS(pos, " block_on_error");
+
+       EMIT_PARAMS(pos, " %u ", seg->mirror_area_count);
+
+       if (_emit_areas_line(dmt, seg, params, paramsize, &pos) <= 0)
+               return_0;
+
+       if (handle_errors)
+               EMIT_PARAMS(pos, " 1 handle_errors");
+
+       return 1;
+}
+
+static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
+                             uint32_t minor, struct load_segment *seg,
+                             uint64_t *seg_start, char *params,
+                             size_t paramsize)
+{
+       int pos = 0;
+       int r;
+       char originbuf[DM_FORMAT_DEV_BUFSIZE], cowbuf[DM_FORMAT_DEV_BUFSIZE];
+
+       switch(seg->type) {
+       case SEG_ERROR:
+       case SEG_ZERO:
+       case SEG_LINEAR:
+               break;
+       case SEG_MIRRORED:
+               /* Mirrors are pretty complicated - now in separate function */
+               r = _mirror_emit_segment_line(dmt, major, minor, seg, seg_start,
+                                             params, paramsize);
+               if (!r)
+                       return_0;
+               break;
+       case SEG_REPLICATOR:
+               if ((r = _replicator_emit_segment_line(seg, params, paramsize,
+                                                      &pos)) <= 0) {
+                       stack;
+                       return r;
+               }
+               break;
+       case SEG_REPLICATOR_DEV:
+               if (!seg->replicator || !_build_dev_string(originbuf,
+                                                          sizeof(originbuf),
+                                                          seg->replicator))
+                       return_0;
+
+               EMIT_PARAMS(pos, "%s %" PRIu64, originbuf, seg->rdevice_index);
+               break;
+       case SEG_SNAPSHOT:
+       case SEG_SNAPSHOT_MERGE:
+               if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin))
+                       return_0;
+               if (!_build_dev_string(cowbuf, sizeof(cowbuf), seg->cow))
+                       return_0;
+               EMIT_PARAMS(pos, "%s %s %c %d", originbuf, cowbuf,
+                           seg->persistent ? 'P' : 'N', seg->chunk_size);
+               break;
+       case SEG_SNAPSHOT_ORIGIN:
+               if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin))
+                       return_0;
+               EMIT_PARAMS(pos, "%s", originbuf);
+               break;
+       case SEG_STRIPED:
+               EMIT_PARAMS(pos, "%u %u ", seg->area_count, seg->stripe_size);
+               break;
+       case SEG_CRYPT:
+               EMIT_PARAMS(pos, "%s%s%s%s%s %s %" PRIu64 " ", seg->cipher,
+                           seg->chainmode ? "-" : "", seg->chainmode ?: "",
+                           seg->iv ? "-" : "", seg->iv ?: "", seg->key,
+                           seg->iv_offset != DM_CRYPT_IV_DEFAULT ?
+                           seg->iv_offset : *seg_start);
+               break;
+       }
+
+       switch(seg->type) {
+       case SEG_ERROR:
+       case SEG_REPLICATOR:
+       case SEG_SNAPSHOT:
+       case SEG_SNAPSHOT_ORIGIN:
+       case SEG_SNAPSHOT_MERGE:
+       case SEG_ZERO:
+               break;
+       case SEG_CRYPT:
+       case SEG_LINEAR:
+       case SEG_REPLICATOR_DEV:
+       case SEG_STRIPED:
+               if ((r = _emit_areas_line(dmt, seg, params, paramsize, &pos)) <= 0) {
+                       stack;
+                       return r;
+               }
+               break;
+       }
+
+       log_debug("Adding target to (%" PRIu32 ":%" PRIu32 "): %" PRIu64
+                 " %" PRIu64 " %s %s", major, minor,
+                 *seg_start, seg->size, dm_segtypes[seg->type].target, params);
+
+       if (!dm_task_add_target(dmt, *seg_start, seg->size, dm_segtypes[seg->type].target, params))
+               return_0;
+
+       *seg_start += seg->size;
+
+       return 1;
+}
+
+#undef EMIT_PARAMS
+
+static int _emit_segment(struct dm_task *dmt, uint32_t major, uint32_t minor,
+                        struct load_segment *seg, uint64_t *seg_start)
+{
+       char *params;
+       size_t paramsize = 4096;
+       int ret;
+
+       do {
+               if (!(params = dm_malloc(paramsize))) {
+                       log_error("Insufficient space for target parameters.");
+                       return 0;
+               }
+
+               params[0] = '\0';
+               ret = _emit_segment_line(dmt, major, minor, seg, seg_start,
+                                        params, paramsize);
+               dm_free(params);
+
+               if (!ret)
+                       stack;
+
+               if (ret >= 0)
+                       return ret;
+
+               log_debug("Insufficient space in params[%" PRIsize_t
+                         "] for target parameters.", paramsize);
+
+               paramsize *= 2;
+       } while (paramsize < MAX_TARGET_PARAMSIZE);
+
+       log_error("Target parameter size too big. Aborting.");
+       return 0;
+}
+
+static int _load_node(struct dm_tree_node *dnode)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct load_segment *seg;
+       uint64_t seg_start = 0;
+
+       log_verbose("Loading %s table (%" PRIu32 ":%" PRIu32 ")", dnode->name,
+                   dnode->info.major, dnode->info.minor);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_RELOAD))) {
+               log_error("Reload dm_task creation failed for %s", dnode->name);
+               return 0;
+       }
+
+       if (!dm_task_set_major(dmt, dnode->info.major) ||
+           !dm_task_set_minor(dmt, dnode->info.minor)) {
+               log_error("Failed to set device number for %s reload.", dnode->name);
+               goto out;
+       }
+
+       if (dnode->props.read_only && !dm_task_set_ro(dmt)) {
+               log_error("Failed to set read only flag for %s", dnode->name);
+               goto out;
+       }
+
+       if (!dm_task_no_open_count(dmt))
+               log_error("Failed to disable open_count");
+
+       dm_list_iterate_items(seg, &dnode->props.segs)
+               if (!_emit_segment(dmt, dnode->info.major, dnode->info.minor,
+                                  seg, &seg_start))
+                       goto_out;
+
+       if (!dm_task_suppress_identical_reload(dmt))
+               log_error("Failed to suppress reload of identical tables.");
+
+       if ((r = dm_task_run(dmt))) {
+               r = dm_task_get_info(dmt, &dnode->info);
+               if (r && !dnode->info.inactive_table)
+                       log_verbose("Suppressed %s identical table reload.",
+                                   dnode->name);
+
+               if ((dnode->props.size_changed =
+                    (dm_task_get_existing_table_size(dmt) == seg_start) ? 0 : 1))
+                       log_debug("Table size changed from %" PRIu64 " to %"
+                                 PRIu64 " for %s",
+                                 dm_task_get_existing_table_size(dmt),
+                                 seg_start, dnode->name);
+       }
+
+       dnode->props.segment_count = 0;
+
+out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+                            const char *uuid_prefix,
+                            size_t uuid_prefix_len)
+{
+       int r = 1;
+       void *handle = NULL;
+       struct dm_tree_node *child;
+       struct dm_info newinfo;
+       int update_devs_flag = 0;
+
+       /* Preload children first */
+       while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+               /* Skip existing non-device-mapper devices */
+               if (!child->info.exists && child->info.major)
+                       continue;
+
+               /* Ignore if it doesn't belong to this VG */
+               if (child->info.exists &&
+                   !_uuid_prefix_matches(child->uuid, uuid_prefix, uuid_prefix_len))
+                       continue;
+
+               if (dm_tree_node_num_children(child, 0))
+                       if (!dm_tree_preload_children(child, uuid_prefix, uuid_prefix_len))
+                               return_0;
+
+               /* FIXME Cope if name exists with no uuid? */
+               if (!child->info.exists) {
+                       if (!_create_node(child)) {
+                               stack;
+                               return 0;
+                       }
+               }
+
+               if (!child->info.inactive_table && child->props.segment_count) {
+                       if (!_load_node(child)) {
+                               stack;
+                               return 0;
+                       }
+               }
+
+               /* Propagate device size change change */
+               if (child->props.size_changed)
+                       dnode->props.size_changed = 1;
+
+               /* Resume device immediately if it has parents and its size changed */
+               if (!dm_tree_node_num_children(child, 1) || !child->props.size_changed)
+                       continue;
+
+               if (!child->info.inactive_table && !child->info.suspended)
+                       continue;
+
+               if (!_resume_node(child->name, child->info.major, child->info.minor,
+                                 child->props.read_ahead, child->props.read_ahead_flags,
+                                 &newinfo, &child->dtree->cookie, child->udev_flags)) {
+                       log_error("Unable to resume %s (%" PRIu32
+                                 ":%" PRIu32 ")", child->name, child->info.major,
+                                 child->info.minor);
+                       r = 0;
+                       continue;
+               }
+
+               /* Update cached info */
+               child->info = newinfo;
+
+               /*
+                * Prepare for immediate synchronization with udev and flush all stacked
+                * dev node operations if requested by immediate_dev_node property. But
+                * finish processing current level in the tree first.
+                */
+               if (child->props.immediate_dev_node)
+                       update_devs_flag = 1;
+
+       }
+
+       handle = NULL;
+
+       if (update_devs_flag) {
+               if (!dm_udev_wait(dm_tree_get_cookie(dnode)))
+                       stack;
+               dm_tree_set_cookie(dnode, 0);
+               dm_task_update_nodes();
+       }
+
+       return r;
+}
+
+/*
+ * Returns 1 if unsure.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+                                const char *uuid_prefix,
+                                size_t uuid_prefix_len)
+{
+       void *handle = NULL;
+       struct dm_tree_node *child = dnode;
+       const char *uuid;
+
+       while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+               if (!(uuid = dm_tree_node_get_uuid(child))) {
+                       log_error("Failed to get uuid for dtree node.");
+                       return 1;
+               }
+
+               if (_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+                       return 1;
+
+               if (dm_tree_node_num_children(child, 0))
+                       dm_tree_children_use_uuid(child, uuid_prefix, uuid_prefix_len);
+       }
+
+       return 0;
+}
+
+/*
+ * Target functions
+ */
+static struct load_segment *_add_segment(struct dm_tree_node *dnode, unsigned type, uint64_t size)
+{
+       struct load_segment *seg;
+
+       if (!(seg = dm_pool_zalloc(dnode->dtree->mem, sizeof(*seg)))) {
+               log_error("dtree node segment allocation failed");
+               return NULL;
+       }
+
+       seg->type = type;
+       seg->size = size;
+       seg->area_count = 0;
+       dm_list_init(&seg->areas);
+       seg->stripe_size = 0;
+       seg->persistent = 0;
+       seg->chunk_size = 0;
+       seg->cow = NULL;
+       seg->origin = NULL;
+       seg->merge = NULL;
+
+       dm_list_add(&dnode->props.segs, &seg->list);
+       dnode->props.segment_count++;
+
+       return seg;
+}
+
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+                                               uint64_t size,
+                                               const char *origin_uuid)
+{
+       struct load_segment *seg;
+       struct dm_tree_node *origin_node;
+
+       if (!(seg = _add_segment(dnode, SEG_SNAPSHOT_ORIGIN, size)))
+               return_0;
+
+       if (!(origin_node = dm_tree_find_node_by_uuid(dnode->dtree, origin_uuid))) {
+               log_error("Couldn't find snapshot origin uuid %s.", origin_uuid);
+               return 0;
+       }
+
+       seg->origin = origin_node;
+       if (!_link_tree_nodes(dnode, origin_node))
+               return_0;
+
+       /* Resume snapshot origins after new snapshots */
+       dnode->activation_priority = 1;
+
+       return 1;
+}
+
+static int _add_snapshot_target(struct dm_tree_node *node,
+                                  uint64_t size,
+                                  const char *origin_uuid,
+                                  const char *cow_uuid,
+                                  const char *merge_uuid,
+                                  int persistent,
+                                  uint32_t chunk_size)
+{
+       struct load_segment *seg;
+       struct dm_tree_node *origin_node, *cow_node, *merge_node;
+       unsigned seg_type;
+
+       seg_type = !merge_uuid ? SEG_SNAPSHOT : SEG_SNAPSHOT_MERGE;
+
+       if (!(seg = _add_segment(node, seg_type, size)))
+               return_0;
+
+       if (!(origin_node = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
+               log_error("Couldn't find snapshot origin uuid %s.", origin_uuid);
+               return 0;
+       }
+
+       seg->origin = origin_node;
+       if (!_link_tree_nodes(node, origin_node))
+               return_0;
+
+       if (!(cow_node = dm_tree_find_node_by_uuid(node->dtree, cow_uuid))) {
+               log_error("Couldn't find snapshot COW device uuid %s.", cow_uuid);
+               return 0;
+       }
+
+       seg->cow = cow_node;
+       if (!_link_tree_nodes(node, cow_node))
+               return_0;
+
+       seg->persistent = persistent ? 1 : 0;
+       seg->chunk_size = chunk_size;
+
+       if (merge_uuid) {
+               if (!(merge_node = dm_tree_find_node_by_uuid(node->dtree, merge_uuid))) {
+                       /* not a pure error, merging snapshot may have been deactivated */
+                       log_verbose("Couldn't find merging snapshot uuid %s.", merge_uuid);
+               } else {
+                       seg->merge = merge_node;
+                       /* must not link merging snapshot, would undermine activation_priority below */
+               }
+
+               /* Resume snapshot-merge (acting origin) after other snapshots */
+               node->activation_priority = 1;
+               if (seg->merge) {
+                       /* Resume merging snapshot after snapshot-merge */
+                       seg->merge->activation_priority = 2;
+               }
+       }
+
+       return 1;
+}
+
+
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+                                    uint64_t size,
+                                    const char *origin_uuid,
+                                    const char *cow_uuid,
+                                    int persistent,
+                                    uint32_t chunk_size)
+{
+       return _add_snapshot_target(node, size, origin_uuid, cow_uuid,
+                                   NULL, persistent, chunk_size);
+}
+
+int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
+                                          uint64_t size,
+                                          const char *origin_uuid,
+                                          const char *cow_uuid,
+                                          const char *merge_uuid,
+                                          uint32_t chunk_size)
+{
+       return _add_snapshot_target(node, size, origin_uuid, cow_uuid,
+                                   merge_uuid, 1, chunk_size);
+}
+
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+                                     uint64_t size)
+{
+       if (!_add_segment(node, SEG_ERROR, size))
+               return_0;
+
+       return 1;
+}
+
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+                                    uint64_t size)
+{
+       if (!_add_segment(node, SEG_ZERO, size))
+               return_0;
+
+       return 1;
+}
+
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+                                      uint64_t size)
+{
+       if (!_add_segment(node, SEG_LINEAR, size))
+               return_0;
+
+       return 1;
+}
+
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+                                       uint64_t size,
+                                       uint32_t stripe_size)
+{
+       struct load_segment *seg;
+
+       if (!(seg = _add_segment(node, SEG_STRIPED, size)))
+               return_0;
+
+       seg->stripe_size = stripe_size;
+
+       return 1;
+}
+
+int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
+                                 uint64_t size,
+                                 const char *cipher,
+                                 const char *chainmode,
+                                 const char *iv,
+                                 uint64_t iv_offset,
+                                 const char *key)
+{
+       struct load_segment *seg;
+
+       if (!(seg = _add_segment(node, SEG_CRYPT, size)))
+               return_0;
+
+       seg->cipher = cipher;
+       seg->chainmode = chainmode;
+       seg->iv = iv;
+       seg->iv_offset = iv_offset;
+       seg->key = key;
+
+       return 1;
+}
+
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+                                         uint32_t region_size,
+                                         unsigned clustered,
+                                         const char *log_uuid,
+                                         unsigned area_count,
+                                         uint32_t flags)
+{
+       struct dm_tree_node *log_node = NULL;
+       struct load_segment *seg;
+
+       if (!node->props.segment_count) {
+               log_error(INTERNAL_ERROR "Attempt to add target area to missing segment.");
+               return 0;
+       }
+
+       seg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+
+       if (log_uuid) {
+               if (!(seg->uuid = dm_pool_strdup(node->dtree->mem, log_uuid))) {
+                       log_error("log uuid pool_strdup failed");
+                       return 0;
+               }
+               if (!(flags & DM_CORELOG)) {
+                       if (!(log_node = dm_tree_find_node_by_uuid(node->dtree, log_uuid))) {
+                               log_error("Couldn't find mirror log uuid %s.", log_uuid);
+                               return 0;
+                       }
+
+                       if (clustered)
+                               log_node->props.immediate_dev_node = 1;
+
+                       if (!_link_tree_nodes(node, log_node))
+                               return_0;
+               }
+       }
+
+       seg->log = log_node;
+       seg->region_size = region_size;
+       seg->clustered = clustered;
+       seg->mirror_area_count = area_count;
+       seg->flags = flags;
+
+       return 1;
+}
+
+int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
+                                      uint64_t size)
+{
+       if (!_add_segment(node, SEG_MIRRORED, size))
+               return_0;
+
+       return 1;
+}
+
+int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
+                                      uint64_t size,
+                                      const char *rlog_uuid,
+                                      const char *rlog_type,
+                                      unsigned rsite_index,
+                                      dm_replicator_mode_t mode,
+                                      uint32_t async_timeout,
+                                      uint64_t fall_behind_data,
+                                      uint32_t fall_behind_ios)
+{
+       struct load_segment *rseg;
+       struct replicator_site *rsite;
+
+       /* Local site0 - adds replicator segment and links rlog device */
+       if (rsite_index == REPLICATOR_LOCAL_SITE) {
+               if (node->props.segment_count) {
+                       log_error(INTERNAL_ERROR "Attempt to add replicator segment to already used node.");
+                       return 0;
+               }
+
+               if (!(rseg = _add_segment(node, SEG_REPLICATOR, size)))
+                       return_0;
+
+               if (!(rseg->log = dm_tree_find_node_by_uuid(node->dtree, rlog_uuid))) {
+                       log_error("Missing replicator log uuid %s.", rlog_uuid);
+                       return 0;
+               }
+
+               if (!_link_tree_nodes(node, rseg->log))
+                       return_0;
+
+               if (strcmp(rlog_type, "ringbuffer") != 0) {
+                       log_error("Unsupported replicator log type %s.", rlog_type);
+                       return 0;
+               }
+
+               if (!(rseg->rlog_type = dm_pool_strdup(node->dtree->mem, rlog_type)))
+                       return_0;
+
+               dm_list_init(&rseg->rsites);
+               rseg->rdevice_count = 0;
+               node->activation_priority = 1;
+       }
+
+       /* Add site to segment */
+       if (mode == DM_REPLICATOR_SYNC
+           && (async_timeout || fall_behind_ios || fall_behind_data)) {
+               log_error("Async parameters passed for synchronnous replicator.");
+               return 0;
+       }
+
+       if (node->props.segment_count != 1) {
+               log_error(INTERNAL_ERROR "Attempt to add remote site area before setting replicator log.");
+               return 0;
+       }
+
+       rseg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+       if (rseg->type != SEG_REPLICATOR) {
+               log_error(INTERNAL_ERROR "Attempt to use non replicator segment %s.",
+                         dm_segtypes[rseg->type].target);
+               return 0;
+       }
+
+       if (!(rsite = dm_pool_zalloc(node->dtree->mem, sizeof(*rsite)))) {
+               log_error("Failed to allocate remote site segment.");
+               return 0;
+       }
+
+       dm_list_add(&rseg->rsites, &rsite->list);
+       rseg->rsite_count++;
+
+       rsite->mode = mode;
+       rsite->async_timeout = async_timeout;
+       rsite->fall_behind_data = fall_behind_data;
+       rsite->fall_behind_ios = fall_behind_ios;
+       rsite->rsite_index = rsite_index;
+
+       return 1;
+}
+
+/* Appends device node to Replicator */
+int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
+                                          uint64_t size,
+                                          const char *replicator_uuid,
+                                          uint64_t rdevice_index,
+                                          const char *rdev_uuid,
+                                          unsigned rsite_index,
+                                          const char *slog_uuid,
+                                          uint32_t slog_flags,
+                                          uint32_t slog_region_size)
+{
+       struct seg_area *area;
+       struct load_segment *rseg;
+       struct load_segment *rep_seg;
+
+       if (rsite_index == REPLICATOR_LOCAL_SITE) {
+               /* Site index for local target */
+               if (!(rseg = _add_segment(node, SEG_REPLICATOR_DEV, size)))
+                       return_0;
+
+               if (!(rseg->replicator = dm_tree_find_node_by_uuid(node->dtree, replicator_uuid))) {
+                       log_error("Missing replicator uuid %s.", replicator_uuid);
+                       return 0;
+               }
+
+               /* Local slink0 for replicator must be always initialized first */
+               if (rseg->replicator->props.segment_count != 1) {
+                       log_error(INTERNAL_ERROR "Attempt to use non replicator segment.");
+                       return 0;
+               }
+
+               rep_seg = dm_list_item(dm_list_last(&rseg->replicator->props.segs), struct load_segment);
+               if (rep_seg->type != SEG_REPLICATOR) {
+                       log_error(INTERNAL_ERROR "Attempt to use non replicator segment %s.",
+                                 dm_segtypes[rep_seg->type].target);
+                       return 0;
+               }
+               rep_seg->rdevice_count++;
+
+               if (!_link_tree_nodes(node, rseg->replicator))
+                       return_0;
+
+               rseg->rdevice_index = rdevice_index;
+       } else {
+               /* Local slink0 for replicator must be always initialized first */
+               if (node->props.segment_count != 1) {
+                       log_error(INTERNAL_ERROR "Attempt to use non replicator-dev segment.");
+                       return 0;
+               }
+
+               rseg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+               if (rseg->type != SEG_REPLICATOR_DEV) {
+                       log_error(INTERNAL_ERROR "Attempt to use non replicator-dev segment %s.",
+                                 dm_segtypes[rseg->type].target);
+                       return 0;
+               }
+       }
+
+       if (!(slog_flags & DM_CORELOG) && !slog_uuid) {
+               log_error("Unspecified sync log uuid.");
+               return 0;
+       }
+
+       if (!dm_tree_node_add_target_area(node, NULL, rdev_uuid, 0))
+               return_0;
+
+       area = dm_list_item(dm_list_last(&rseg->areas), struct seg_area);
+
+       if (!(slog_flags & DM_CORELOG)) {
+               if (!(area->slog = dm_tree_find_node_by_uuid(node->dtree, slog_uuid))) {
+                       log_error("Couldn't find sync log uuid %s.", slog_uuid);
+                       return 0;
+               }
+
+               if (!_link_tree_nodes(node, area->slog))
+                       return_0;
+       }
+
+       area->flags = slog_flags;
+       area->region_size = slog_region_size;
+       area->rsite_index = rsite_index;
+
+       return 1;
+}
+
+static int _add_area(struct dm_tree_node *node, struct load_segment *seg, struct dm_tree_node *dev_node, uint64_t offset)
+{
+       struct seg_area *area;
+
+       if (!(area = dm_pool_zalloc(node->dtree->mem, sizeof (*area)))) {
+               log_error("Failed to allocate target segment area.");
+               return 0;
+       }
+
+       area->dev_node = dev_node;
+       area->offset = offset;
+
+       dm_list_add(&seg->areas, &area->list);
+       seg->area_count++;
+
+       return 1;
+}
+
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+                                    const char *dev_name,
+                                    const char *uuid,
+                                    uint64_t offset)
+{
+       struct load_segment *seg;
+       struct stat info;
+       struct dm_tree_node *dev_node;
+
+       if ((!dev_name || !*dev_name) && (!uuid || !*uuid)) {
+               log_error("dm_tree_node_add_target_area called without device");
+               return 0;
+       }
+
+       if (uuid) {
+               if (!(dev_node = dm_tree_find_node_by_uuid(node->dtree, uuid))) {
+                       log_error("Couldn't find area uuid %s.", uuid);
+                       return 0;
+               }
+               if (!_link_tree_nodes(node, dev_node))
+                       return_0;
+       } else {
+               if (stat(dev_name, &info) < 0) {
+                       log_error("Device %s not found.", dev_name);
+                       return 0;
+               }
+
+               if (!S_ISBLK(info.st_mode)) {
+                       log_error("Device %s is not a block device.", dev_name);
+                       return 0;
+               }
+
+               /* FIXME Check correct macro use */
+               if (!(dev_node = _add_dev(node->dtree, node, MAJOR(info.st_rdev),
+                                         MINOR(info.st_rdev), 0)))
+                       return_0;
+       }
+
+       if (!node->props.segment_count) {
+               log_error(INTERNAL_ERROR "Attempt to add target area to missing segment.");
+               return 0;
+       }
+
+       seg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+
+       if (!_add_area(node, seg, dev_node, offset))
+               return_0;
+
+       return 1;
+}
+
+void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie)
+{
+       node->dtree->cookie = cookie;
+}
+
+uint32_t dm_tree_get_cookie(struct dm_tree_node *node)
+{
+       return node->dtree->cookie;
+}
diff --git a/libdm/libdm-file.c b/libdm/libdm-file.c
new file mode 100644 (file)
index 0000000..82fae34
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+
+#include <sys/file.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+static int _create_dir_recursive(const char *dir)
+{
+       char *orig, *s;
+       int rc, r = 0;
+
+       log_verbose("Creating directory \"%s\"", dir);
+       /* Create parent directories */
+       orig = s = dm_strdup(dir);
+       while ((s = strchr(s, '/')) != NULL) {
+               *s = '\0';
+               if (*orig) {
+                       rc = mkdir(orig, 0777);
+                       if (rc < 0 && errno != EEXIST) {
+                               if (errno != EROFS)
+                                       log_sys_error("mkdir", orig);
+                               goto out;
+                       }
+               }
+               *s++ = '/';
+       }
+
+       /* Create final directory */
+       rc = mkdir(dir, 0777);
+       if (rc < 0 && errno != EEXIST) {
+               if (errno != EROFS)
+                       log_sys_error("mkdir", orig);
+               goto out;
+       }
+
+       r = 1;
+out:
+       dm_free(orig);
+       return r;
+}
+
+int dm_create_dir(const char *dir)
+{
+       struct stat info;
+
+       if (!*dir)
+               return 1;
+
+       if (stat(dir, &info) < 0)
+               return _create_dir_recursive(dir);
+
+       if (S_ISDIR(info.st_mode))
+               return 1;
+
+       log_error("Directory \"%s\" not found", dir);
+       return 0;
+}
+
+int dm_fclose(FILE *stream)
+{
+       int prev_fail = ferror(stream);
+       int fclose_fail = fclose(stream);
+
+       /* If there was a previous failure, but fclose succeeded,
+          clear errno, since ferror does not set it, and its value
+          may be unrelated to the ferror-reported failure.  */
+       if (prev_fail && !fclose_fail)
+               errno = 0;
+
+       return prev_fail || fclose_fail ? EOF : 0;
+}
+
+int dm_create_lockfile(const char *lockfile)
+{
+       int fd, value;
+       size_t bufferlen;
+       ssize_t write_out;
+       struct flock lock;
+       char buffer[50];
+       int retries = 0;
+
+       if((fd = open(lockfile, O_CREAT | O_WRONLY,
+                     (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) {
+               log_error("Cannot open lockfile [%s], error was [%s]",
+                         lockfile, strerror(errno));
+               return 0;
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_start = 0;
+       lock.l_whence = SEEK_SET;
+       lock.l_len = 0;
+retry_fcntl:
+       if (fcntl(fd, F_SETLK, &lock) < 0) {
+               switch (errno) {
+               case EINTR:
+                       goto retry_fcntl;
+                       break;
+               case EACCES:
+               case EAGAIN:
+                       if (retries == 20) {
+                               log_error("Cannot lock lockfile [%s], error was [%s]",
+                                         lockfile, strerror(errno));
+                               break;
+                       } else {
+                               ++ retries;
+                               usleep(1000);
+                               goto retry_fcntl;
+                       }
+               default:
+                       log_error("process is already running");
+               }
+
+               goto fail_close;
+       }
+
+       if (ftruncate(fd, 0) < 0) {
+               log_error("Cannot truncate pidfile [%s], error was [%s]",
+                         lockfile, strerror(errno));
+
+               goto fail_close_unlink;
+       }
+
+       memset(buffer, 0, sizeof(buffer));
+       snprintf(buffer, sizeof(buffer)-1, "%u\n", getpid());
+
+       bufferlen = strlen(buffer);
+       write_out = write(fd, buffer, bufferlen);
+
+       if ((write_out < 0) || (write_out == 0 && errno)) {
+               log_error("Cannot write pid to pidfile [%s], error was [%s]",
+                         lockfile, strerror(errno));
+
+               goto fail_close_unlink;
+       }
+
+       if ((write_out == 0) || (write_out < bufferlen)) {
+               log_error("Cannot write pid to pidfile [%s], shortwrite of"
+                         "[%" PRIsize_t "] bytes, expected [%" PRIsize_t "]\n",
+                         lockfile, write_out, bufferlen);
+
+               goto fail_close_unlink;
+       }
+
+       if ((value = fcntl(fd, F_GETFD, 0)) < 0) {
+               log_error("Cannot get close-on-exec flag from pidfile [%s], "
+                         "error was [%s]", lockfile, strerror(errno));
+
+               goto fail_close_unlink;
+       }
+       value |= FD_CLOEXEC;
+       if (fcntl(fd, F_SETFD, value) < 0) {
+               log_error("Cannot set close-on-exec flag from pidfile [%s], "
+                         "error was [%s]", lockfile, strerror(errno));
+
+               goto fail_close_unlink;
+       }
+
+       return 1;
+
+fail_close_unlink:
+       if (unlink(lockfile))
+               stack;
+fail_close:
+       if (close(fd))
+               stack;
+
+       return 0;
+}
+
+int dm_daemon_is_running(const char* lockfile)
+{
+       int fd;
+       struct flock lock;
+
+       if((fd = open(lockfile, O_RDONLY)) < 0)
+               return 0;
+
+       lock.l_type = F_WRLCK;
+       lock.l_start = 0;
+       lock.l_whence = SEEK_SET;
+       lock.l_len = 0;
+       if (fcntl(fd, F_GETLK, &lock) < 0) {
+               log_error("Cannot check lock status of lockfile [%s], error was [%s]",
+                         lockfile, strerror(errno));
+               if (close(fd))
+                       stack;
+               return 0;
+       }
+
+       if (close(fd))
+               stack;
+
+       return (lock.l_type == F_UNLCK) ? 0 : 1;
+}
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
new file mode 100644 (file)
index 0000000..9b8e3c1
--- /dev/null
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+
+#include <ctype.h>
+
+/*
+ * Internal flags
+ */
+#define RH_SORT_REQUIRED       0x00000100
+#define RH_HEADINGS_PRINTED    0x00000200
+
+struct dm_report {
+       struct dm_pool *mem;
+
+       /* To report all available types */
+#define REPORT_TYPES_ALL       UINT32_MAX
+       uint32_t report_types;
+       const char *output_field_name_prefix;
+       const char *field_prefix;
+       uint32_t flags;
+       const char *separator;
+
+       uint32_t keys_count;
+
+       /* Ordered list of fields needed for this report */
+       struct dm_list field_props;
+
+       /* Rows of report data */
+       struct dm_list rows;
+
+       /* Array of field definitions */
+       const struct dm_report_field_type *fields;
+       const struct dm_report_object_type *types;
+
+       /* To store caller private data */
+       void *private;
+};
+
+/*
+ * Internal per-field flags
+ */
+#define FLD_HIDDEN     0x00000100
+#define FLD_SORT_KEY   0x00000200
+#define FLD_ASCENDING  0x00000400
+#define FLD_DESCENDING 0x00000800
+
+struct field_properties {
+       struct dm_list list;
+       uint32_t field_num;
+       uint32_t sort_posn;
+       int32_t width;
+       const struct dm_report_object_type *type;
+       uint32_t flags;
+};
+
+/*
+ * Report data field
+ */
+struct dm_report_field {
+       struct dm_list list;
+       struct field_properties *props;
+
+       const char *report_string;      /* Formatted ready for display */
+       const void *sort_value;         /* Raw value for sorting */
+};
+
+struct row {
+       struct dm_list list;
+       struct dm_report *rh;
+       struct dm_list fields;                    /* Fields in display order */
+       struct dm_report_field *(*sort_fields)[]; /* Fields in sort order */
+};
+
+static const struct dm_report_object_type *_find_type(struct dm_report *rh,
+                                                     uint32_t report_type)
+{
+       const struct dm_report_object_type *t;
+
+       for (t = rh->types; t->data_fn; t++)
+               if (t->id == report_type)
+                       return t;
+
+       return NULL;
+}
+
+/*
+ * Data-munging functions to prepare each data type for display and sorting
+ */
+
+int dm_report_field_string(struct dm_report *rh,
+                          struct dm_report_field *field, const char **data)
+{
+       char *repstr;
+
+       if (!(repstr = dm_pool_strdup(rh->mem, *data))) {
+               log_error("dm_report_field_string: dm_pool_strdup failed");
+               return 0;
+       }
+
+       field->report_string = repstr;
+       field->sort_value = (const void *) field->report_string;
+
+       return 1;
+}
+
+int dm_report_field_int(struct dm_report *rh,
+                       struct dm_report_field *field, const int *data)
+{
+       const int value = *data;
+       uint64_t *sortval;
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
+               log_error("dm_report_field_int: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
+               log_error("dm_report_field_int: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, 12, "%d", value) < 0) {
+               log_error("dm_report_field_int: int too big: %d", value);
+               return 0;
+       }
+
+       *sortval = (const uint64_t) value;
+       field->sort_value = sortval;
+       field->report_string = repstr;
+
+       return 1;
+}
+
+int dm_report_field_uint32(struct dm_report *rh,
+                          struct dm_report_field *field, const uint32_t *data)
+{
+       const uint32_t value = *data;
+       uint64_t *sortval;
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(rh->mem, 12))) {
+               log_error("dm_report_field_uint32: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
+               log_error("dm_report_field_uint32: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, 11, "%u", value) < 0) {
+               log_error("dm_report_field_uint32: uint32 too big: %u", value);
+               return 0;
+       }
+
+       *sortval = (const uint64_t) value;
+       field->sort_value = sortval;
+       field->report_string = repstr;
+
+       return 1;
+}
+
+int dm_report_field_int32(struct dm_report *rh,
+                         struct dm_report_field *field, const int32_t *data)
+{
+       const int32_t value = *data;
+       uint64_t *sortval;
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
+               log_error("dm_report_field_int32: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
+               log_error("dm_report_field_int32: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, 12, "%d", value) < 0) {
+               log_error("dm_report_field_int32: int32 too big: %d", value);
+               return 0;
+       }
+
+       *sortval = (const uint64_t) value;
+       field->sort_value = sortval;
+       field->report_string = repstr;
+
+       return 1;
+}
+
+int dm_report_field_uint64(struct dm_report *rh,
+                          struct dm_report_field *field, const uint64_t *data)
+{
+       const uint64_t value = *data;
+       uint64_t *sortval;
+       char *repstr;
+
+       if (!(repstr = dm_pool_zalloc(rh->mem, 22))) {
+               log_error("dm_report_field_uint64: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
+               log_error("dm_report_field_uint64: dm_pool_alloc failed");
+               return 0;
+       }
+
+       if (dm_snprintf(repstr, 21, "%" PRIu64 , value) < 0) {
+               log_error("dm_report_field_uint64: uint64 too big: %" PRIu64, value);
+               return 0;
+       }
+
+       *sortval = value;
+       field->sort_value = sortval;
+       field->report_string = repstr;
+
+       return 1;
+}
+
+/*
+ * Helper functions for custom report functions
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue)
+{
+       field->report_string = (const char *) value;
+       field->sort_value = sortvalue ? : value;
+}
+
+/*
+ * show help message
+ */
+static void _display_fields(struct dm_report *rh)
+{
+       uint32_t f;
+       const struct dm_report_object_type *type;
+       const char *desc, *last_desc = "";
+       size_t id_len = 0;
+
+       for (f = 0; rh->fields[f].report_fn; f++)
+               if (strlen(rh->fields[f].id) > id_len)
+                       id_len = strlen(rh->fields[f].id);
+
+
+       for (type = rh->types; type->data_fn; type++)
+               if (strlen(type->prefix) + 3 > id_len)
+                       id_len = strlen(type->prefix) + 3;
+
+       for (f = 0; rh->fields[f].report_fn; f++) {
+               if ((type = _find_type(rh, rh->fields[f].type)) && type->desc)
+                       desc = type->desc;
+               else
+                       desc = " ";
+               if (desc != last_desc) {
+                       if (*last_desc)
+                               log_warn(" ");
+                       log_warn("%s Fields", desc);
+                       log_warn("%*.*s", (int) strlen(desc) + 7,
+                                (int) strlen(desc) + 7,
+                                "-------------------------------------------------------------------------------");
+                       log_warn("  %sall%-*s - %s", type->prefix,
+                                (int) (id_len - 3 - strlen(type->prefix)), "",
+                                "All fields in this section.");
+               }
+
+               /* FIXME Add line-wrapping at terminal width (or 80 cols) */
+               log_warn("  %-*s - %s", (int) id_len, rh->fields[f].id, rh->fields[f].desc);
+               last_desc = desc;
+       }
+}
+
+/*
+ * Initialise report handle
+ */
+static int _copy_field(struct dm_report *rh, struct field_properties *dest,
+                      uint32_t field_num)
+{
+       dest->field_num = field_num;
+       dest->width = rh->fields[field_num].width;
+       dest->flags = rh->fields[field_num].flags & DM_REPORT_FIELD_MASK;
+
+       /* set object type method */
+       dest->type = _find_type(rh, rh->fields[field_num].type);
+       if (!dest->type) {
+               log_error("dm_report: field not match: %s",
+                         rh->fields[field_num].id);
+               return 0;
+       }
+
+       return 1;
+}
+
+static struct field_properties * _add_field(struct dm_report *rh,
+                                           uint32_t field_num, uint32_t flags)
+{
+       struct field_properties *fp;
+
+       if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
+               log_error("dm_report: struct field_properties allocation "
+                         "failed");
+               return NULL;
+       }
+
+       if (!_copy_field(rh, fp, field_num)) {
+               stack;
+               dm_pool_free(rh->mem, fp);
+               return NULL;
+       }
+
+       fp->flags |= flags;
+
+       /*
+        * Place hidden fields at the front so dm_list_end() will
+        * tell us when we've reached the last visible field.
+        */
+       if (fp->flags & FLD_HIDDEN)
+               dm_list_add_h(&rh->field_props, &fp->list);
+       else
+               dm_list_add(&rh->field_props, &fp->list);
+
+       return fp;
+}
+
+/*
+ * Compare name1 against name2 or prefix plus name2
+ * name2 is not necessarily null-terminated.
+ * len2 is the length of name2.
+ */
+static int _is_same_field(const char *name1, const char *name2,
+                         size_t len2, const char *prefix)
+{
+       size_t prefix_len;
+
+       /* Exact match? */
+       if (!strncasecmp(name1, name2, len2) && strlen(name1) == len2)
+               return 1;
+
+       /* Match including prefix? */
+       prefix_len = strlen(prefix);
+       if (!strncasecmp(prefix, name1, prefix_len) &&
+           !strncasecmp(name1 + prefix_len, name2, len2) &&
+           strlen(name1) == prefix_len + len2)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Check for a report type prefix + "all" match.
+ */
+static uint32_t _all_match(struct dm_report *rh, const char *field, size_t flen)
+{
+       size_t prefix_len;
+       const struct dm_report_object_type *t;
+       char prefixed_all[32];
+
+       if (!strncasecmp(field, "all", 3) && flen == 3) {
+               if (strlen(rh->field_prefix)) {
+                       strcpy(prefixed_all, rh->field_prefix);
+                       strcat(prefixed_all, "all");
+                       /*
+                        * Add also prefix to receive all attributes
+                        * (e.g.LABEL/PVS use the same prefix)
+                        */
+                       return rh->report_types |
+                              _all_match(rh, prefixed_all,
+                                         strlen(prefixed_all));
+               } else
+                       return (rh->report_types)
+                               ? rh->report_types : REPORT_TYPES_ALL;
+       }
+
+       for (t = rh->types; t->data_fn; t++) {
+               prefix_len = strlen(t->prefix);
+               if (!strncasecmp(t->prefix, field, prefix_len) &&
+                   !strncasecmp(field + prefix_len, "all", 3) &&
+                   flen == prefix_len + 3)
+                       return t->id;
+       }
+
+       return 0;
+}
+
+/*
+ * Add all fields with a matching type.
+ */
+static int _add_all_fields(struct dm_report *rh, uint32_t type)
+{
+       uint32_t f;
+
+       for (f = 0; rh->fields[f].report_fn; f++)
+               if ((rh->fields[f].type & type) && !_add_field(rh, f, 0))
+                       return 0;
+
+       return 1;
+}
+
+static int _field_match(struct dm_report *rh, const char *field, size_t flen,
+                       unsigned report_type_only)
+{
+       uint32_t f, type;
+
+       if (!flen)
+               return 0;
+
+       for (f = 0; rh->fields[f].report_fn; f++)
+               if (_is_same_field(rh->fields[f].id, field, flen,
+                                  rh->field_prefix)) {
+                       if (report_type_only) {
+                               rh->report_types |= rh->fields[f].type;
+                               return 1;
+                       } else
+                               return _add_field(rh, f, 0) ? 1 : 0;
+               }
+
+       if ((type = _all_match(rh, field, flen))) {
+               if (report_type_only) {
+                       rh->report_types |= type;
+                       return 1;
+               } else
+                       return  _add_all_fields(rh, type);
+       }
+
+       return 0;
+}
+
+static int _add_sort_key(struct dm_report *rh, uint32_t field_num,
+                        uint32_t flags, unsigned report_type_only)
+{
+       struct field_properties *fp, *found = NULL;
+
+       dm_list_iterate_items(fp, &rh->field_props) {
+               if (fp->field_num == field_num) {
+                       found = fp;
+                       break;
+               }
+       }
+
+       if (!found) {
+               if (report_type_only)
+                       rh->report_types |= rh->fields[field_num].type;
+               else if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
+                       return_0;
+       }
+
+       if (report_type_only)
+               return 1;
+
+       if (found->flags & FLD_SORT_KEY) {
+               log_error("dm_report: Ignoring duplicate sort field: %s",
+                         rh->fields[field_num].id);
+               return 1;
+       }
+
+       found->flags |= FLD_SORT_KEY;
+       found->sort_posn = rh->keys_count++;
+       found->flags |= flags;
+
+       return 1;
+}
+
+static int _key_match(struct dm_report *rh, const char *key, size_t len,
+                     unsigned report_type_only)
+{
+       uint32_t f;
+       uint32_t flags;
+
+       if (!len)
+               return 0;
+
+       if (*key == '+') {
+               key++;
+               len--;
+               flags = FLD_ASCENDING;
+       } else if (*key == '-') {
+               key++;
+               len--;
+               flags = FLD_DESCENDING;
+       } else
+               flags = FLD_ASCENDING;
+
+       if (!len) {
+               log_error("dm_report: Missing sort field name");
+               return 0;
+       }
+
+       for (f = 0; rh->fields[f].report_fn; f++)
+               if (_is_same_field(rh->fields[f].id, key, len,
+                                  rh->field_prefix))
+                       return _add_sort_key(rh, f, flags, report_type_only);
+
+       return 0;
+}
+
+static int _parse_fields(struct dm_report *rh, const char *format,
+                        unsigned report_type_only)
+{
+       const char *ws;         /* Word start */
+       const char *we = format;        /* Word end */
+
+       while (*we) {
+               /* Allow consecutive commas */
+               while (*we && *we == ',')
+                       we++;
+
+               /* start of the field name */
+               ws = we;
+               while (*we && *we != ',')
+                       we++;
+
+               if (!_field_match(rh, ws, (size_t) (we - ws), report_type_only)) {
+                       _display_fields(rh);
+                       log_warn(" ");
+                       if (strcasecmp(ws, "help") && strcmp(ws, "?"))
+                               log_error("Unrecognised field: %.*s",
+                                         (int) (we - ws), ws);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int _parse_keys(struct dm_report *rh, const char *keys,
+                      unsigned report_type_only)
+{
+       const char *ws;         /* Word start */
+       const char *we = keys;  /* Word end */
+
+       while (*we) {
+               /* Allow consecutive commas */
+               while (*we && *we == ',')
+                       we++;
+               ws = we;
+               while (*we && *we != ',')
+                       we++;
+               if (!_key_match(rh, ws, (size_t) (we - ws), report_type_only)) {
+                       log_error("dm_report: Unrecognised field: %.*s",
+                                 (int) (we - ws), ws);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+                                const struct dm_report_object_type *types,
+                                const struct dm_report_field_type *fields,
+                                const char *output_fields,
+                                const char *output_separator,
+                                uint32_t output_flags,
+                                const char *sort_keys,
+                                void *private_data)
+{
+       struct dm_report *rh;
+       const struct dm_report_object_type *type;
+
+       if (!(rh = dm_zalloc(sizeof(*rh)))) {
+               log_error("dm_report_init: dm_malloc failed");
+               return 0;
+       }
+
+       /*
+        * rh->report_types is updated in _parse_fields() and _parse_keys()
+        * to contain all types corresponding to the fields specified by
+        * fields or keys.
+        */
+       if (report_types)
+               rh->report_types = *report_types;
+
+       rh->separator = output_separator;
+       rh->fields = fields;
+       rh->types = types;
+       rh->private = private_data;
+
+       rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
+
+       /* With columns_as_rows we must buffer and not align. */
+       if (output_flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS) {
+               if (!(output_flags & DM_REPORT_OUTPUT_BUFFERED))
+                       rh->flags |= DM_REPORT_OUTPUT_BUFFERED;
+               if (output_flags & DM_REPORT_OUTPUT_ALIGNED)
+                       rh->flags &= ~DM_REPORT_OUTPUT_ALIGNED;
+       }
+
+       if (output_flags & DM_REPORT_OUTPUT_BUFFERED)
+               rh->flags |= RH_SORT_REQUIRED;
+
+       dm_list_init(&rh->field_props);
+       dm_list_init(&rh->rows);
+
+       if ((type = _find_type(rh, rh->report_types)) && type->prefix)
+               rh->field_prefix = type->prefix;
+       else
+               rh->field_prefix = "";
+
+       if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
+               log_error("dm_report_init: allocation of memory pool failed");
+               dm_free(rh);
+               return NULL;
+       }
+
+       /*
+        * To keep the code needed to add the "all" field to a minimum, we parse
+        * the field lists twice.  The first time we only update the report type.
+        * FIXME Use one pass instead and expand the "all" field afterwards.
+        */
+       if (!_parse_fields(rh, output_fields, 1) ||
+           !_parse_keys(rh, sort_keys, 1)) {
+               dm_report_free(rh);
+               return NULL;
+       }
+
+       /* Generate list of fields for output based on format string & flags */
+       if (!_parse_fields(rh, output_fields, 0) ||
+           !_parse_keys(rh, sort_keys, 0)) {
+               dm_report_free(rh);
+               return NULL;
+       }
+
+       /* Return updated types value for further compatility check by caller */
+       if (report_types)
+               *report_types = rh->report_types;
+
+       return rh;
+}
+
+void dm_report_free(struct dm_report *rh)
+{
+       dm_pool_destroy(rh->mem);
+       dm_free(rh);
+}
+
+static char *_toupperstr(char *str)
+{
+       char *u = str;
+
+       do
+               *u = toupper(*u);
+       while (*u++);
+
+       return str;
+}
+
+int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
+{
+       char *prefix;
+
+       if (!(prefix = dm_pool_strdup(rh->mem, output_field_name_prefix))) {
+               log_error("dm_report_set_output_field_name_prefix: dm_pool_strdup failed");
+               return 0;
+       }
+
+       rh->output_field_name_prefix = _toupperstr(prefix);
+       
+       return 1;
+}
+
+/*
+ * Create a row of data for an object
+ */
+static void * _report_get_field_data(struct dm_report *rh,
+                             struct field_properties *fp, void *object)
+{
+       void *ret = fp->type->data_fn(object);
+
+       if (!ret)
+               return NULL;
+
+       return ret + rh->fields[fp->field_num].offset;
+}
+
+int dm_report_object(struct dm_report *rh, void *object)
+{
+       struct field_properties *fp;
+       struct row *row;
+       struct dm_report_field *field;
+       void *data = NULL;
+
+       if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
+               log_error("dm_report_object: struct row allocation failed");
+               return 0;
+       }
+
+       row->rh = rh;
+
+       if ((rh->flags & RH_SORT_REQUIRED) &&
+           !(row->sort_fields =
+               dm_pool_zalloc(rh->mem, sizeof(struct dm_report_field *) *
+                              rh->keys_count))) {
+               log_error("dm_report_object: "
+                         "row sort value structure allocation failed");
+               return 0;
+       }
+
+       dm_list_init(&row->fields);
+       dm_list_add(&rh->rows, &row->list);
+
+       /* For each field to be displayed, call its report_fn */
+       dm_list_iterate_items(fp, &rh->field_props) {
+               if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
+                       log_error("dm_report_object: "
+                                 "struct dm_report_field allocation failed");
+                       return 0;
+               }
+               field->props = fp;
+
+               data = _report_get_field_data(rh, fp, object);
+               if (!data)
+                       return 0;
+
+               if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
+                                                        field, data,
+                                                        rh->private)) {
+                       log_error("dm_report_object: "
+                                 "report function failed for field %s",
+                                 rh->fields[fp->field_num].id);
+                       return 0;
+               }
+
+               if ((strlen(field->report_string) > field->props->width))
+                       field->props->width = strlen(field->report_string);
+
+               if ((rh->flags & RH_SORT_REQUIRED) &&
+                   (field->props->flags & FLD_SORT_KEY)) {
+                       (*row->sort_fields)[field->props->sort_posn] = field;
+               }
+               dm_list_add(&row->fields, &field->list);
+       }
+
+       if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
+               return dm_report_output(rh);
+
+       return 1;
+}
+
+/*
+ * Print row of headings
+ */
+static int _report_headings(struct dm_report *rh)
+{
+       struct field_properties *fp;
+       const char *heading;
+       char *buf = NULL;
+       size_t buf_size = 0;
+
+       if (rh->flags & RH_HEADINGS_PRINTED)
+               return 1;
+
+       rh->flags |= RH_HEADINGS_PRINTED;
+
+       if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
+               return 1;
+
+       if (!dm_pool_begin_object(rh->mem, 128)) {
+               log_error("dm_report: "
+                         "dm_pool_begin_object failed for headings");
+               return 0;
+       }
+
+       dm_list_iterate_items(fp, &rh->field_props) {
+               if (buf_size < fp->width)
+                       buf_size = fp->width;
+       }
+       /* Including trailing '\0'! */
+       buf_size++;
+
+       if (!(buf = dm_malloc(buf_size))) {
+               log_error("dm_report: Could not allocate memory for heading buffer.");
+               goto bad;
+       }
+
+       /* First heading line */
+       dm_list_iterate_items(fp, &rh->field_props) {
+               if (fp->flags & FLD_HIDDEN)
+                       continue;
+
+               heading = rh->fields[fp->field_num].heading;
+               if (rh->flags & DM_REPORT_OUTPUT_ALIGNED) {
+                       if (dm_snprintf(buf, buf_size, "%-*.*s",
+                                        fp->width, fp->width, heading) < 0) {
+                               log_error("dm_report: snprintf heading failed");
+                               goto bad;
+                       }
+                       if (!dm_pool_grow_object(rh->mem, buf, fp->width)) {
+                               log_error("dm_report: Failed to generate report headings for printing");
+                               goto bad;
+                       }
+               } else if (!dm_pool_grow_object(rh->mem, heading, 0)) {
+                       log_error("dm_report: Failed to generate report headings for printing");
+                       goto bad;
+               }
+
+               if (!dm_list_end(&rh->field_props, &fp->list))
+                       if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+                               log_error("dm_report: Failed to generate report headings for printing");
+                               goto bad;
+                       }
+       }
+       if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+               log_error("dm_report: Failed to generate report headings for printing");
+               goto bad;
+       }
+       log_print("%s", (char *) dm_pool_end_object(rh->mem));
+
+       dm_free(buf);
+
+       return 1;
+
+      bad:
+       dm_free(buf);
+       dm_pool_abandon_object(rh->mem);
+       return 0;
+}
+
+/*
+ * Sort rows of data
+ */
+static int _row_compare(const void *a, const void *b)
+{
+       const struct row *rowa = *(const struct row * const *) a;
+       const struct row *rowb = *(const struct row * const *) b;
+       const struct dm_report_field *sfa, *sfb;
+       uint32_t cnt;
+
+       for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
+               sfa = (*rowa->sort_fields)[cnt];
+               sfb = (*rowb->sort_fields)[cnt];
+               if (sfa->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) {
+                       const uint64_t numa =
+                           *(const uint64_t *) sfa->sort_value;
+                       const uint64_t numb =
+                           *(const uint64_t *) sfb->sort_value;
+
+                       if (numa == numb)
+                               continue;
+
+                       if (sfa->props->flags & FLD_ASCENDING) {
+                               return (numa > numb) ? 1 : -1;
+                       } else {        /* FLD_DESCENDING */
+                               return (numa < numb) ? 1 : -1;
+                       }
+               } else {        /* DM_REPORT_FIELD_TYPE_STRING */
+                       const char *stra = (const char *) sfa->sort_value;
+                       const char *strb = (const char *) sfb->sort_value;
+                       int cmp = strcmp(stra, strb);
+
+                       if (!cmp)
+                               continue;
+
+                       if (sfa->props->flags & FLD_ASCENDING) {
+                               return (cmp > 0) ? 1 : -1;
+                       } else {        /* FLD_DESCENDING */
+                               return (cmp < 0) ? 1 : -1;
+                       }
+               }
+       }
+
+       return 0;               /* Identical */
+}
+
+static int _sort_rows(struct dm_report *rh)
+{
+       struct row *(*rows)[];
+       uint32_t count = 0;
+       struct row *row;
+
+       if (!(rows = dm_pool_alloc(rh->mem, sizeof(**rows) *
+                               dm_list_size(&rh->rows)))) {
+               log_error("dm_report: sort array allocation failed");
+               return 0;
+       }
+
+       dm_list_iterate_items(row, &rh->rows)
+               (*rows)[count++] = row;
+
+       qsort(rows, count, sizeof(**rows), _row_compare);
+
+       dm_list_init(&rh->rows);
+       while (count--)
+               dm_list_add_h(&rh->rows, &(*rows)[count]->list);
+
+       return 1;
+}
+
+/*
+ * Produce report output
+ */
+static int _output_field(struct dm_report *rh, struct dm_report_field *field)
+{
+       char *field_id;
+       int32_t width;
+       uint32_t align;
+       const char *repstr;
+       char *buf = NULL;
+       size_t buf_size = 0;
+
+       if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
+               if (!(field_id = dm_strdup(rh->fields[field->props->field_num].id))) {
+                       log_error("dm_report: Failed to copy field name");
+                       return 0;
+               }
+
+               if (!dm_pool_grow_object(rh->mem, rh->output_field_name_prefix, 0)) {
+                       log_error("dm_report: Unable to extend output line");
+                       dm_free(field_id);
+                       return 0;
+               }
+
+               if (!dm_pool_grow_object(rh->mem, _toupperstr(field_id), 0)) {
+                       log_error("dm_report: Unable to extend output line");
+                       dm_free(field_id);
+                       return 0;
+               }
+
+               dm_free(field_id);
+
+               if (!dm_pool_grow_object(rh->mem, "=", 1)) {
+                       log_error("dm_report: Unable to extend output line");
+                       return 0;
+               }
+
+               if (!(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED) &&
+                   !dm_pool_grow_object(rh->mem, "\'", 1)) {
+                       log_error("dm_report: Unable to extend output line");
+                       return 0;
+               }
+       }
+
+       repstr = field->report_string;
+       width = field->props->width;
+       if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
+               if (!dm_pool_grow_object(rh->mem, repstr, 0)) {
+                       log_error("dm_report: Unable to extend output line");
+                       return 0;
+               }
+       } else {
+               if (!(align = field->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
+                       align = (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ? 
+                               DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
+
+               /* Including trailing '\0'! */
+               buf_size = width + 1;
+               if (!(buf = dm_malloc(buf_size))) {
+                       log_error("dm_report: Could not allocate memory for output line buffer.");
+                       return 0;
+               }
+
+               if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
+                       if (dm_snprintf(buf, buf_size, "%-*.*s",
+                                        width, width, repstr) < 0) {
+                               log_error("dm_report: left-aligned snprintf() failed");
+                               goto bad;
+                       }
+                       if (!dm_pool_grow_object(rh->mem, buf, width)) {
+                               log_error("dm_report: Unable to extend output line");
+                               goto bad;
+                       }
+               } else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
+                       if (dm_snprintf(buf, buf_size, "%*.*s",
+                                        width, width, repstr) < 0) {
+                               log_error("dm_report: right-aligned snprintf() failed");
+                               goto bad;
+                       }
+                       if (!dm_pool_grow_object(rh->mem, buf, width)) {
+                               log_error("dm_report: Unable to extend output line");
+                               goto bad;
+                       }
+               }
+       }
+
+       if ((rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) &&
+           !(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED))
+               if (!dm_pool_grow_object(rh->mem, "\'", 1)) {
+                       log_error("dm_report: Unable to extend output line");
+                       goto bad;
+               }
+
+       dm_free(buf);
+       return 1;
+
+bad:
+       dm_free(buf);
+       return 0;
+}
+
+static int _output_as_rows(struct dm_report *rh)
+{
+       struct field_properties *fp;
+       struct dm_report_field *field;
+       struct row *row;
+
+       if (!dm_pool_begin_object(rh->mem, 512)) {
+               log_error("dm_report: Unable to allocate output line");
+               return 0;
+       }
+
+       dm_list_iterate_items(fp, &rh->field_props) {
+               if (fp->flags & FLD_HIDDEN) {
+                       dm_list_iterate_items(row, &rh->rows) {
+                               field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field);
+                               dm_list_del(&field->list);
+                       }
+                       continue;
+               }
+
+               if ((rh->flags & DM_REPORT_OUTPUT_HEADINGS)) {
+                       if (!dm_pool_grow_object(rh->mem, rh->fields[fp->field_num].heading, 0)) {
+                               log_error("dm_report: Failed to extend row for field name");
+                               goto bad;
+                       }
+                       if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+                               log_error("dm_report: Failed to extend row with separator");
+                               goto bad;
+                       }
+               }
+
+               dm_list_iterate_items(row, &rh->rows) {
+                       if ((field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field))) {
+                               if (!_output_field(rh, field))
+                                       goto bad;
+                               dm_list_del(&field->list);
+                       }
+
+                       if (!dm_list_end(&rh->rows, &row->list))
+                               if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+                                       log_error("dm_report: Unable to extend output line");
+                                       goto bad;
+                               }
+               }
+
+               if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+                       log_error("dm_report: Failed to terminate row");
+                       goto bad;
+               }
+               log_print("%s", (char *) dm_pool_end_object(rh->mem));
+       }
+
+       return 1;
+
+      bad:
+       dm_pool_abandon_object(rh->mem);
+       return 0;
+}
+
+static int _output_as_columns(struct dm_report *rh)
+{
+       struct dm_list *fh, *rowh, *ftmp, *rtmp;
+       struct row *row = NULL;
+       struct dm_report_field *field;
+
+       /* If headings not printed yet, calculate field widths and print them */
+       if (!(rh->flags & RH_HEADINGS_PRINTED))
+               _report_headings(rh);
+
+       /* Print and clear buffer */
+       dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
+               if (!dm_pool_begin_object(rh->mem, 512)) {
+                       log_error("dm_report: Unable to allocate output line");
+                       return 0;
+               }
+               row = dm_list_item(rowh, struct row);
+               dm_list_iterate_safe(fh, ftmp, &row->fields) {
+                       field = dm_list_item(fh, struct dm_report_field);
+                       if (field->props->flags & FLD_HIDDEN)
+                               continue;
+
+                       if (!_output_field(rh, field))
+                               goto bad;
+
+                       if (!dm_list_end(&row->fields, fh))
+                               if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+                                       log_error("dm_report: Unable to extend output line");
+                                       goto bad;
+                               }
+
+                       dm_list_del(&field->list);
+               }
+               if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+                       log_error("dm_report: Unable to terminate output line");
+                       goto bad;
+               }
+               log_print("%s", (char *) dm_pool_end_object(rh->mem));
+               dm_list_del(&row->list);
+       }
+
+       if (row)
+               dm_pool_free(rh->mem, row);
+
+       return 1;
+
+      bad:
+       dm_pool_abandon_object(rh->mem);
+       return 0;
+}
+
+int dm_report_output(struct dm_report *rh)
+{
+       if (dm_list_empty(&rh->rows))
+               return 1;
+
+       if ((rh->flags & RH_SORT_REQUIRED))
+               _sort_rows(rh);
+
+       if ((rh->flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS))
+               return _output_as_rows(rh);
+       else
+               return _output_as_columns(rh);
+}
diff --git a/libdm/libdm-string.c b/libdm/libdm-string.c
new file mode 100644 (file)
index 0000000..365e7ec
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2006-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "libdevmapper.h"
+
+#include <ctype.h>
+
+/*
+ * consume characters while they match the predicate function.
+ */
+static char *_consume(char *buffer, int (*fn) (int))
+{
+       while (*buffer && fn(*buffer))
+               buffer++;
+
+       return buffer;
+}
+
+static int _isword(int c)
+{
+       return !isspace(c);
+}
+
+/*
+ * Split buffer into NULL-separated words in argv.
+ * Returns number of words.
+ */
+int dm_split_words(char *buffer, unsigned max,
+                  unsigned ignore_comments __attribute__((unused)),
+                  char **argv)
+{
+       unsigned arg;
+
+       for (arg = 0; arg < max; arg++) {
+               buffer = _consume(buffer, isspace);
+               if (!*buffer)
+                       break;
+
+               argv[arg] = buffer;
+               buffer = _consume(buffer, _isword);
+
+               if (*buffer) {
+                       *buffer = '\0';
+                       buffer++;
+               }
+       }
+
+       return arg;
+}
+
+/*
+ * Remove hyphen quoting from a component of a name.
+ * NULL-terminates the component and returns start of next component.
+ */
+static char *_unquote(char *component)
+{
+       char *c = component;
+       char *o = c;
+       char *r;
+
+       while (*c) {
+               if (*(c + 1)) {
+                       if (*c == '-') {
+                               if (*(c + 1) == '-')
+                                       c++;
+                               else
+                                       break;
+                       }
+               }
+               *o = *c;
+               o++;
+               c++;
+       }
+
+       r = (*c) ? c + 1 : c;
+       *o = '\0';
+
+       return r;
+}
+
+int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
+                     char **vgname, char **lvname, char **layer)
+{
+       if (mem && !(*vgname = dm_pool_strdup(mem, dmname)))
+               return 0;
+
+       _unquote(*layer = _unquote(*lvname = _unquote(*vgname)));
+
+       return 1;
+}
+
+/*
+ * On error, up to glibc 2.0.6, snprintf returned -1 if buffer was too small;
+ * From glibc 2.1 it returns number of chars (excl. trailing null) that would 
+ * have been written had there been room.
+ *
+ * dm_snprintf reverts to the old behaviour.
+ */
+int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
+{
+       int n;
+       va_list ap;
+
+       va_start(ap, format);
+       n = vsnprintf(buf, bufsize, format, ap);
+       va_end(ap);
+
+       if (n < 0 || ((unsigned) n + 1 > bufsize))
+               return -1;
+
+       return n;
+}
+
+const char *dm_basename(const char *path)
+{
+       const char *p = strrchr(path, '/');
+
+       return p ? p + 1 : path;
+}
+
+int dm_asprintf(char **result, const char *format, ...)
+{
+       int n, ok = 0, size = 32;
+       va_list ap;
+       char *buf = dm_malloc(size);
+
+       *result = 0;
+
+       if (!buf)
+               return -1;
+
+       while (!ok) {
+               va_start(ap, format);
+               n = vsnprintf(buf, size, format, ap);
+               va_end(ap);
+
+               if (0 <= n && n < size)
+                       ok = 1;
+               else {
+                       dm_free(buf);
+                       size *= 2;
+                       buf = dm_malloc(size);
+                       if (!buf)
+                               return -1;
+               }
+       }
+
+       *result = dm_strdup(buf);
+       dm_free(buf);
+       return n + 1;
+}
diff --git a/libdm/misc/dm-ioctl.h b/libdm/misc/dm-ioctl.h
new file mode 100644 (file)
index 0000000..fb11b5c
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited.
+ * Copyright (C) 2004 - 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef _LINUX_DM_IOCTL_V4_H
+#define _LINUX_DM_IOCTL_V4_H
+
+#ifdef linux
+#  include <linux/types.h>
+#endif
+
+#define DM_DIR "mapper"                /* Slashes not supported */
+#define DM_CONTROL_NODE "control"
+#define DM_MAX_TYPE_NAME 16
+#define DM_NAME_LEN 128
+#define DM_UUID_LEN 129
+
+/*
+ * A traditional ioctl interface for the device mapper.
+ *
+ * Each device can have two tables associated with it, an
+ * 'active' table which is the one currently used by io passing
+ * through the device, and an 'inactive' one which is a table
+ * that is being prepared as a replacement for the 'active' one.
+ *
+ * DM_VERSION:
+ * Just get the version information for the ioctl interface.
+ *
+ * DM_REMOVE_ALL:
+ * Remove all dm devices, destroy all tables.  Only really used
+ * for debug.
+ *
+ * DM_LIST_DEVICES:
+ * Get a list of all the dm device names.
+ *
+ * DM_DEV_CREATE:
+ * Create a new device, neither the 'active' or 'inactive' table
+ * slots will be filled.  The device will be in suspended state
+ * after creation, however any io to the device will get errored
+ * since it will be out-of-bounds.
+ *
+ * DM_DEV_REMOVE:
+ * Remove a device, destroy any tables.
+ *
+ * DM_DEV_RENAME:
+ * Rename a device or set its uuid if none was previously supplied.
+ *
+ * DM_SUSPEND:
+ * This performs both suspend and resume, depending which flag is
+ * passed in.
+ * Suspend: This command will not return until all pending io to
+ * the device has completed.  Further io will be deferred until
+ * the device is resumed.
+ * Resume: It is no longer an error to issue this command on an
+ * unsuspended device.  If a table is present in the 'inactive'
+ * slot, it will be moved to the active slot, then the old table
+ * from the active slot will be _destroyed_.  Finally the device
+ * is resumed.
+ *
+ * DM_DEV_STATUS:
+ * Retrieves the status for the table in the 'active' slot.
+ *
+ * DM_DEV_WAIT:
+ * Wait for a significant event to occur to the device.  This
+ * could either be caused by an event triggered by one of the
+ * targets of the table in the 'active' slot, or a table change.
+ *
+ * DM_TABLE_LOAD:
+ * Load a table into the 'inactive' slot for the device.  The
+ * device does _not_ need to be suspended prior to this command.
+ *
+ * DM_TABLE_CLEAR:
+ * Destroy any table in the 'inactive' slot (ie. abort).
+ *
+ * DM_TABLE_DEPS:
+ * Return a set of device dependencies for the 'active' table.
+ *
+ * DM_TABLE_STATUS:
+ * Return the targets status for the 'active' table.
+ *
+ * DM_TARGET_MSG:
+ * Pass a message string to the target at a specific offset of a device.
+ *
+ * DM_DEV_SET_GEOMETRY:
+ * Set the geometry of a device by passing in a string in this format:
+ *
+ * "cylinders heads sectors_per_track start_sector"
+ *
+ * Beware that CHS geometry is nearly obsolete and only provided
+ * for compatibility with dm devices that can be booted by a PC
+ * BIOS.  See struct hd_geometry for range limits.  Also note that
+ * the geometry is erased if the device size changes.
+ */
+
+/*
+ * All ioctl arguments consist of a single chunk of memory, with
+ * this structure at the start.  If a uuid is specified any
+ * lookup (eg. for a DM_INFO) will be done on that, *not* the
+ * name.
+ */
+struct dm_ioctl {
+       /*
+        * The version number is made up of three parts:
+        * major - no backward or forward compatibility,
+        * minor - only backwards compatible,
+        * patch - both backwards and forwards compatible.
+        *
+        * All clients of the ioctl interface should fill in the
+        * version number of the interface that they were
+        * compiled with.
+        *
+        * All recognised ioctl commands (ie. those that don't
+        * return -ENOTTY) fill out this field, even if the
+        * command failed.
+        */
+       uint32_t version[3];    /* in/out */
+       uint32_t data_size;     /* total size of data passed in
+                                * including this struct */
+
+       uint32_t data_start;    /* offset to start of data
+                                * relative to start of this struct */
+
+       uint32_t target_count;  /* in/out */
+       int32_t open_count;     /* out */
+       uint32_t flags;         /* in/out */
+
+       /*
+        * event_nr holds either the event number (input and output) or the
+        * udev cookie value (input only).
+        * The DM_DEV_WAIT ioctl takes an event number as input.
+        * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls
+        * use the field as a cookie to return in the DM_COOKIE
+        * variable with the uevents they issue.
+        * For output, the ioctls return the event number, not the cookie.
+        */
+       uint32_t event_nr;              /* in/out */
+       uint32_t padding;
+
+       uint64_t dev;           /* in/out */
+
+       char name[DM_NAME_LEN]; /* device name */
+       char uuid[DM_UUID_LEN]; /* unique identifier for
+                                * the block device */
+       char data[7];           /* padding or data */
+};
+
+/*
+ * Used to specify tables.  These structures appear after the
+ * dm_ioctl.
+ */
+struct dm_target_spec {
+       uint64_t sector_start;
+       uint64_t length;
+       int32_t status;         /* used when reading from kernel only */
+
+       /*
+        * Location of the next dm_target_spec.
+        * - When specifying targets on a DM_TABLE_LOAD command, this value is
+        *   the number of bytes from the start of the "current" dm_target_spec
+        *   to the start of the "next" dm_target_spec.
+        * - When retrieving targets on a DM_TABLE_STATUS command, this value
+        *   is the number of bytes from the start of the first dm_target_spec
+        *   (that follows the dm_ioctl struct) to the start of the "next"
+        *   dm_target_spec.
+        */
+       uint32_t next;
+
+       char target_type[DM_MAX_TYPE_NAME];
+
+       /*
+        * Parameter string starts immediately after this object.
+        * Be careful to add padding after string to ensure correct
+        * alignment of subsequent dm_target_spec.
+        */
+};
+
+/*
+ * Used to retrieve the target dependencies.
+ */
+struct dm_target_deps {
+       uint32_t count; /* Array size */
+       uint32_t padding;       /* unused */
+       uint64_t dev[0];        /* out */
+};
+
+/*
+ * Used to get a list of all dm devices.
+ */
+struct dm_name_list {
+       uint64_t dev;
+       uint32_t next;          /* offset to the next record from
+                                  the _start_ of this */
+       char name[0];
+};
+
+/*
+ * Used to retrieve the target versions
+ */
+struct dm_target_versions {
+        uint32_t next;
+        uint32_t version[3];
+
+        char name[0];
+};
+
+/*
+ * Used to pass message to a target
+ */
+struct dm_target_msg {
+       uint64_t sector;        /* Device sector */
+
+       char message[0];
+};
+
+/*
+ * If you change this make sure you make the corresponding change
+ * to dm-ioctl.c:lookup_ioctl()
+ */
+enum {
+       /* Top level cmds */
+       DM_VERSION_CMD = 0,
+       DM_REMOVE_ALL_CMD,
+       DM_LIST_DEVICES_CMD,
+
+       /* device level cmds */
+       DM_DEV_CREATE_CMD,
+       DM_DEV_REMOVE_CMD,
+       DM_DEV_RENAME_CMD,
+       DM_DEV_SUSPEND_CMD,
+       DM_DEV_STATUS_CMD,
+       DM_DEV_WAIT_CMD,
+
+       /* Table level cmds */
+       DM_TABLE_LOAD_CMD,
+       DM_TABLE_CLEAR_CMD,
+       DM_TABLE_DEPS_CMD,
+       DM_TABLE_STATUS_CMD,
+
+       /* Added later */
+       DM_LIST_VERSIONS_CMD,
+       DM_TARGET_MSG_CMD,
+       DM_DEV_SET_GEOMETRY_CMD
+};
+
+#define DM_IOCTL 0xfd
+
+#define DM_VERSION       _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
+#define DM_REMOVE_ALL    _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
+#define DM_LIST_DEVICES  _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl)
+
+#define DM_DEV_CREATE    _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl)
+#define DM_DEV_REMOVE    _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl)
+#define DM_DEV_RENAME    _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl)
+#define DM_DEV_SUSPEND   _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl)
+#define DM_DEV_STATUS    _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl)
+#define DM_DEV_WAIT      _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl)
+
+#define DM_TABLE_LOAD    _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl)
+#define DM_TABLE_CLEAR   _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl)
+#define DM_TABLE_DEPS    _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl)
+#define DM_TABLE_STATUS  _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl)
+
+#define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl)
+
+#define DM_TARGET_MSG   _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl)
+#define DM_DEV_SET_GEOMETRY    _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
+
+#define DM_VERSION_MAJOR       4
+#define DM_VERSION_MINOR       19
+#define DM_VERSION_PATCHLEVEL  0
+#define DM_VERSION_EXTRA       "-ioctl (2010-10-14)"
+
+/* Status bits */
+#define DM_READONLY_FLAG       (1 << 0) /* In/Out */
+#define DM_SUSPEND_FLAG                (1 << 1) /* In/Out */
+#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */
+
+/*
+ * Flag passed into ioctl STATUS command to get table information
+ * rather than current status.
+ */
+#define DM_STATUS_TABLE_FLAG   (1 << 4) /* In */
+
+/*
+ * Flags that indicate whether a table is present in either of
+ * the two table slots that a device has.
+ */
+#define DM_ACTIVE_PRESENT_FLAG   (1 << 5) /* Out */
+#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */
+
+/*
+ * Indicates that the buffer passed in wasn't big enough for the
+ * results.
+ */
+#define DM_BUFFER_FULL_FLAG    (1 << 8) /* Out */
+
+/*
+ * This flag is now ignored.
+ */
+#define DM_SKIP_BDGET_FLAG     (1 << 9) /* In */
+
+/*
+ * Set this to avoid attempting to freeze any filesystem when suspending.
+ */
+#define DM_SKIP_LOCKFS_FLAG    (1 << 10) /* In */
+
+/*
+ * Set this to suspend without flushing queued ios.
+ */
+#define DM_NOFLUSH_FLAG                (1 << 11) /* In */
+
+/*
+ * If set, any table information returned will relate to the inactive
+ * table instead of the live one.  Always check DM_INACTIVE_PRESENT_FLAG
+ * is set before using the data returned.
+ */
+#define DM_QUERY_INACTIVE_TABLE_FLAG   (1 << 12) /* In */
+
+/*
+ * If set, a uevent was generated for which the caller may need to wait.
+ */
+#define DM_UEVENT_GENERATED_FLAG       (1 << 13) /* Out */
+
+/*
+ * If set, rename changes the uuid not the name.  Only permitted
+ * if no uuid was previously supplied: an existing uuid cannot be changed.
+ */
+#define DM_UUID_FLAG                   (1 << 14) /* In */
+
+#endif                         /* _LINUX_DM_IOCTL_H */
diff --git a/libdm/misc/dm-log-userspace.h b/libdm/misc/dm-log-userspace.h
new file mode 100644 (file)
index 0000000..7cacb18
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef __DM_LOG_USERSPACE_H__
+#define __DM_LOG_USERSPACE_H__
+
+#include "dm-ioctl.h" /* For DM_UUID_LEN */
+
+/*
+ * The device-mapper userspace log module consists of a kernel component and
+ * a user-space component.  The kernel component implements the API defined
+ * in dm-dirty-log.h.  Its purpose is simply to pass the parameters and
+ * return values of those API functions between kernel and user-space.
+ *
+ * Below are defined the 'request_types' - DM_ULOG_CTR, DM_ULOG_DTR, etc.
+ * These request types represent the different functions in the device-mapper
+ * dirty log API.  Each of these is described in more detail below.
+ *
+ * The user-space program must listen for requests from the kernel (representing
+ * the various API functions) and process them.
+ *
+ * User-space begins by setting up the communication link (error checking
+ * removed for clarity):
+ *     fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+ *     addr.nl_family = AF_NETLINK;
+ *     addr.nl_groups = CN_IDX_DM;
+ *     addr.nl_pid = 0;
+ *     r = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
+ *     opt = addr.nl_groups;
+ *     setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
+ *
+ * User-space will then wait to receive requests form the kernel, which it
+ * will process as described below.  The requests are received in the form,
+ * ((struct dm_ulog_request) + (additional data)).  Depending on the request
+ * type, there may or may not be 'additional data'.  In the descriptions below,
+ * you will see 'Payload-to-userspace' and 'Payload-to-kernel'.  The
+ * 'Payload-to-userspace' is what the kernel sends in 'additional data' as
+ * necessary parameters to complete the request.  The 'Payload-to-kernel' is
+ * the 'additional data' returned to the kernel that contains the necessary
+ * results of the request.  The 'data_size' field in the dm_ulog_request
+ * structure denotes the availability and amount of payload data.
+ */
+
+/*
+ * DM_ULOG_CTR corresponds to (found in dm-dirty-log.h):
+ * int (*ctr)(struct dm_dirty_log *log, struct dm_target *ti,
+ *           unsigned argc, char **argv);
+ *
+ * Payload-to-userspace:
+ *     A single string containing all the argv arguments separated by ' 's
+ * Payload-to-kernel:
+ *     None.  ('data_size' in the dm_ulog_request struct should be 0.)
+ *
+ * The UUID contained in the dm_ulog_request structure is the reference that
+ * will be used by all request types to a specific log.  The constructor must
+ * record this assotiation with instance created.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_CTR                    1
+
+/*
+ * DM_ULOG_DTR corresponds to (found in dm-dirty-log.h):
+ * void (*dtr)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     A single string containing all the argv arguments separated by ' 's
+ * Payload-to-kernel:
+ *     None.  ('data_size' in the dm_ulog_request struct should be 0.)
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being destroyed.  There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_DTR                    2
+
+/*
+ * DM_ULOG_PRESUSPEND corresponds to (found in dm-dirty-log.h):
+ * int (*presuspend)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     None.
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being presuspended.  There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_PRESUSPEND             3
+
+/*
+ * DM_ULOG_POSTSUSPEND corresponds to (found in dm-dirty-log.h):
+ * int (*postsuspend)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     None.
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being postsuspended.  There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_POSTSUSPEND            4
+
+/*
+ * DM_ULOG_RESUME corresponds to (found in dm-dirty-log.h):
+ * int (*resume)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     None.
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being resumed.  There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_RESUME                 5
+
+/*
+ * DM_ULOG_GET_REGION_SIZE corresponds to (found in dm-dirty-log.h):
+ * uint32_t (*get_region_size)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     uint64_t - contains the region size
+ *
+ * The region size is something that was determined at constructor time.
+ * It is returned in the payload area and 'data_size' is set to
+ * reflect this.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field appropriately.
+ */
+#define DM_ULOG_GET_REGION_SIZE        6
+
+/*
+ * DM_ULOG_IS_CLEAN corresponds to (found in dm-dirty-log.h):
+ * int (*is_clean)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ *     uint64_t - the region to get clean status on
+ * Payload-to-kernel:
+ *     int64_t  - 1 if clean, 0 otherwise
+ *
+ * Payload is sizeof(uint64_t) and contains the region for which the clean
+ * status is being made.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - filling the payload with 0 (not clean) or
+ * 1 (clean), setting 'data_size' and 'error' appropriately.
+ */
+#define DM_ULOG_IS_CLEAN               7
+
+/*
+ * DM_ULOG_IN_SYNC corresponds to (found in dm-dirty-log.h):
+ * int (*in_sync)(struct dm_dirty_log *log, region_t region,
+ *               int can_block);
+ *
+ * Payload-to-userspace:
+ *     uint64_t - the region to get sync status on
+ * Payload-to-kernel:
+ *     int64_t - 1 if in-sync, 0 otherwise
+ *
+ * Exactly the same as 'is_clean' above, except this time asking "has the
+ * region been recovered?" vs. "is the region not being modified?"
+ */
+#define DM_ULOG_IN_SYNC                8
+
+/*
+ * DM_ULOG_FLUSH corresponds to (found in dm-dirty-log.h):
+ * int (*flush)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     None.
+ *
+ * No incoming or outgoing payload.  Simply flush log state to disk.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_FLUSH                  9
+
+/*
+ * DM_ULOG_MARK_REGION corresponds to (found in dm-dirty-log.h):
+ * void (*mark_region)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ *     uint64_t [] - region(s) to mark
+ * Payload-to-kernel:
+ *     None.
+ *
+ * Incoming payload contains the one or more regions to mark dirty.
+ * The number of regions contained in the payload can be determined from
+ * 'data_size/sizeof(uint64_t)'.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_MARK_REGION           10
+
+/*
+ * DM_ULOG_CLEAR_REGION corresponds to (found in dm-dirty-log.h):
+ * void (*clear_region)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ *     uint64_t [] - region(s) to clear
+ * Payload-to-kernel:
+ *     None.
+ *
+ * Incoming payload contains the one or more regions to mark clean.
+ * The number of regions contained in the payload can be determined from
+ * 'data_size/sizeof(uint64_t)'.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_CLEAR_REGION          11
+
+/*
+ * DM_ULOG_GET_RESYNC_WORK corresponds to (found in dm-dirty-log.h):
+ * int (*get_resync_work)(struct dm_dirty_log *log, region_t *region);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     {
+ *             int64_t i; -- 1 if recovery necessary, 0 otherwise
+ *             uint64_t r; -- The region to recover if i=1
+ *     }
+ * 'data_size' should be set appropriately.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field appropriately.
+ */
+#define DM_ULOG_GET_RESYNC_WORK       12
+
+/*
+ * DM_ULOG_SET_REGION_SYNC corresponds to (found in dm-dirty-log.h):
+ * void (*set_region_sync)(struct dm_dirty_log *log,
+ *                        region_t region, int in_sync);
+ *
+ * Payload-to-userspace:
+ *     {
+ *             uint64_t - region to set sync state on
+ *             int64_t  - 0 if not-in-sync, 1 if in-sync
+ *     }
+ * Payload-to-kernel:
+ *     None.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_SET_REGION_SYNC       13
+
+/*
+ * DM_ULOG_GET_SYNC_COUNT corresponds to (found in dm-dirty-log.h):
+ * region_t (*get_sync_count)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     uint64_t - the number of in-sync regions
+ *
+ * No incoming payload.  Kernel-bound payload contains the number of
+ * regions that are in-sync (in a size_t).
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_GET_SYNC_COUNT        14
+
+/*
+ * DM_ULOG_STATUS_INFO corresponds to (found in dm-dirty-log.h):
+ * int (*status)(struct dm_dirty_log *log, STATUSTYPE_INFO,
+ *              char *result, unsigned maxlen);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     Character string containing STATUSTYPE_INFO
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_STATUS_INFO           15
+
+/*
+ * DM_ULOG_STATUS_TABLE corresponds to (found in dm-dirty-log.h):
+ * int (*status)(struct dm_dirty_log *log, STATUSTYPE_TABLE,
+ *              char *result, unsigned maxlen);
+ *
+ * Payload-to-userspace:
+ *     None.
+ * Payload-to-kernel:
+ *     Character string containing STATUSTYPE_TABLE
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_STATUS_TABLE          16
+
+/*
+ * DM_ULOG_IS_REMOTE_RECOVERING corresponds to (found in dm-dirty-log.h):
+ * int (*is_remote_recovering)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ *     uint64_t - region to determine recovery status on
+ * Payload-to-kernel:
+ *     {
+ *             int64_t is_recovering;  -- 0 if no, 1 if yes
+ *             uint64_t in_sync_hint;  -- lowest region still needing resync
+ *     }
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_IS_REMOTE_RECOVERING  17
+
+/*
+ * (DM_ULOG_REQUEST_MASK & request_type) to get the request type
+ *
+ * Payload-to-userspace:
+ *     A single string containing all the argv arguments separated by ' 's
+ * Payload-to-kernel:
+ *     None.  ('data_size' in the dm_ulog_request struct should be 0.)
+ *
+ * We are reserving 8 bits of the 32-bit 'request_type' field for the
+ * various request types above.  The remaining 24-bits are currently
+ * set to zero and are reserved for future use and compatibility concerns.
+ *
+ * User-space should always use DM_ULOG_REQUEST_TYPE to aquire the
+ * request type from the 'request_type' field to maintain forward compatibility.
+ */
+#define DM_ULOG_REQUEST_MASK 0xFF
+#define DM_ULOG_REQUEST_TYPE(request_type) \
+       (DM_ULOG_REQUEST_MASK & (request_type))
+
+struct dm_ulog_request {
+       /*
+        * The local unique identifier (luid) and the universally unique
+        * identifier (uuid) are used to tie a request to a specific
+        * mirror log.  A single machine log could probably make due with
+        * just the 'luid', but a cluster-aware log must use the 'uuid' and
+        * the 'luid'.  The uuid is what is required for node to node
+        * communication concerning a particular log, but the 'luid' helps
+        * differentiate between logs that are being swapped and have the
+        * same 'uuid'.  (Think "live" and "inactive" device-mapper tables.)
+        */
+       uint64_t luid;
+       char uuid[DM_UUID_LEN];
+       char padding[7];        /* Padding because DM_UUID_LEN = 129 */
+
+       int32_t error;          /* Used to report back processing errors */
+
+       uint32_t seq;           /* Sequence number for request */
+       uint32_t request_type;  /* DM_ULOG_* defined above */
+       uint32_t data_size;     /* How much data (not including this struct) */
+
+       char data[];
+};
+
+#endif /* __DM_LOG_USERSPACE_H__ */
diff --git a/libdm/misc/dm-logging.h b/libdm/misc/dm-logging.h
new file mode 100644 (file)
index 0000000..13ab804
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _DM_LOGGING_H
+#define _DM_LOGGING_H
+
+#include "libdevmapper.h"
+
+extern dm_log_fn dm_log;
+extern dm_log_with_errno_fn dm_log_with_errno;
+
+#define LOG_MESG(l, f, ln, e, x...) \
+       do { \
+               if (dm_log_is_non_default()) \
+                       dm_log(l, f, ln, ## x); \
+               else \
+                       dm_log_with_errno(l, f, ln, e, ## x); \
+       } while (0)
+
+#define LOG_LINE(l, x...) LOG_MESG(l, __FILE__, __LINE__, 0, ## x)
+#define LOG_LINE_WITH_ERRNO(l, e, x...) LOG_MESG(l, __FILE__, __LINE__, e, ## x)
+
+#include "log.h"
+
+#endif
diff --git a/libdm/misc/dmlib.h b/libdm/misc/dmlib.h
new file mode 100644 (file)
index 0000000..9293b7c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * This file must be included first by every device-mapper library source file.
+ */
+#ifndef _DM_LIB_H
+#define _DM_LIB_H
+
+#define DM
+
+#include "lib.h"
+
+#endif
diff --git a/libdm/misc/kdev_t.h b/libdm/misc/kdev_t.h
new file mode 100644 (file)
index 0000000..5fcda74
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LIBDM_KDEV_H
+#define _LIBDM_KDEV_H
+
+#define MAJOR(dev)      ((dev & 0xfff00) >> 8)
+#define MINOR(dev)      ((dev & 0xff) | ((dev >> 12) & 0xfff00))
+#define MKDEV(ma,mi)    ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
+
+#endif
diff --git a/libdm/mm/dbg_malloc.c b/libdm/mm/dbg_malloc.c
new file mode 100644 (file)
index 0000000..db7f5f3
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+char *dm_strdup_aux(const char *str, const char *file, int line)
+{
+       char *ret;
+
+       if (!str) {
+               log_error(INTERNAL_ERROR "dm_strdup called with NULL pointer");
+               return NULL;
+       }
+
+       if ((ret = dm_malloc_aux_debug(strlen(str) + 1, file, line)))
+               strcpy(ret, str);
+
+       return ret;
+}
+
+struct memblock {
+       struct memblock *prev, *next;   /* All allocated blocks are linked */
+       size_t length;          /* Size of the requested block */
+       int id;                 /* Index of the block */
+       const char *file;       /* File that allocated */
+       int line;               /* Line that allocated */
+       void *magic;            /* Address of this block */
+} __attribute__((aligned(8)));
+
+static struct {
+       unsigned block_serialno;/* Non-decreasing serialno of block */
+       unsigned blocks_allocated; /* Current number of blocks allocated */
+       unsigned blocks_max;    /* Max no of concurrently-allocated blocks */
+       unsigned int bytes, mbytes;
+
+} _mem_stats = {
+0, 0, 0, 0, 0};
+
+static struct memblock *_head = 0;
+static struct memblock *_tail = 0;
+
+void *dm_malloc_aux_debug(size_t s, const char *file, int line)
+{
+       struct memblock *nb;
+       size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
+
+       if (s > 50000000) {
+               log_error("Huge memory allocation (size %" PRIsize_t
+                         ") rejected - metadata corruption?", s);
+               return 0;
+       }
+
+       if (!(nb = malloc(tsize))) {
+               log_error("couldn't allocate any memory, size = %" PRIsize_t,
+                         s);
+               return 0;
+       }
+
+       /* set up the file and line info */
+       nb->file = file;
+       nb->line = line;
+
+       dm_bounds_check();
+
+       /* setup fields */
+       nb->magic = nb + 1;
+       nb->length = s;
+       nb->id = ++_mem_stats.block_serialno;
+       nb->next = 0;
+
+       /* stomp a pretty pattern across the new memory
+          and fill in the boundary bytes */
+       {
+               char *ptr = (char *) (nb + 1);
+               size_t i;
+               for (i = 0; i < s; i++)
+                       *ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
+
+               for (i = 0; i < sizeof(unsigned long); i++)
+                       *ptr++ = (char) nb->id;
+       }
+
+       nb->prev = _tail;
+
+       /* link to tail of the list */
+       if (!_head)
+               _head = _tail = nb;
+       else {
+               _tail->next = nb;
+               _tail = nb;
+       }
+
+       _mem_stats.blocks_allocated++;
+       if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)
+               _mem_stats.blocks_max = _mem_stats.blocks_allocated;
+
+       _mem_stats.bytes += s;
+       if (_mem_stats.bytes > _mem_stats.mbytes)
+               _mem_stats.mbytes = _mem_stats.bytes;
+
+       /* log_debug("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
+                 _mem_stats.bytes); */
+
+       return nb + 1;
+}
+
+void *dm_zalloc_aux_debug(size_t s, const char *file, int line)
+{
+       void *ptr = dm_malloc_aux_debug(s, file, line);
+
+       if (ptr)
+               memset(ptr, 0, s);
+
+       return ptr;
+}
+
+void dm_free_aux(void *p)
+{
+       char *ptr;
+       size_t i;
+       struct memblock *mb = ((struct memblock *) p) - 1;
+       if (!p)
+               return;
+
+       dm_bounds_check();
+
+       /* sanity check */
+       assert(mb->magic == p);
+
+       /* check data at the far boundary */
+       ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
+       for (i = 0; i < sizeof(unsigned long); i++)
+               if (*ptr++ != (char) mb->id)
+                       assert(!"Damage at far end of block");
+
+       /* have we freed this before ? */
+       assert(mb->id != 0);
+
+       /* unlink */
+       if (mb->prev)
+               mb->prev->next = mb->next;
+       else
+               _head = mb->next;
+
+       if (mb->next)
+               mb->next->prev = mb->prev;
+       else
+               _tail = mb->prev;
+
+       mb->id = 0;
+
+       /* stomp a different pattern across the memory */
+       ptr = ((char *) mb) + sizeof(struct memblock);
+       for (i = 0; i < mb->length; i++)
+               *ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
+
+       assert(_mem_stats.blocks_allocated);
+       _mem_stats.blocks_allocated--;
+       _mem_stats.bytes -= mb->length;
+
+       /* free the memory */
+       free(mb);
+}
+
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
+{
+       void *r;
+       struct memblock *mb = ((struct memblock *) p) - 1;
+
+       r = dm_malloc_aux_debug(s, file, line);
+
+       if (p) {
+               memcpy(r, p, mb->length);
+               dm_free_aux(p);
+       }
+
+       return r;
+}
+
+int dm_dump_memory_debug(void)
+{
+       unsigned long tot = 0;
+       struct memblock *mb;
+       char str[32];
+       size_t c;
+
+       if (_head)
+               log_very_verbose("You have a memory leak:");
+
+       for (mb = _head; mb; mb = mb->next) {
+#ifdef VALGRIND_POOL
+               /*
+                * We can't look at the memory in case it has had
+                * VALGRIND_MAKE_MEM_NOACCESS called on it.
+                */
+               str[0] = '\0';
+#else
+               for (c = 0; c < sizeof(str) - 1; c++) {
+                       if (c >= mb->length)
+                               str[c] = ' ';
+                       else if (((char *)mb->magic)[c] == '\0')
+                               str[c] = '\0';
+                       else if (((char *)mb->magic)[c] < ' ')
+                               str[c] = '?';
+                       else
+                               str[c] = ((char *)mb->magic)[c];
+               }
+               str[sizeof(str) - 1] = '\0';
+#endif
+
+               LOG_MESG(_LOG_INFO, mb->file, mb->line, 0,
+                        "block %d at %p, size %" PRIsize_t "\t [%s]",
+                        mb->id, mb->magic, mb->length, str);
+               tot += mb->length;
+       }
+
+       if (_head)
+               log_very_verbose("%ld bytes leaked in total", tot);
+
+       return 1;
+}
+
+void dm_bounds_check_debug(void)
+{
+       struct memblock *mb = _head;
+       while (mb) {
+               size_t i;
+               char *ptr = ((char *) (mb + 1)) + mb->length;
+               for (i = 0; i < sizeof(unsigned long); i++)
+                       if (*ptr++ != (char) mb->id)
+                               assert(!"Memory smash");
+
+               mb = mb->next;
+       }
+}
+
+void *dm_malloc_aux(size_t s, const char *file __attribute__((unused)),
+                   int line __attribute__((unused)))
+{
+       if (s > 50000000) {
+               log_error("Huge memory allocation (size %" PRIsize_t
+                         ") rejected - metadata corruption?", s);
+               return 0;
+       }
+
+       return malloc(s);
+}
+
+void *dm_zalloc_aux(size_t s, const char *file, int line)
+{
+       void *ptr = dm_malloc_aux(s, file, line);
+
+       if (ptr)
+               memset(ptr, 0, s);
+
+       return ptr;
+}
diff --git a/libdm/mm/dbg_malloc.h b/libdm/mm/dbg_malloc.h
new file mode 100644 (file)
index 0000000..6de1020
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _DM_DBG_MALLOC_H
+#define _DM_DBG_MALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+void *malloc_aux(size_t s, const char *file, int line);
+#define dm_malloc(s) malloc_aux((s), __FILE__, __LINE__)
+
+char *dbg_strdup(const char *str);
+
+#ifdef DEBUG_MEM
+
+void free_aux(void *p);
+void *realloc_aux(void *p, unsigned int s, const char *file, int line);
+int dump_memory(void);
+void bounds_check(void);
+
+#  define dm_free(p) free_aux(p)
+#  define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__)
+
+#else
+
+#  define dm_free(p) do {if (p) free(p); } while (0)
+#  define dbg_realloc(p, s) realloc(p, s)
+#  define dump_memory()
+#  define bounds_check()
+
+#endif
+
+#endif
diff --git a/libdm/mm/pool-debug.c b/libdm/mm/pool-debug.c
new file mode 100644 (file)
index 0000000..7fde3a6
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include <assert.h>
+
+struct block {
+       struct block *next;
+       size_t size;
+       void *data;
+};
+
+typedef struct {
+       unsigned block_serialno;        /* Non-decreasing serialno of block */
+       unsigned blocks_allocated;      /* Current number of blocks allocated */
+       unsigned blocks_max;    /* Max no of concurrently-allocated blocks */
+       unsigned int bytes, maxbytes;
+} pool_stats;
+
+struct dm_pool {
+       struct dm_list list;
+       const char *name;
+       void *orig_pool;        /* to pair it with first allocation call */
+
+       int begun;
+       struct block *object;
+
+       struct block *blocks;
+       struct block *tail;
+
+       pool_stats stats;
+};
+
+/* by default things come out aligned for doubles */
+#define DEFAULT_ALIGNMENT __alignof__ (double)
+
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+{
+       struct dm_pool *mem = dm_malloc(sizeof(*mem));
+
+       if (!mem) {
+               log_error("Couldn't create memory pool %s (size %"
+                         PRIsize_t ")", name, sizeof(*mem));
+               return NULL;
+       }
+
+       mem->name = name;
+       mem->begun = 0;
+       mem->object = 0;
+       mem->blocks = mem->tail = NULL;
+
+       mem->stats.block_serialno = 0;
+       mem->stats.blocks_allocated = 0;
+       mem->stats.blocks_max = 0;
+       mem->stats.bytes = 0;
+       mem->stats.maxbytes = 0;
+
+       mem->orig_pool = mem;
+
+#ifdef DEBUG_POOL
+       log_debug("Created mempool %s", name);
+#endif
+
+       dm_list_add(&_dm_pools, &mem->list);
+       return mem;
+}
+
+static void _free_blocks(struct dm_pool *p, struct block *b)
+{
+       struct block *n;
+
+       while (b) {
+               p->stats.bytes -= b->size;
+               p->stats.blocks_allocated--;
+
+               n = b->next;
+               dm_free(b->data);
+               dm_free(b);
+               b = n;
+       }
+}
+
+static void _pool_stats(struct dm_pool *p, const char *action)
+{
+#ifdef DEBUG_POOL
+       log_debug("%s mempool %s: %u/%u bytes, %u/%u blocks, "
+                 "%u allocations)", action, p->name, p->stats.bytes,
+                 p->stats.maxbytes, p->stats.blocks_allocated,
+                 p->stats.blocks_max, p->stats.block_serialno);
+#else
+       ;
+#endif
+}
+
+void dm_pool_destroy(struct dm_pool *p)
+{
+       _pool_stats(p, "Destroying");
+       _free_blocks(p, p->blocks);
+       dm_list_del(&p->list);
+       dm_free(p);
+}
+
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+{
+       return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
+}
+
+static void _append_block(struct dm_pool *p, struct block *b)
+{
+       if (p->tail) {
+               p->tail->next = b;
+               p->tail = b;
+       } else
+               p->blocks = p->tail = b;
+
+       p->stats.block_serialno++;
+       p->stats.blocks_allocated++;
+       if (p->stats.blocks_allocated > p->stats.blocks_max)
+               p->stats.blocks_max = p->stats.blocks_allocated;
+
+       p->stats.bytes += b->size;
+       if (p->stats.bytes > p->stats.maxbytes)
+               p->stats.maxbytes = p->stats.bytes;
+}
+
+static struct block *_new_block(size_t s, unsigned alignment)
+{
+       /* FIXME: I'm currently ignoring the alignment arg. */
+       size_t len = sizeof(struct block) + s;
+       struct block *b = dm_malloc(len);
+
+       /*
+        * Too lazy to implement alignment for debug version, and
+        * I don't think LVM will use anything but default
+        * align.
+        */
+       assert(alignment == DEFAULT_ALIGNMENT);
+
+       if (!b) {
+               log_error("Out of memory");
+               return NULL;
+       }
+
+       if (!(b->data = dm_malloc(s))) {
+               log_error("Out of memory");
+               dm_free(b);
+               return NULL;
+       }
+
+       b->next = NULL;
+       b->size = s;
+
+       return b;
+}
+
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+{
+       struct block *b = _new_block(s, alignment);
+
+       if (!b)
+               return NULL;
+
+       _append_block(p, b);
+
+       return b->data;
+}
+
+void dm_pool_empty(struct dm_pool *p)
+{
+       _pool_stats(p, "Emptying");
+       _free_blocks(p, p->blocks);
+       p->blocks = p->tail = NULL;
+}
+
+void dm_pool_free(struct dm_pool *p, void *ptr)
+{
+       struct block *b, *prev = NULL;
+
+       _pool_stats(p, "Freeing (before)");
+
+       for (b = p->blocks; b; b = b->next) {
+               if (b->data == ptr)
+                       break;
+               prev = b;
+       }
+
+       /*
+        * If this fires then you tried to free a
+        * pointer that either wasn't from this
+        * pool, or isn't the start of a block.
+        */
+       assert(b);
+
+       _free_blocks(p, b);
+
+       if (prev) {
+               p->tail = prev;
+               prev->next = NULL;
+       } else
+               p->blocks = p->tail = NULL;
+
+       _pool_stats(p, "Freeing (after)");
+}
+
+int dm_pool_begin_object(struct dm_pool *p, size_t init_size)
+{
+       assert(!p->begun);
+       p->begun = 1;
+       return 1;
+}
+
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
+{
+       struct block *new;
+       size_t new_size;
+
+       if (!delta)
+               delta = strlen(extra);
+
+       assert(p->begun);
+
+       if (p->object)
+               new_size = delta + p->object->size;
+       else
+               new_size = delta;
+
+       if (!(new = _new_block(new_size, DEFAULT_ALIGNMENT))) {
+               log_error("Couldn't extend object.");
+               return 0;
+       }
+
+       if (p->object) {
+               memcpy(new->data, p->object->data, p->object->size);
+               dm_free(p->object->data);
+               dm_free(p->object);
+       }
+       p->object = new;
+
+       memcpy(new->data + new_size - delta, extra, delta);
+
+       return 1;
+}
+
+void *dm_pool_end_object(struct dm_pool *p)
+{
+       assert(p->begun);
+       _append_block(p, p->object);
+
+       p->begun = 0;
+       p->object = NULL;
+       return p->tail->data;
+}
+
+void dm_pool_abandon_object(struct dm_pool *p)
+{
+       assert(p->begun);
+       dm_free(p->object);
+       p->begun = 0;
+       p->object = NULL;
+}
diff --git a/libdm/mm/pool-fast.c b/libdm/mm/pool-fast.c
new file mode 100644 (file)
index 0000000..ebd982e
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifdef VALGRIND_POOL
+#include "valgrind/memcheck.h"
+#endif
+
+#include "dmlib.h"
+
+struct chunk {
+       char *begin, *end;
+       struct chunk *prev;
+};
+
+struct dm_pool {
+       struct dm_list list;
+       struct chunk *chunk, *spare_chunk;      /* spare_chunk is a one entry free
+                                                  list to stop 'bobbling' */
+       size_t chunk_size;
+       size_t object_len;
+       unsigned object_alignment;
+};
+
+static void _align_chunk(struct chunk *c, unsigned alignment);
+static struct chunk *_new_chunk(struct dm_pool *p, size_t s);
+static void _free_chunk(struct chunk *c);
+
+/* by default things come out aligned for doubles */
+#define DEFAULT_ALIGNMENT __alignof__ (double)
+
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+{
+       size_t new_size = 1024;
+       struct dm_pool *p = dm_zalloc(sizeof(*p));
+
+       if (!p) {
+               log_error("Couldn't create memory pool %s (size %"
+                         PRIsize_t ")", name, sizeof(*p));
+               return 0;
+       }
+
+       /* round chunk_hint up to the next power of 2 */
+       p->chunk_size = chunk_hint + sizeof(struct chunk);
+       while (new_size < p->chunk_size)
+               new_size <<= 1;
+       p->chunk_size = new_size;
+       dm_list_add(&_dm_pools, &p->list);
+       return p;
+}
+
+void dm_pool_destroy(struct dm_pool *p)
+{
+       struct chunk *c, *pr;
+       _free_chunk(p->spare_chunk);
+       c = p->chunk;
+       while (c) {
+               pr = c->prev;
+               _free_chunk(c);
+               c = pr;
+       }
+
+       dm_list_del(&p->list);
+       dm_free(p);
+}
+
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+{
+       return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
+}
+
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+{
+       struct chunk *c = p->chunk;
+       void *r;
+
+       /* realign begin */
+       if (c)
+               _align_chunk(c, alignment);
+
+       /* have we got room ? */
+       if (!c || (c->begin > c->end) || (c->end - c->begin < s)) {
+               /* allocate new chunk */
+               size_t needed = s + alignment + sizeof(struct chunk);
+               c = _new_chunk(p, (needed > p->chunk_size) ?
+                              needed : p->chunk_size);
+
+               if (!c)
+                       return NULL;
+
+               _align_chunk(c, alignment);
+       }
+
+       r = c->begin;
+       c->begin += s;
+
+#ifdef VALGRIND_POOL
+       VALGRIND_MAKE_MEM_UNDEFINED(r, s);
+#endif
+
+       return r;
+}
+
+void dm_pool_empty(struct dm_pool *p)
+{
+       struct chunk *c;
+
+       for (c = p->chunk; c && c->prev; c = c->prev)
+               ;
+
+       if (c)
+               dm_pool_free(p, (char *) (c + 1));
+}
+
+void dm_pool_free(struct dm_pool *p, void *ptr)
+{
+       struct chunk *c = p->chunk;
+
+       while (c) {
+               if (((char *) c < (char *) ptr) &&
+                   ((char *) c->end > (char *) ptr)) {
+                       c->begin = ptr;
+#ifdef VALGRIND_POOL
+                       VALGRIND_MAKE_MEM_NOACCESS(c->begin, c->end - c->begin);
+#endif
+                       break;
+               }
+
+               if (p->spare_chunk)
+                       _free_chunk(p->spare_chunk);
+
+               c->begin = (char *) (c + 1);
+#ifdef VALGRIND_POOL
+                VALGRIND_MAKE_MEM_NOACCESS(c->begin, c->end - c->begin);
+#endif
+
+               p->spare_chunk = c;
+               c = c->prev;
+       }
+
+       if (!c)
+               log_error(INTERNAL_ERROR "pool_free asked to free pointer "
+                         "not in pool");
+       else
+               p->chunk = c;
+}
+
+int dm_pool_begin_object(struct dm_pool *p, size_t hint)
+{
+       struct chunk *c = p->chunk;
+       const size_t align = DEFAULT_ALIGNMENT;
+
+       p->object_len = 0;
+       p->object_alignment = align;
+
+       if (c)
+               _align_chunk(c, align);
+
+       if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
+               /* allocate a new chunk */
+               c = _new_chunk(p,
+                              hint > (p->chunk_size - sizeof(struct chunk)) ?
+                              hint + sizeof(struct chunk) + align :
+                              p->chunk_size);
+
+               if (!c)
+                       return 0;
+
+               _align_chunk(c, align);
+       }
+
+       return 1;
+}
+
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
+{
+       struct chunk *c = p->chunk, *nc;
+
+       if (!delta)
+               delta = strlen(extra);
+
+       if (c->end - (c->begin + p->object_len) < delta) {
+               /* move into a new chunk */
+               if (p->object_len + delta > (p->chunk_size / 2))
+                       nc = _new_chunk(p, (p->object_len + delta) * 2);
+               else
+                       nc = _new_chunk(p, p->chunk_size);
+
+               if (!nc)
+                       return 0;
+
+               _align_chunk(p->chunk, p->object_alignment);
+
+#ifdef VALGRIND_POOL
+               VALGRIND_MAKE_MEM_UNDEFINED(p->chunk->begin, p->object_len);
+#endif
+
+               memcpy(p->chunk->begin, c->begin, p->object_len);
+
+#ifdef VALGRIND_POOL
+               VALGRIND_MAKE_MEM_NOACCESS(c->begin, p->object_len);
+#endif
+
+               c = p->chunk;
+       }
+
+#ifdef VALGRIND_POOL
+       VALGRIND_MAKE_MEM_UNDEFINED(p->chunk->begin + p->object_len, delta);
+#endif
+
+       memcpy(c->begin + p->object_len, extra, delta);
+       p->object_len += delta;
+       return 1;
+}
+
+void *dm_pool_end_object(struct dm_pool *p)
+{
+       struct chunk *c = p->chunk;
+       void *r = c->begin;
+       c->begin += p->object_len;
+       p->object_len = 0u;
+       p->object_alignment = DEFAULT_ALIGNMENT;
+       return r;
+}
+
+void dm_pool_abandon_object(struct dm_pool *p)
+{
+       p->object_len = 0;
+       p->object_alignment = DEFAULT_ALIGNMENT;
+}
+
+static void _align_chunk(struct chunk *c, unsigned alignment)
+{
+       c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
+}
+
+static struct chunk *_new_chunk(struct dm_pool *p, size_t s)
+{
+       struct chunk *c;
+
+       if (p->spare_chunk &&
+           ((p->spare_chunk->end - p->spare_chunk->begin) >= s)) {
+               /* reuse old chunk */
+               c = p->spare_chunk;
+               p->spare_chunk = 0;
+       } else {
+               if (!(c = dm_malloc(s))) {
+                       log_error("Out of memory.  Requested %" PRIsize_t
+                                 " bytes.", s);
+                       return NULL;
+               }
+
+               c->begin = (char *) (c + 1);
+               c->end = (char *) c + s;
+
+#ifdef VALGRIND_POOL
+               VALGRIND_MAKE_MEM_NOACCESS(c->begin, c->end - c->begin);
+#endif
+       }
+
+       c->prev = p->chunk;
+       p->chunk = c;
+       return c;
+}
+
+static void _free_chunk(struct chunk *c)
+{
+       if (c) {
+#ifdef VALGRIND_POOL
+               VALGRIND_MAKE_MEM_UNDEFINED(c, c->end - (char *) c);
+#endif
+
+               dm_free(c);
+       }
+}
diff --git a/libdm/mm/pool.c b/libdm/mm/pool.c
new file mode 100644 (file)
index 0000000..35bfffa
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+
+/* FIXME: thread unsafe */
+static DM_LIST_INIT(_dm_pools);
+void dm_pools_check_leaks(void);
+
+#ifdef DEBUG_POOL
+#include "pool-debug.c"
+#else
+#include "pool-fast.c"
+#endif
+
+char *dm_pool_strdup(struct dm_pool *p, const char *str)
+{
+       char *ret = dm_pool_alloc(p, strlen(str) + 1);
+
+       if (ret)
+               strcpy(ret, str);
+
+       return ret;
+}
+
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
+{
+       char *ret = dm_pool_alloc(p, n + 1);
+
+       if (ret) {
+               strncpy(ret, str, n);
+               ret[n] = '\0';
+       }
+
+       return ret;
+}
+
+void *dm_pool_zalloc(struct dm_pool *p, size_t s)
+{
+       void *ptr = dm_pool_alloc(p, s);
+
+       if (ptr)
+               memset(ptr, 0, s);
+
+       return ptr;
+}
+
+void dm_pools_check_leaks(void)
+{
+       struct dm_pool *p;
+
+       if (dm_list_empty(&_dm_pools))
+               return;
+
+       log_error("You have a memory leak (not released memory pool):");
+       dm_list_iterate_items(p, &_dm_pools) {
+#ifdef DEBUG_POOL
+               log_error(" [%p] %s (%u bytes)",
+                         p->orig_pool,
+                         p->name, p->stats.bytes);
+#else
+               log_error(" [%p]", p);
+#endif
+       }
+}
diff --git a/libdm/regex/matcher.c b/libdm/regex/matcher.c
new file mode 100644 (file)
index 0000000..9590865
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "parse_rx.h"
+#include "ttree.h"
+#include "assert.h"
+
+struct dfa_state {
+       struct dfa_state *next;
+       int final;
+       dm_bitset_t bits;
+       struct dfa_state *lookup[256];
+};
+
+struct dm_regex {              /* Instance variables for the lexer */
+       struct dfa_state *start;
+       unsigned num_nodes;
+        unsigned num_charsets;
+       int nodes_entered;
+       struct rx_node **nodes;
+        int charsets_entered;
+        struct rx_node **charsets;
+       struct dm_pool *scratch, *mem;
+
+        /* stuff for on the fly dfa calculation */
+        dm_bitset_t charmap[256];
+        dm_bitset_t dfa_copy;
+        struct ttree *tt;
+        dm_bitset_t bs;
+        struct dfa_state *h, *t;
+};
+
+static int _count_nodes(struct rx_node *rx)
+{
+       int r = 1;
+
+       if (rx->left)
+               r += _count_nodes(rx->left);
+
+       if (rx->right)
+               r += _count_nodes(rx->right);
+
+       return r;
+}
+
+static unsigned _count_charsets(struct rx_node *rx)
+{
+        if (rx->type == CHARSET)
+                return 1;
+
+        return (rx->left ? _count_charsets(rx->left) : 0) +
+                (rx->right ? _count_charsets(rx->right) : 0);
+}
+
+static void _enumerate_charsets_internal(struct rx_node *rx, unsigned *i)
+{
+        if (rx->type == CHARSET)
+                rx->charset_index = (*i)++;
+        else {
+                if (rx->left)
+                        _enumerate_charsets_internal(rx->left, i);
+                if (rx->right)
+                        _enumerate_charsets_internal(rx->right, i);
+        }
+}
+
+static void _enumerate_charsets(struct rx_node *rx)
+{
+        unsigned i = 0;
+        _enumerate_charsets_internal(rx, &i);
+}
+
+static void _fill_table(struct dm_regex *m, struct rx_node *rx)
+{
+       assert((rx->type != OR) || (rx->left && rx->right));
+
+       if (rx->left)
+               _fill_table(m, rx->left);
+
+       if (rx->right)
+               _fill_table(m, rx->right);
+
+       m->nodes[m->nodes_entered++] = rx;
+        if (rx->type == CHARSET)
+                m->charsets[m->charsets_entered++] = rx;
+}
+
+static void _create_bitsets(struct dm_regex *m)
+{
+       int i;
+
+       for (i = 0; i < m->num_nodes; i++) {
+               struct rx_node *n = m->nodes[i];
+               n->firstpos = dm_bitset_create(m->scratch, m->num_charsets);
+               n->lastpos = dm_bitset_create(m->scratch, m->num_charsets);
+               n->followpos = dm_bitset_create(m->scratch, m->num_charsets);
+       }
+}
+
+static void _calc_functions(struct dm_regex *m)
+{
+       int i, j, final = 1;
+       struct rx_node *rx, *c1, *c2;
+
+       for (i = 0; i < m->num_nodes; i++) {
+               rx = m->nodes[i];
+               c1 = rx->left;
+               c2 = rx->right;
+
+               if (rx->type == CHARSET && dm_bit(rx->charset, TARGET_TRANS))
+                       rx->final = final++;
+
+               switch (rx->type) {
+               case CAT:
+                       if (c1->nullable)
+                               dm_bit_union(rx->firstpos,
+                                         c1->firstpos, c2->firstpos);
+                       else
+                               dm_bit_copy(rx->firstpos, c1->firstpos);
+
+                       if (c2->nullable)
+                               dm_bit_union(rx->lastpos,
+                                         c1->lastpos, c2->lastpos);
+                       else
+                               dm_bit_copy(rx->lastpos, c2->lastpos);
+
+                       rx->nullable = c1->nullable && c2->nullable;
+                       break;
+
+               case PLUS:
+                       dm_bit_copy(rx->firstpos, c1->firstpos);
+                       dm_bit_copy(rx->lastpos, c1->lastpos);
+                       rx->nullable = c1->nullable;
+                       break;
+
+               case OR:
+                       dm_bit_union(rx->firstpos, c1->firstpos, c2->firstpos);
+                       dm_bit_union(rx->lastpos, c1->lastpos, c2->lastpos);
+                       rx->nullable = c1->nullable || c2->nullable;
+                       break;
+
+               case QUEST:
+               case STAR:
+                       dm_bit_copy(rx->firstpos, c1->firstpos);
+                       dm_bit_copy(rx->lastpos, c1->lastpos);
+                       rx->nullable = 1;
+                       break;
+
+               case CHARSET:
+                       dm_bit_set(rx->firstpos, rx->charset_index);
+                       dm_bit_set(rx->lastpos, rx->charset_index);
+                       rx->nullable = 0;
+                       break;
+
+               default:
+                       log_error(INTERNAL_ERROR "Unknown calc node type");
+               }
+
+               /*
+                * followpos has it's own switch
+                * because PLUS and STAR do the
+                * same thing.
+                */
+               switch (rx->type) {
+               case CAT:
+                       for (j = 0; j < m->num_charsets; j++) {
+                                struct rx_node *n = m->charsets[j];
+                               if (dm_bit(c1->lastpos, j))
+                                       dm_bit_union(n->followpos,
+                                                     n->followpos, c2->firstpos);
+                       }
+                       break;
+
+               case PLUS:
+               case STAR:
+                       for (j = 0; j < m->num_charsets; j++) {
+                                struct rx_node *n = m->charsets[j];
+                               if (dm_bit(rx->lastpos, j))
+                                       dm_bit_union(n->followpos,
+                                                     n->followpos, rx->firstpos);
+                       }
+                       break;
+               }
+       }
+}
+
+static struct dfa_state *_create_dfa_state(struct dm_pool *mem)
+{
+       return dm_pool_zalloc(mem, sizeof(struct dfa_state));
+}
+
+static struct dfa_state *_create_state_queue(struct dm_pool *mem,
+                                             struct dfa_state *dfa,
+                                             dm_bitset_t bits)
+{
+       dfa->bits = dm_bitset_create(mem, bits[0]);     /* first element is the size */
+       dm_bit_copy(dfa->bits, bits);
+       dfa->next = 0;
+        dfa->final = -1;
+       return dfa;
+}
+
+static void _calc_state(struct dm_regex *m, struct dfa_state *dfa, int a)
+{
+        int set_bits = 0, i;
+        dm_bitset_t dfa_bits = dfa->bits;
+        dm_bit_and(m->dfa_copy, m->charmap[a], dfa_bits);
+
+        /* iterate through all the states in firstpos */
+        for (i = dm_bit_get_first(m->dfa_copy); i >= 0; i = dm_bit_get_next(m->dfa_copy, i)) {
+                if (a == TARGET_TRANS)
+                        dfa->final = m->charsets[i]->final;
+
+                dm_bit_union(m->bs, m->bs, m->charsets[i]->followpos);
+                set_bits = 1;
+        }
+
+        if (set_bits) {
+                struct dfa_state *tmp;
+                struct dfa_state *ldfa = ttree_lookup(m->tt, m->bs + 1);
+                if (!ldfa) {
+                        /* push */
+                        ldfa = _create_dfa_state(m->mem);
+                        ttree_insert(m->tt, m->bs + 1, ldfa);
+                        tmp = _create_state_queue(m->scratch, ldfa, m->bs);
+                        if (!m->h)
+                                m->h = m->t = tmp;
+                        else {
+                                m->t->next = tmp;
+                                m->t = tmp;
+                        }
+                }
+
+                dfa->lookup[a] = ldfa;
+                dm_bit_clear_all(m->bs);
+        }
+}
+
+static int _calc_states(struct dm_regex *m, struct rx_node *rx)
+{
+       unsigned iwidth = (m->num_charsets / DM_BITS_PER_INT) + 1;
+       struct dfa_state *dfa;
+       int i, a;
+
+       m->tt = ttree_create(m->scratch, iwidth);
+       if (!m->tt)
+               return_0;
+
+       if (!(m->bs = dm_bitset_create(m->scratch, m->num_charsets)))
+               return_0;
+
+        /* build some char maps */
+        for (a = 0; a < 256; a++) {
+                m->charmap[a] = dm_bitset_create(m->scratch, m->num_charsets);
+                if (!m->charmap[a])
+                        return_0;
+        }
+
+        for (i = 0; i < m->num_nodes; i++) {
+                struct rx_node *n = m->nodes[i];
+                if (n->type == CHARSET) {
+                        for (a = dm_bit_get_first(n->charset);
+                             a >= 0; a = dm_bit_get_next(n->charset, a))
+                                dm_bit_set(m->charmap[a], n->charset_index);
+                }
+        }
+
+       /* create first state */
+       dfa = _create_dfa_state(m->mem);
+       m->start = dfa;
+       ttree_insert(m->tt, rx->firstpos + 1, dfa);
+
+       /* prime the queue */
+       m->h = m->t = _create_state_queue(m->scratch, dfa, rx->firstpos);
+        m->dfa_copy = dm_bitset_create(m->scratch, m->num_charsets);
+       return 1;
+}
+
+/*
+ * Forces all the dfa states to be calculated up front, ie. what
+ * _calc_states() used to do before we switched to calculating on demand.
+ */
+static void _force_states(struct dm_regex *m)
+{
+        int a;
+
+        /* keep processing until there's nothing in the queue */
+        struct dfa_state *s;
+        while ((s = m->h)) {
+                /* pop state off front of the queue */
+                m->h = m->h->next;
+
+                /* iterate through all the inputs for this state */
+                dm_bit_clear_all(m->bs);
+                for (a = 0; a < 256; a++)
+                        _calc_state(m, s, a);
+        }
+}
+
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
+                                unsigned num_patterns)
+{
+       char *all, *ptr;
+       int i;
+       size_t len = 0;
+       struct rx_node *rx;
+       struct dm_regex *m;
+       struct dm_pool *scratch = mem;
+
+       if (!(m = dm_pool_zalloc(mem, sizeof(*m))))
+               return_NULL;
+
+       /* join the regexps together, delimiting with zero */
+       for (i = 0; i < num_patterns; i++)
+               len += strlen(patterns[i]) + 8;
+
+       ptr = all = dm_pool_alloc(scratch, len + 1);
+
+       if (!all)
+               goto_bad;
+
+       for (i = 0; i < num_patterns; i++) {
+               ptr += sprintf(ptr, "(.*(%s)%c)", patterns[i], TARGET_TRANS);
+               if (i < (num_patterns - 1))
+                       *ptr++ = '|';
+       }
+
+       /* parse this expression */
+       if (!(rx = rx_parse_tok(scratch, all, ptr))) {
+               log_error("Couldn't parse regex");
+               goto bad;
+       }
+
+       m->mem = mem;
+       m->scratch = scratch;
+       m->num_nodes = _count_nodes(rx);
+        m->num_charsets = _count_charsets(rx);
+        _enumerate_charsets(rx);
+       m->nodes = dm_pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes);
+       if (!m->nodes)
+               goto_bad;
+
+        m->charsets = dm_pool_alloc(scratch, sizeof(*m->charsets) * m->num_charsets);
+        if (!m->charsets)
+                goto_bad;
+
+       _fill_table(m, rx);
+       _create_bitsets(m);
+       _calc_functions(m);
+       _calc_states(m, rx);
+       return m;
+
+      bad:
+       dm_pool_free(mem, m);
+       return NULL;
+}
+
+static struct dfa_state *_step_matcher(struct dm_regex *m, int c, struct dfa_state *cs, int *r)
+{
+        struct dfa_state *ns;
+
+       if (!(ns = cs->lookup[(unsigned char) c])) {
+               _calc_state(m, cs, (unsigned char) c);
+               if (!(ns = cs->lookup[(unsigned char) c]))
+                       return NULL;
+       }
+
+        // yuck, we have to special case the target trans
+        if (ns->final == -1)
+                _calc_state(m, ns, TARGET_TRANS);
+
+       if (ns->final && (ns->final > *r))
+               *r = ns->final;
+
+       return ns;
+}
+
+int dm_regex_match(struct dm_regex *regex, const char *s)
+{
+       struct dfa_state *cs = regex->start;
+       int r = 0;
+
+        dm_bit_clear_all(regex->bs);
+       if (!(cs = _step_matcher(regex, HAT_CHAR, cs, &r)))
+               goto out;
+
+       for (; *s; s++)
+               if (!(cs = _step_matcher(regex, *s, cs, &r)))
+                       goto out;
+
+       _step_matcher(regex, DOLLAR_CHAR, cs, &r);
+
+      out:
+       /* subtract 1 to get back to zero index */
+       return r - 1;
+}
+
+/*
+ * The next block of code concerns calculating a fingerprint for the dfa.
+ *
+ * We're not calculating a minimal dfa in _calculate_state (maybe a future
+ * improvement).  As such it's possible that two non-isomorphic dfas
+ * recognise the same language.  This can only really happen if you start
+ * with equivalent, but different regexes (for example the simplifier in
+ * parse_rx.c may have changed).
+ *
+ * The code is inefficient; repeatedly searching a singly linked list for
+ * previously seen nodes.  Not worried since this is test code.
+ */
+struct node_list {
+        unsigned node_id;
+        struct dfa_state *node;
+        struct node_list *next;
+};
+
+struct printer {
+        struct dm_pool *mem;
+        struct node_list *pending;
+        struct node_list *processed;
+        unsigned next_index;
+};
+
+static uint32_t randomise_(uint32_t n)
+{
+        /* 2^32 - 5 */
+        uint32_t const prime = (~0) - 4;
+        return n * prime;
+}
+
+static int seen_(struct node_list *n, struct dfa_state *node, uint32_t *i)
+{
+        while (n) {
+                if (n->node == node) {
+                        *i = n->node_id;
+                        return 1;
+                }
+                n = n->next;
+        }
+
+        return 0;
+}
+
+/*
+ * Push node if it's not been seen before, returning a unique index.
+ */
+static uint32_t push_node_(struct printer *p, struct dfa_state *node)
+{
+        uint32_t i;
+        if (seen_(p->pending, node, &i) ||
+            seen_(p->processed, node, &i))
+                return i;
+        else {
+                struct node_list *n = dm_pool_alloc(p->mem, sizeof(*n));
+                assert(n);
+                n->node_id = p->next_index++;
+                n->node = node;
+                n->next = p->pending;
+                p->pending = n;
+                return n->node_id;
+        }
+}
+
+/*
+ * Pop the front node, and fill out it's previously assigned index.
+ */
+static struct dfa_state *pop_node_(struct printer *p)
+{
+        struct dfa_state *node = NULL;
+
+        if (p->pending) {
+                struct node_list *n = p->pending;
+                p->pending = n->next;
+                n->next = p->processed;
+                p->processed = n;
+
+                node = n->node;
+        }
+
+        return node;
+}
+
+static uint32_t combine_(uint32_t n1, uint32_t n2)
+{
+        return ((n1 << 8) | (n1 >> 24)) ^ randomise_(n2);
+}
+
+static uint32_t fingerprint_(struct printer *p)
+{
+        int c;
+        uint32_t result = 0;
+        struct dfa_state *node;
+
+        while ((node = pop_node_(p))) {
+                result = combine_(result, node->final < 0 ? 0 : node->final);
+                for (c = 0; c < 256; c++)
+                        result = combine_(result,
+                                          push_node_(p, node->lookup[c]));
+        }
+
+        return result;
+}
+
+uint32_t dm_regex_fingerprint(struct dm_regex *regex)
+{
+        uint32_t result;
+        struct printer p;
+        struct dm_pool *mem = dm_pool_create("regex fingerprint", 1024);
+
+        _force_states(regex);
+
+        assert(mem);
+        p.mem = mem;
+        p.pending = NULL;
+        p.processed = NULL;
+        p.next_index = 0;
+
+        push_node_(&p, regex->start);
+        result = fingerprint_(&p);
+        dm_pool_destroy(mem);
+        return result;
+}
diff --git a/libdm/regex/parse_rx.c b/libdm/regex/parse_rx.c
new file mode 100644 (file)
index 0000000..ea3922f
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "parse_rx.h"
+
+#ifdef DEBUG
+#include <ctype.h>
+
+static void _regex_print(struct rx_node *rx, int depth, unsigned show_nodes)
+{
+       int i, numchars;
+
+       if (rx->left) {
+               if (rx->left->type != CHARSET && (show_nodes || (!((rx->type == CAT || rx->type == OR) && rx->left->type == CAT))))
+                       printf("(");
+
+               _regex_print(rx->left, depth + 1, show_nodes);
+
+               if (rx->left->type != CHARSET && (show_nodes || (!((rx->type == CAT || rx->type == OR) && rx->left->type == CAT))))
+                       printf(")");
+       }
+
+       /* display info about the node */
+       switch (rx->type) {
+       case CAT:
+               break;
+
+       case OR:
+               printf("|");
+               break;
+
+       case STAR:
+               printf("*");
+               break;
+
+       case PLUS:
+               printf("+");
+               break;
+
+       case QUEST:
+               printf("?");
+               break;
+
+       case CHARSET:
+               numchars = 0;
+               for (i = 0; i < 256; i++)
+                       if (dm_bit(rx->charset, i) && (isprint(i) || i == HAT_CHAR || i == DOLLAR_CHAR))
+                               numchars++;
+               if (numchars == 97) {
+                       printf(".");
+                       break;
+               }
+               if (numchars > 1)
+                       printf("[");
+               for (i = 0; i < 256; i++)
+                       if (dm_bit(rx->charset, i)) {
+                               if isprint(i)
+                                       printf("%c", (char) i);
+                               else if (i == HAT_CHAR)
+                                       printf("^");
+                               else if (i == DOLLAR_CHAR)
+                                       printf("$");
+                       }
+               if (numchars > 1)
+                       printf("]");
+               break;
+
+       default:
+               fprintf(stderr, "Unknown type");
+       }
+
+       if (rx->right) {
+               if (rx->right->type != CHARSET && (show_nodes || (!(rx->type == CAT && rx->right->type == CAT) && rx->right->right)))
+                       printf("(");
+               _regex_print(rx->right, depth + 1, show_nodes);
+               if (rx->right->type != CHARSET && (show_nodes || (!(rx->type == CAT && rx->right->type == CAT) && rx->right->right)))
+                       printf(")");
+       }
+
+       if (!depth)
+               printf("\n");
+}
+#endif /* DEBUG */
+
+struct parse_sp {              /* scratch pad for the parsing process */
+       struct dm_pool *mem;
+       int type;               /* token type, 0 indicates a charset */
+       dm_bitset_t charset;    /* The current charset */
+       const char *cursor;     /* where we are in the regex */
+       const char *rx_end;     /* 1pte for the expression being parsed */
+};
+
+static struct rx_node *_or_term(struct parse_sp *ps);
+
+static void _single_char(struct parse_sp *ps, unsigned int c, const char *ptr)
+{
+       ps->type = 0;
+       ps->cursor = ptr + 1;
+       dm_bit_clear_all(ps->charset);
+       dm_bit_set(ps->charset, c);
+}
+
+/*
+ * Get the next token from the regular expression.
+ * Returns: 1 success, 0 end of input, -1 error.
+ */
+static int _rx_get_token(struct parse_sp *ps)
+{
+       int neg = 0, range = 0;
+       char c, lc = 0;
+       const char *ptr = ps->cursor;
+       if (ptr == ps->rx_end) {        /* end of input ? */
+               ps->type = -1;
+               return 0;
+       }
+
+       switch (*ptr) {
+               /* charsets and ncharsets */
+       case '[':
+               ptr++;
+               if (*ptr == '^') {
+                       dm_bit_set_all(ps->charset);
+
+                       /* never transition on zero */
+                       dm_bit_clear(ps->charset, 0);
+                       neg = 1;
+                       ptr++;
+
+               } else
+                       dm_bit_clear_all(ps->charset);
+
+               while ((ptr < ps->rx_end) && (*ptr != ']')) {
+                       if (*ptr == '\\') {
+                               /* an escaped character */
+                               ptr++;
+                               switch (*ptr) {
+                               case 'n':
+                                       c = '\n';
+                                       break;
+                               case 'r':
+                                       c = '\r';
+                                       break;
+                               case 't':
+                                       c = '\t';
+                                       break;
+                               default:
+                                       c = *ptr;
+                               }
+                       } else if (*ptr == '-' && lc) {
+                               /* we've got a range on our hands */
+                               range = 1;
+                               ptr++;
+                               if (ptr == ps->rx_end) {
+                                       log_error("Incomplete range"
+                                                 "specification");
+                                       return -1;
+                               }
+                               c = *ptr;
+                       } else
+                               c = *ptr;
+
+                       if (range) {
+                               /* add lc - c into the bitset */
+                               if (lc > c) {
+                                       char tmp = c;
+                                       c = lc;
+                                       lc = tmp;
+                               }
+
+                               for (; lc <= c; lc++) {
+                                       if (neg)
+                                               dm_bit_clear(ps->charset, lc);
+                                       else
+                                               dm_bit_set(ps->charset, lc);
+                               }
+                               range = 0;
+                       } else {
+                               /* add c into the bitset */
+                               if (neg)
+                                       dm_bit_clear(ps->charset, c);
+                               else
+                                       dm_bit_set(ps->charset, c);
+                       }
+                       ptr++;
+                       lc = c;
+               }
+
+               if (ptr >= ps->rx_end) {
+                       ps->type = -1;
+                       return -1;
+               }
+
+               ps->type = 0;
+               ps->cursor = ptr + 1;
+               break;
+
+               /* These characters are special, we just return their ASCII
+                  codes as the type.  Sorted into ascending order to help the
+                  compiler */
+       case '(':
+       case ')':
+       case '*':
+       case '+':
+       case '?':
+       case '|':
+               ps->type = (int) *ptr;
+               ps->cursor = ptr + 1;
+               break;
+
+       case '^':
+               _single_char(ps, HAT_CHAR, ptr);
+               break;
+
+       case '$':
+               _single_char(ps, DOLLAR_CHAR, ptr);
+               break;
+
+       case '.':
+               /* The 'all but newline' character set */
+               ps->type = 0;
+               ps->cursor = ptr + 1;
+               dm_bit_set_all(ps->charset);
+               dm_bit_clear(ps->charset, (int) '\n');
+               dm_bit_clear(ps->charset, (int) '\r');
+               dm_bit_clear(ps->charset, 0);
+               break;
+
+       case '\\':
+               /* escaped character */
+               ptr++;
+               if (ptr >= ps->rx_end) {
+                       log_error("Badly quoted character at end "
+                                 "of expression");
+                       ps->type = -1;
+                       return -1;
+               }
+
+               ps->type = 0;
+               ps->cursor = ptr + 1;
+               dm_bit_clear_all(ps->charset);
+               switch (*ptr) {
+               case 'n':
+                       dm_bit_set(ps->charset, (int) '\n');
+                       break;
+               case 'r':
+                       dm_bit_set(ps->charset, (int) '\r');
+                       break;
+               case 't':
+                       dm_bit_set(ps->charset, (int) '\t');
+                       break;
+               default:
+                       dm_bit_set(ps->charset, (int) *ptr);
+               }
+               break;
+
+       default:
+               /* add a single character to the bitset */
+               ps->type = 0;
+               ps->cursor = ptr + 1;
+               dm_bit_clear_all(ps->charset);
+               dm_bit_set(ps->charset, (int) (unsigned char) *ptr);
+               break;
+       }
+
+       return 1;
+}
+
+static struct rx_node *_node(struct dm_pool *mem, int type,
+                            struct rx_node *l, struct rx_node *r)
+{
+       struct rx_node *n = dm_pool_zalloc(mem, sizeof(*n));
+
+       if (n) {
+               if (type == CHARSET && !(n->charset = dm_bitset_create(mem, 256))) {
+                       dm_pool_free(mem, n);
+                       return NULL;
+               }
+
+               n->type = type;
+               n->left = l;
+               n->right = r;
+       }
+
+       return n;
+}
+
+static struct rx_node *_term(struct parse_sp *ps)
+{
+       struct rx_node *n;
+
+       switch (ps->type) {
+       case 0:
+               if (!(n = _node(ps->mem, CHARSET, NULL, NULL))) {
+                       stack;
+                       return NULL;
+               }
+
+               dm_bit_copy(n->charset, ps->charset);
+               _rx_get_token(ps);      /* match charset */
+               break;
+
+       case '(':
+               _rx_get_token(ps);      /* match '(' */
+               n = _or_term(ps);
+               if (ps->type != ')') {
+                       log_error("missing ')' in regular expression");
+                       return 0;
+               }
+               _rx_get_token(ps);      /* match ')' */
+               break;
+
+       default:
+               n = 0;
+       }
+
+       return n;
+}
+
+static struct rx_node *_closure_term(struct parse_sp *ps)
+{
+       struct rx_node *l, *n;
+
+       if (!(l = _term(ps)))
+               return NULL;
+
+       for (;;) {
+               switch (ps->type) {
+               case '*':
+                       n = _node(ps->mem, STAR, l, NULL);
+                       break;
+
+               case '+':
+                       n = _node(ps->mem, PLUS, l, NULL);
+                       break;
+
+               case '?':
+                       n = _node(ps->mem, QUEST, l, NULL);
+                       break;
+
+               default:
+                       return l;
+               }
+
+               if (!n) {
+                       stack;
+                       return NULL;
+               }
+
+               _rx_get_token(ps);
+               l = n;
+       }
+
+       return n;
+}
+
+static struct rx_node *_cat_term(struct parse_sp *ps)
+{
+       struct rx_node *l, *r, *n;
+
+       if (!(l = _closure_term(ps)))
+               return NULL;
+
+       if (ps->type == '|')
+               return l;
+
+       if (!(r = _cat_term(ps)))
+               return l;
+
+       if (!(n = _node(ps->mem, CAT, l, r)))
+               stack;
+
+       return n;
+}
+
+static struct rx_node *_or_term(struct parse_sp *ps)
+{
+       struct rx_node *l, *r, *n;
+
+       if (!(l = _cat_term(ps)))
+               return NULL;
+
+       if (ps->type != '|')
+               return l;
+
+       _rx_get_token(ps);              /* match '|' */
+
+       if (!(r = _or_term(ps))) {
+               log_error("Badly formed 'or' expression");
+               return NULL;
+       }
+
+       if (!(n = _node(ps->mem, OR, l, r)))
+               stack;
+
+       return n;
+}
+
+/*----------------------------------------------------------------*/
+
+/* Macros for left and right nodes.  Inverted if 'leftmost' is set. */
+#define LEFT(a) (leftmost ? (a)->left : (a)->right)
+#define RIGHT(a) (leftmost ? (a)->right : (a)->left)
+
+/*
+ * The optimiser spots common prefixes on either side of an 'or' node, and
+ * lifts them outside the 'or' with a 'cat'.
+ */
+static unsigned _depth(struct rx_node *r, unsigned leftmost)
+{
+       int count = 1;
+
+       while (r->type != CHARSET && LEFT(r) && (leftmost || r->type != OR)) {
+               count++;
+               r = LEFT(r);
+       }
+
+       return count;
+}
+
+/*
+ * FIXME: a unique key could be built up as part of the parse, to make the
+ * comparison quick.  Alternatively we could use cons-hashing, and then
+ * this would simply be a pointer comparison.
+ */
+static int _nodes_equal(struct rx_node *l, struct rx_node *r)
+{
+       if (l->type != r->type)
+               return 0;
+
+       switch (l->type) {
+       case CAT:
+       case OR:
+               return _nodes_equal(l->left, r->left) &&
+                       _nodes_equal(l->right, r->right);
+
+       case STAR:
+       case PLUS:
+       case QUEST:
+               return _nodes_equal(l->left, r->left);
+
+       case CHARSET:
+               /*
+                * Never change anything containing TARGET_TRANS
+                * used by matcher as boundary marker between concatenated
+                * expressions.
+                */
+               return (!dm_bit(l->charset, TARGET_TRANS) && dm_bitset_equal(l->charset, r->charset));
+       }
+
+       /* NOTREACHED */
+       return_0;
+}
+
+static int _find_leftmost_common(struct rx_node *or,
+                                 struct rx_node **l,
+                                 struct rx_node **r,
+                                unsigned leftmost)
+{
+       struct rx_node *left = or->left, *right = or->right;
+       unsigned left_depth = _depth(left, leftmost);
+       unsigned right_depth = _depth(right, leftmost);
+
+       while (left_depth > right_depth && left->type != OR) {
+               left = LEFT(left);
+               left_depth--;
+       }
+
+       while (right_depth > left_depth && right->type != OR) {
+               right = LEFT(right);
+               right_depth--;
+       }
+
+       if (left_depth != right_depth)
+               return 0;
+
+       while (left_depth) {
+               if (left->type == CAT && right->type == CAT) {
+                       if (_nodes_equal(LEFT(left), LEFT(right))) {
+                               *l = left;
+                               *r = right;
+                               return 1;
+                       }
+               }
+               if (left->type == OR || right->type == OR)
+                       break;
+               left = LEFT(left);
+               right = LEFT(right);
+               left_depth--;
+       }
+
+       return 0;
+}
+
+/* If top node is OR, rotate (leftmost example) from ((ab)|((ac)|d)) to (((ab)|(ac))|d) */
+static int _rotate_ors(struct rx_node *r, unsigned leftmost)
+{
+       struct rx_node *old_node;
+
+       if (r->type != OR || RIGHT(r)->type != OR)
+               return 0;
+
+       old_node = RIGHT(r);
+
+       if (leftmost) {
+               r->right = RIGHT(old_node);
+               old_node->right = LEFT(old_node);
+               old_node->left = LEFT(r);
+               r->left = old_node;
+       } else {
+               r->left = RIGHT(old_node);
+               old_node->left = LEFT(old_node);
+               old_node->right = LEFT(r);
+               r->right = old_node;
+       }
+
+       return 1;
+}
+
+static struct rx_node *_exchange_nodes(struct dm_pool *mem, struct rx_node *r,
+                                      struct rx_node *left_cat, struct rx_node *right_cat,
+                                      unsigned leftmost)
+{
+       struct rx_node *new_r;
+
+       if (leftmost)
+               new_r = _node(mem, CAT, LEFT(left_cat), r);
+       else
+               new_r = _node(mem, CAT, r, LEFT(right_cat));
+
+       if (!new_r)
+               return_NULL;
+
+       memcpy(left_cat, RIGHT(left_cat), sizeof(*left_cat));
+       memcpy(right_cat, RIGHT(right_cat), sizeof(*right_cat));
+
+       return new_r;
+}
+
+static struct rx_node *_pass(struct dm_pool *mem,
+                             struct rx_node *r,
+                             int *changed)
+{
+       struct rx_node *left, *right;
+
+       /*
+        * walk the tree, optimising every 'or' node.
+        */
+       switch (r->type) {
+       case CAT:
+               if (!(r->left = _pass(mem, r->left, changed)))
+                       return_NULL;
+
+               if (!(r->right = _pass(mem, r->right, changed)))
+                       return_NULL;
+
+               break;
+
+       case STAR:
+       case PLUS:
+       case QUEST:
+               if (!(r->left = _pass(mem, r->left, changed)))
+                       return_NULL;
+
+               break;
+       case OR:
+               /* It's important we optimise sub nodes first */
+               if (!(r->left = _pass(mem, r->left, changed)))
+                       return_NULL;
+
+               if (!(r->right = _pass(mem, r->right, changed)))
+                       return_NULL;
+               /*
+                * If rotate_ors changes the tree, left and right are stale,
+                * so just set 'changed' to repeat the search.
+                *
+                * FIXME Check we can't 'bounce' between left and right rotations here.
+                */
+               if (_find_leftmost_common(r, &left, &right, 1)) {
+                       if (!_rotate_ors(r, 1))
+                               r = _exchange_nodes(mem, r, left, right, 1);
+                       *changed = 1;
+               } else if (_find_leftmost_common(r, &left, &right, 0)) {
+                       if (!_rotate_ors(r, 0))
+                               r = _exchange_nodes(mem, r, left, right, 0);
+                       *changed = 1;
+               }
+               break;
+
+       case CHARSET:
+               break;
+       }
+
+       return r;
+}
+
+static struct rx_node *_optimise(struct dm_pool *mem, struct rx_node *r)
+{
+       /*
+        * We're looking for (or (... (cat <foo> a)) (... (cat <foo> b)))
+        * and want to turn it into (cat <foo> (or (... a) (... b)))
+        *
+        * (fa)|(fb) becomes f(a|b)
+        */
+
+       /*
+        * Initially done as an inefficient multipass algorithm.
+        */
+       int changed;
+
+       do {
+               changed = 0;
+               r = _pass(mem, r, &changed);
+       } while (r && changed);
+
+       return r;
+}
+
+/*----------------------------------------------------------------*/
+
+struct rx_node *rx_parse_tok(struct dm_pool *mem,
+                            const char *begin, const char *end)
+{
+       struct rx_node *r;
+       struct parse_sp *ps = dm_pool_zalloc(mem, sizeof(*ps));
+
+       if (!ps)
+               return_NULL;
+
+       ps->mem = mem;
+       if (!(ps->charset = dm_bitset_create(mem, 256))) {
+               log_error("Regex charset allocation failed");
+               dm_pool_free(mem, ps);
+               return NULL;
+       }
+       ps->cursor = begin;
+       ps->rx_end = end;
+       _rx_get_token(ps);              /* load the first token */
+
+       if (!(r = _or_term(ps))) {
+               log_error("Parse error in regex");
+               dm_pool_free(mem, ps);
+               return NULL;
+       }
+
+       if (!(r = _optimise(mem, r))) {
+               log_error("Regex optimisation error");
+               dm_pool_free(mem, ps);
+               return NULL;
+       }
+
+       return r;
+}
+
+struct rx_node *rx_parse_str(struct dm_pool *mem, const char *str)
+{
+       return rx_parse_tok(mem, str, str + strlen(str));
+}
diff --git a/libdm/regex/parse_rx.h b/libdm/regex/parse_rx.h
new file mode 100644 (file)
index 0000000..37d7ced
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _DM_PARSE_REGEX_H
+#define _DM_PARSE_REGEX_H
+
+enum {
+       CAT,
+       STAR,
+       PLUS,
+       OR,
+       QUEST,
+       CHARSET
+};
+
+/*
+ * We're never going to be running the regex on non-printable
+ * chars, so we can use a couple of these chars to represent the
+ * start and end of a string.
+ */
+#define HAT_CHAR 0x2
+#define DOLLAR_CHAR 0x3
+
+#define TARGET_TRANS '\0'
+
+struct rx_node {
+       int type;
+       dm_bitset_t charset;
+       struct rx_node *left, *right;
+
+       /* used to build the dfa for the toker */
+        unsigned charset_index;
+       int nullable, final;
+       dm_bitset_t firstpos;
+       dm_bitset_t lastpos;
+       dm_bitset_t followpos;
+};
+
+struct rx_node *rx_parse_str(struct dm_pool *mem, const char *str);
+struct rx_node *rx_parse_tok(struct dm_pool *mem,
+                            const char *begin, const char *end);
+
+#endif
diff --git a/libdm/regex/ttree.c b/libdm/regex/ttree.c
new file mode 100644 (file)
index 0000000..ec97c98
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "dmlib.h"
+#include "ttree.h"
+
+struct node {
+       unsigned k;
+       struct node *l, *m, *r;
+       void *data;
+};
+
+struct ttree {
+       int klen;
+       struct dm_pool *mem;
+       struct node *root;
+};
+
+static struct node **_lookup_single(struct node **c, unsigned int k)
+{
+       while (*c) {
+               if (k < (*c)->k)
+                       c = &((*c)->l);
+
+               else if (k > (*c)->k)
+                       c = &((*c)->r);
+
+               else {
+                       c = &((*c)->m);
+                       break;
+               }
+       }
+
+       return c;
+}
+
+void *ttree_lookup(struct ttree *tt, unsigned *key)
+{
+       struct node **c = &tt->root;
+       int count = tt->klen;
+
+       while (*c && count) {
+               c = _lookup_single(c, *key++);
+               count--;
+       }
+
+       return *c ? (*c)->data : NULL;
+}
+
+static struct node *_tree_node(struct dm_pool *mem, unsigned int k)
+{
+       struct node *n = dm_pool_zalloc(mem, sizeof(*n));
+
+       if (n)
+               n->k = k;
+
+       return n;
+}
+
+int ttree_insert(struct ttree *tt, unsigned int *key, void *data)
+{
+       struct node **c = &tt->root;
+       int count = tt->klen;
+       unsigned int k;
+
+       do {
+               k = *key++;
+               c = _lookup_single(c, k);
+               count--;
+
+       } while (*c && count);
+
+       if (!*c) {
+               count++;
+
+               while (count--) {
+                       if (!(*c = _tree_node(tt->mem, k))) {
+                               stack;
+                               return 0;
+                       }
+
+                       if (count) {
+                               k = *key++;
+                               c = &((*c)->m);
+                       }
+               }
+       }
+       (*c)->data = data;
+
+       return 1;
+}
+
+struct ttree *ttree_create(struct dm_pool *mem, unsigned int klen)
+{
+       struct ttree *tt;
+
+       if (!(tt = dm_pool_zalloc(mem, sizeof(*tt)))) {
+               stack;
+               return NULL;
+       }
+
+       tt->klen = klen;
+       tt->mem = mem;
+       return tt;
+}
diff --git a/libdm/regex/ttree.h b/libdm/regex/ttree.h
new file mode 100644 (file)
index 0000000..b4e3ba2
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _DM_TTREE_H
+#define _DM_TTREE_H
+
+struct ttree;
+
+struct ttree *ttree_create(struct dm_pool *mem, unsigned int klen);
+
+void *ttree_lookup(struct ttree *tt, unsigned *key);
+int ttree_insert(struct ttree *tt, unsigned *key, void *data);
+
+#endif
diff --git a/liblvm/.exported_symbols b/liblvm/.exported_symbols
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/liblvm/Doxyfile b/liblvm/Doxyfile
new file mode 100644 (file)
index 0000000..3d2e8e4
--- /dev/null
@@ -0,0 +1,254 @@
+# Doxyfile 1.5.7.1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           =
+PROJECT_NUMBER         =
+OUTPUT_DIRECTORY       = doxygen-output
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       =
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = YES
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+TYPEDEF_HIDES_STRUCT   = NO
+SYMBOL_CACHE_SIZE      = 0
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = NO
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = ./
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.c \
+                         *.h
+RECURSIVE              = NO
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           = ../test/api
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_ALIGN_MEMBERS     = YES
+HTML_DYNAMIC_SECTIONS  = NO
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+CHM_INDEX_ENCODING     =
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+GENERATE_QHP           = NO
+QCH_FILE               =
+QHP_NAMESPACE          = org.doxygen.Project
+QHP_VIRTUAL_FOLDER     = doc
+QHG_LOCATION           =
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NONE
+TREEVIEW_WIDTH         = 250
+FORMULA_FONTSIZE       = 10
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = YES
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             =
+XML_DTD                =
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = ../libdm
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             =
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+DOT_FONTNAME           = FreeSans
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = YES
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/liblvm/Makefile.in b/liblvm/Makefile.in
new file mode 100644 (file)
index 0000000..ec9522b
--- /dev/null
@@ -0,0 +1,81 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+       lvm_misc.c \
+       lvm_base.c \
+       lvm_lv.c \
+       lvm_pv.c \
+       lvm_vg.c
+
+LIB_NAME = liblvm2app
+LIB_VERSION = $(LIB_VERSION_APP)
+
+ifeq ("@STATIC_LINK@", "yes")
+  LIB_STATIC = $(LIB_NAME).a
+endif
+
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+
+CLEAN_TARGETS += liblvm.cflow $(LIB_NAME).a
+
+EXPORTED_HEADER = $(srcdir)/lvm2app.h
+EXPORTED_FN_PREFIX = lvm
+
+include $(top_builddir)/make.tmpl
+
+LIBS += $(LVMINTERNAL_LIBS) -ldevmapper
+
+ifeq ("@DMEVENTD@", "yes")
+  LIBS += -ldevmapper-event
+endif
+
+.PHONY: install_dynamic install_static install_include install_pkgconfig
+
+INSTALL_TYPE = install_dynamic
+
+ifeq ("@STATIC_LINK@", "yes")
+  INSTALL_TYPE += install_static
+endif
+
+ifeq ("@PKGCONFIG@", "yes")
+  INSTALL_TYPE += install_pkgconfig
+endif
+
+install: $(INSTALL_TYPE) install_include
+
+install_include: $(srcdir)/lvm2app.h
+       $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_dynamic: install_lib_shared
+
+install_static: $(LIB_STATIC)
+       $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
+
+install_pkgconfig: $(LIB_NAME).pc
+       $(INSTALL_DATA) -D $< $(pkgconfigdir)/lvm2app.pc
+
+liblvm.cflow: $(SOURCES)
+       set -e; (echo -n "SOURCES += "; \
+                echo $(SOURCES) | \
+                sed "s/^/ /;s/ / $(top_srcdir)\/liblvm\//g;s/$$//"; \
+                ) > $@
+
+cflow: liblvm.cflow
+
+DISTCLEAN_TARGETS += $(LIB_NAME).pc .exported_symbols_generated
diff --git a/liblvm/liblvm2app.pc.in b/liblvm/liblvm2app.pc.in
new file mode 100644 (file)
index 0000000..4c71c36
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: lvm2app
+Description: lvm2 application library
+Version: @LVM_MAJOR@.@LVM_LIBAPI@
+Cflags: -I${includedir} 
+Libs: -L${libdir} -llvm2app
+Requires.private: devmapper
diff --git a/liblvm/lvm2app.h b/liblvm/lvm2app.h
new file mode 100644 (file)
index 0000000..317911d
--- /dev/null
@@ -0,0 +1,1610 @@
+/*
+ * Copyright (C) 2008,2009,2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LIB_LVM2APP_H
+#define _LIB_LVM2APP_H
+
+#include <libdevmapper.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************** WARNING ***********************************
+ *
+ * NOTE: This API is under development and subject to change at any time.
+ *
+ * Please send feedback to lvm-devel@redhat.com
+ *
+ *********************************** WARNING ********************************/
+
+/*************************** Design Overview ********************************/
+
+/**
+ * \mainpage LVM library API
+ *
+ * The API is designed around the following basic LVM objects:
+ * 1) Physical Volume (pv_t) 2) Volume Group (vg_t) 3) Logical Volume (lv_t).
+ *
+ * The library provides functions to list the objects in a system,
+ * get and set object properties (such as names, UUIDs, and sizes), as well
+ * as create/remove objects and perform more complex operations and
+ * transformations. Each object instance is represented by a handle, and
+ * handles are passed to and from the functions to perform the operations.
+ *
+ * A central object in the library is the Volume Group, represented by the
+ * VG handle, vg_t. Performing an operation on a PV or LV object first
+ * requires obtaining a VG handle. Once the vg_t has been obtained, it can
+ * be used to enumerate the pv_t and lv_t objects within that vg_t. Attributes
+ * of these objects can then be queried or changed.
+ *
+ * A volume group handle may be obtained with read or write permission.
+ * Any attempt to change a property of a pv_t, vg_t, or lv_t without
+ * obtaining write permission on the vg_t will fail with EPERM.
+ *
+ * An application first opening a VG read-only, then later wanting to change
+ * a property of an object must first close the VG and re-open with write
+ * permission. Currently liblvm provides no mechanism to determine whether
+ * the VG has changed on-disk in between these operations - this is the
+ * application's responsiblity. One way the application can ensure the VG
+ * has not changed is to save the "vg_seqno" field after opening the VG with
+ * READ permission. If the application later needs to modify the VG, it can
+ * close the VG and re-open with WRITE permission. It should then check
+ * whether the original "vg_seqno" obtained with READ permission matches
+ * the new one obtained with WRITE permission.
+ */
+
+/**
+ * Retrieve the library version.
+ *
+ * The library version is the same format as the full LVM version.
+ * The format is as follows:
+ *    LVM_MAJOR.LVM_MINOR.LVM_PATCHLEVEL(LVM_LIBAPI)[-LVM_RELEASE]
+ * An application wishing to determine compatibility with a particular version
+ * of the library should check at least the LVM_MAJOR, LVM_MINOR, and
+ * LVM_LIBAPI numbers.  For example, assume the full LVM version is
+ * 2.02.50(1)-1.  The application should verify the "2.02" and the "(1)".
+ *
+ * \return  A string describing the library version.
+ */
+const char *lvm_library_get_version(void);
+
+/******************************** structures ********************************/
+
+/**
+ * Opaque structures - do not use directly.  Internal structures may change
+ * without notice between releases, whereas this API will be changed much less
+ * frequently.  Backwards compatibility will normally be preserved in future
+ * releases.  On any occasion when the developers do decide to break backwards
+ * compatibility in any significant way, the LVM_LIBAPI number (included in
+ * the library's soname) will be incremented.
+ */
+struct lvm;
+struct physical_volume;
+struct volume_group;
+struct logical_volume;
+struct lv_segment;
+struct pv_segment;
+
+/**
+ * \class lvm_t
+ *
+ * This is the base handle that is needed to open and create objects such as
+ * volume groups and logical volumes.  In addition, this handle provides a
+ * context for error handling information, saving any error number (see
+ * lvm_errno()) and error message (see lvm_errmsg()) that any function may
+ * generate.
+ */
+typedef struct lvm *lvm_t;
+
+/**
+ * \class vg_t
+ *
+ * The volume group object is a central object in the library, and can be
+ * either a read-only object or a read-write object depending on the function
+ * used to obtain the object handle. For example, lvm_vg_create() always
+ * returns a read/write handle, while lvm_vg_open() has a "mode" argument
+ * to define the read/write mode of the handle.
+ */
+typedef struct volume_group *vg_t;
+
+/**
+ * \class lv_t
+ *
+ * This logical volume object is bound to a vg_t and has the same
+ * read/write mode as the vg_t.  Changes will be written to disk
+ * when the vg_t gets committed to disk by calling lvm_vg_write().
+ */
+typedef struct logical_volume *lv_t;
+
+/**
+ * \class pv_t
+ *
+ * This physical volume object is bound to a vg_t and has the same
+ * read/write mode as the vg_t.  Changes will be written to disk
+ * when the vg_t gets committed to disk by calling lvm_vg_write().
+ */
+typedef struct physical_volume *pv_t;
+
+/**
+ * \class lvseg_t
+ *
+ * This lv segment object is bound to a lv_t.
+ */
+typedef struct lv_segment *lvseg_t;
+
+/**
+ * \class pvseg_t
+ *
+ * This pv segment object is bound to a pv_t.
+ */
+typedef struct pv_segment *pvseg_t;
+
+/**
+ * Logical Volume object list.
+ *
+ * Lists of these structures are returned by lvm_vg_list_lvs().
+ */
+typedef struct lvm_lv_list {
+       struct dm_list list;
+       lv_t lv;
+} lv_list_t;
+
+/**
+ * Logical Volume Segment object list.
+ *
+ * Lists of these structures are returned by lvm_lv_list_lvsegs().
+ */
+typedef struct lvm_lvseg_list {
+       struct dm_list list;
+       lvseg_t lvseg;
+} lvseg_list_t;
+
+/**
+ * Physical volume object list.
+ *
+ * Lists of these structures are returned by lvm_vg_list_pvs().
+ */
+typedef struct lvm_pv_list {
+       struct dm_list list;
+       pv_t pv;
+} pv_list_t;
+
+/**
+ * Physical Volume Segment object list.
+ *
+ * Lists of these structures are returned by lvm_pv_list_pvsegs().
+ */
+typedef struct lvm_pvseg_list {
+       struct dm_list list;
+       pvseg_t pvseg;
+} pvseg_list_t;
+
+/**
+ * String list.
+ *
+ * This string list contains read-only strings.
+ * Lists of these structures are returned by functions such as
+ * lvm_list_vg_names() and lvm_list_vg_uuids().
+ */
+typedef struct lvm_str_list {
+       struct dm_list list;
+       const char *str;
+} lvm_str_list_t;
+
+/**
+ * Property Value
+ *
+ * This structure defines a single LVM property value for an LVM object.
+ * The structures are returned by functions such as
+ * lvm_vg_get_property().
+ *
+ * is_settable: indicates whether a 'set' function exists for this property
+ * is_string: indicates whether this property is a string (1) or not (0)
+ * is_integer: indicates whether this property is an integer (1) or not (0)
+ * is_valid: indicates whether 'value' is valid (1) or not (0)
+ */
+typedef struct lvm_property_value {
+       uint32_t is_settable:1;
+       uint32_t is_string:1;
+       uint32_t is_integer:1;
+       uint32_t is_valid:1;
+       uint32_t padding:28;
+       union {
+               const char *string;
+               uint64_t integer;
+       } value;
+} lvm_property_value_t;
+
+/*************************** generic lvm handling ***************************/
+/**
+ * Create a LVM handle.
+ *
+ * \memberof lvm_t
+ *
+ * Once all LVM operations have been completed, use lvm_quit() to release
+ * the handle and any associated resources.
+ *
+ * \param system_dir
+ * Set an alternative LVM system directory. Use NULL to use the
+ * default value. If the environment variable LVM_SYSTEM_DIR is set,
+ * it will override any system_dir setting.
+ *
+ * \return
+ * A valid LVM handle is returned or NULL if there has been a
+ * memory allocation problem. You have to check if an error occured
+ * with the lvm_error() function.
+ */
+lvm_t lvm_init(const char *system_dir);
+
+/**
+ * Destroy a LVM handle allocated with lvm_init().
+ *
+ * \memberof lvm_t
+ *
+ * This function should be used after all LVM operations are complete or after
+ * an unrecoverable error.  Destroying the LVM handle frees the memory and
+ * other resources associated with the handle.  Once destroyed, the handle
+ * cannot be used subsequently.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ */
+void lvm_quit(lvm_t libh);
+
+/**
+ * Reload the original configuration from the system directory.
+ *
+ * \memberof lvm_t
+ *
+ * This function should be used when any LVM configuration changes in the LVM
+ * system_dir or by another lvm_config* function, and the change is needed by
+ * the application.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_config_reload(lvm_t libh);
+
+/**
+ * Override the LVM configuration with a configuration string.
+ *
+ * \memberof lvm_t
+ *
+ * This function is equivalent to the --config option on lvm commands.
+ * Once this API has been used to over-ride the configuration,
+ * use lvm_config_reload() to apply the new settings.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \param   config_string
+ * LVM configuration string to apply.  See the lvm.conf file man page
+ * for the format of the config string.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_config_override(lvm_t libh, const char *config_string);
+
+/**
+ * Return stored error no describing last LVM API error.
+ *
+ * \memberof lvm_t
+ *
+ * Users of liblvm should use lvm_errno to determine the details of a any
+ * failure of the last call.  A basic success or fail is always returned by
+ * every function, either by returning a 0 or -1, or a non-NULL / NULL.
+ * If a function has failed, lvm_errno may be used to get a more specific
+ * error code describing the failure.  In this way, lvm_errno may be used
+ * after every function call, even after a 'get' function call that simply
+ * returns a value.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * An errno value describing the last LVM error.
+ */
+int lvm_errno(lvm_t libh);
+
+/**
+ * Return stored error message describing last LVM error.
+ *
+ * \memberof lvm_t
+ *
+ * This function may be used in conjunction with lvm_errno() to obtain more
+ * specific error information for a function that is known to have failed.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * An error string describing the last LVM error.
+ */
+const char *lvm_errmsg(lvm_t libh);
+
+/**
+ * Scan all devices on the system for VGs and LVM metadata.
+ *
+ * \memberof lvm_t
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_scan(lvm_t libh);
+
+/**
+ * Return the list of volume group names.
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the list is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function normally does not scan devices in the system for LVM
+ * metadata.  To scan the system, use lvm_scan().
+ *
+ * To process the list, use the dm_list iterator functions.  For example:
+ *      vg_t vg;
+ *      struct dm_list *vgnames;
+ *      struct lvm_str_list *strl;
+ *
+ *      vgnames = lvm_list_vg_names(libh);
+ *     dm_list_iterate_items(strl, vgnames) {
+ *             vgname = strl->str;
+ *              vg = lvm_vg_open(libh, vgname, "r");
+ *              // do something with vg
+ *              lvm_vg_close(vg);
+ *      }
+ *
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * VG name strings of the Volume Groups known to the system.
+ * NULL is returned if unable to allocate memory.
+ * An empty list (verify with dm_list_empty) is returned if no VGs
+ * exist on the system.
+ */
+struct dm_list *lvm_list_vg_names(lvm_t libh);
+
+/**
+ * Return the list of volume group uuids.
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the list is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function normally does not scan devices in the system for LVM
+ * metadata.  To scan the system, use lvm_scan().
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * VG UUID strings of the Volume Groups known to the system.
+ * NULL is returned if unable to allocate memory.
+ * An empty list (verify with dm_list_empty) is returned if no VGs
+ * exist on the system.
+ */
+struct dm_list *lvm_list_vg_uuids(lvm_t libh);
+
+/**
+ * Return the volume group name given a PV UUID
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the name is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function may scan devices in the system for LVM metadata.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * The volume group name for the given PV UUID.
+ * NULL is returned if the PV UUID is not associated with a volume group.
+ */
+const char *lvm_vgname_from_pvid(lvm_t libh, const char *pvid);
+
+/**
+ * Return the volume group name given a device name
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the name is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function may scan devices in the system for LVM metadata.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * The volume group name for the given device name.
+ * NULL is returned if the device is not an LVM device.
+ *
+ */
+const char *lvm_vgname_from_device(lvm_t libh, const char *device);
+
+/**
+ * Open an existing VG.
+ *
+ * Open a VG for reading or writing.
+ *
+ * \memberof lvm_t
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \param   vgname
+ * Name of the VG to open.
+ *
+ * \param   mode
+ * Open mode - either "r" (read) or "w" (read/write).
+ * Any other character results in an error with EINVAL set.
+ *
+ * \param   flags
+ * Open flags - currently ignored.
+ *
+ * \return  non-NULL VG handle (success) or NULL (failure).
+ */
+vg_t lvm_vg_open(lvm_t libh, const char *vgname, const char *mode,
+                 uint32_t flags);
+
+/**
+ * Create a VG with default parameters.
+ *
+ * \memberof lvm_t
+ *
+ * This function creates a Volume Group object in memory.
+ * Upon success, other APIs may be used to set non-default parameters.
+ * For example, to set a non-default extent size, use lvm_vg_set_extent_size().
+ * Next, to add physical storage devices to the volume group, use
+ * lvm_vg_extend() for each device.
+ * Once all parameters are set appropriately and all devices are added to the
+ * VG, use lvm_vg_write() to commit the new VG to disk, and lvm_vg_close() to
+ * release the VG handle.
+ *
+ * \param   libh
+ * Handle obtained from lvm_init().
+ *
+ * \param   vg_name
+ * Name of the VG to open.
+ *
+ * \return
+ * non-NULL vg handle (success) or NULL (failure)
+ */
+vg_t lvm_vg_create(lvm_t libh, const char *vg_name);
+
+/*************************** volume group handling **************************/
+
+/**
+ * Return a list of LV handles for a given VG handle.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * A list of lvm_lv_list structures containing lv handles for this vg.
+ * If no LVs exist on the given VG, NULL is returned.
+ */
+struct dm_list *lvm_vg_list_lvs(vg_t vg);
+
+/**
+ * Return a list of PV handles for a given VG handle.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * A list of lvm_pv_list structures containing pv handles for this vg.
+ * If no PVs exist on the given VG, NULL is returned.
+ */
+struct dm_list *lvm_vg_list_pvs(vg_t vg);
+
+/**
+ * Write a VG to disk.
+ *
+ * \memberof vg_t
+ *
+ * This function commits the Volume Group object referenced by the VG handle
+ * to disk. Upon failure, retry the operation and/or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_write(vg_t vg);
+
+/**
+ * Remove a VG from the system.
+ *
+ * \memberof vg_t
+ *
+ * This function removes a Volume Group object in memory, and requires
+ * calling lvm_vg_write() to commit the removal to disk.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_remove(vg_t vg);
+
+/**
+ * Close a VG opened with lvm_vg_create or lvm_vg_open().
+ *
+ * \memberof vg_t
+ *
+ * This function releases a VG handle and any resources associated with the
+ * handle.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_close(vg_t vg);
+
+/**
+ * Extend a VG by adding a device.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully adding a device, use lvm_vg_write() to commit the new VG
+ * to disk.  Upon failure, retry the operation or release the VG handle with
+ * lvm_vg_close().
+ * If the device is not initialized for LVM use, it will be initialized
+ * before adding to the VG.  Although some internal checks are done,
+ * the caller should be sure the device is not in use by other subsystems
+ * before calling lvm_vg_extend().
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   device
+ * Absolute pathname of device to add to VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_extend(vg_t vg, const char *device);
+
+/**
+ * Reduce a VG by removing an unused device.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully removing a device, use lvm_vg_write() to commit the new VG
+ * to disk.  Upon failure, retry the operation or release the VG handle with
+ * lvm_vg_close().
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   device
+ * Name of device to remove from VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_reduce(vg_t vg, const char *device);
+
+/**
+ * Add a tag to a VG.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully adding a tag, use lvm_vg_write() to commit the
+ * new VG to disk.  Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   tag
+ * Tag to add to the VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_add_tag(vg_t vg, const char *tag);
+
+/**
+ * Remove a tag from a VG.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully removing a tag, use lvm_vg_write() to commit the
+ * new VG to disk.  Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   tag
+ * Tag to remove from VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_remove_tag(vg_t vg, const char *tag);
+
+/**
+ * Set the extent size of a VG.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully setting a new extent size, use lvm_vg_write() to commit
+ * the new VG to disk.  Upon failure, retry the operation or release the VG
+ * handle with lvm_vg_close().
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   new_size
+ * New extent size in bytes.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_set_extent_size(vg_t vg, uint32_t new_size);
+
+/**
+ * Get whether or not a volume group is clustered.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 1 if the VG is clustered, 0 if not
+ */
+uint64_t lvm_vg_is_clustered(vg_t vg);
+
+/**
+ * Get whether or not a volume group is exported.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 1 if the VG is exported, 0 if not
+ */
+uint64_t lvm_vg_is_exported(vg_t vg);
+
+/**
+ * Get whether or not a volume group is a partial volume group.
+ *
+ * \memberof vg_t
+ *
+ * When one or more physical volumes belonging to the volume group
+ * are missing from the system the volume group is a partial volume
+ * group.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 1 if the VG is PVs, 0 if not
+ */
+uint64_t lvm_vg_is_partial(vg_t vg);
+
+/**
+ * Get the current metadata sequence number of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * The metadata sequence number is incrented for each metadata change.
+ * Applications may use the sequence number to determine if any LVM objects
+ * have changed from a prior query.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Metadata sequence number.
+ */
+uint64_t lvm_vg_get_seqno(const vg_t vg);
+
+/**
+ * Get the current uuid of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for the uuid is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Copy of the uuid string.
+ */
+const char *lvm_vg_get_uuid(const vg_t vg);
+
+/**
+ * Get the current name of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for the name is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Copy of the name.
+ */
+const char *lvm_vg_get_name(const vg_t vg);
+
+/**
+ * Get the current size in bytes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_vg_get_size(const vg_t vg);
+
+/**
+ * Get the current unallocated space in bytes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Free size in bytes.
+ */
+uint64_t lvm_vg_get_free_size(const vg_t vg);
+
+/**
+ * Get the current extent size in bytes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Extent size in bytes.
+ */
+uint64_t lvm_vg_get_extent_size(const vg_t vg);
+
+/**
+ * Get the current number of total extents of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Extent count.
+ */
+uint64_t lvm_vg_get_extent_count(const vg_t vg);
+
+/**
+ * Get the current number of free extents of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Free extent count.
+ */
+uint64_t lvm_vg_get_free_extent_count(const vg_t vg);
+
+/**
+ * Get the current number of physical volumes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Physical volume count.
+ */
+uint64_t lvm_vg_get_pv_count(const vg_t vg);
+
+/**
+ * Get the maximum number of physical volumes allowed in a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Maximum number of physical volumes allowed in a volume group.
+ */
+uint64_t lvm_vg_get_max_pv(const vg_t vg);
+
+/**
+ * Get the maximum number of logical volumes allowed in a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Maximum number of logical volumes allowed in a volume group.
+ */
+uint64_t lvm_vg_get_max_lv(const vg_t vg);
+
+/**
+ * Return the list of volume group tags.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for the list is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * To process the list, use the dm_list iterator functions.  For example:
+ *      vg_t vg;
+ *      struct dm_list *tags;
+ *      struct lvm_str_list *strl;
+ *
+ *      tags = lvm_vg_get_tags(vg);
+ *     dm_list_iterate_items(strl, tags) {
+ *             tag = strl->str;
+ *              // do something with tag
+ *      }
+ *
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * tag strings attached to volume group.
+ * If no tags are attached to the given VG, an empty list is returned
+ * (check with dm_list_empty()).
+ * If there is a problem obtaining the list of tags, NULL is returned.
+ */
+struct dm_list *lvm_vg_get_tags(const vg_t vg);
+
+/**
+ * Get the value of a VG property
+ *
+ * \memberof vg_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   name
+ * Name of property to query.  See vgs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ *      lvm_property_value v;
+ *      char *prop_name = "vg_mda_count";
+ *
+ *      v = lvm_vg_get_property(vg, prop_name);
+ *      if (!v.is_valid) {
+ *           printf("Invalid property name or unable to query"
+ *                  "'%s', errno = %d.\n", prop_name, lvm_errno(libh));
+ *           return;
+ *      }
+ *      if (v.is_string)
+ *           printf(", value = %s\n", v.value.string);
+ *     if (v.is_integer)
+ *           printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property.  Caller should check 'is_valid' flag before using
+ * the value.  If 'is_valid' is not set, caller should check lvm_errno()
+ * for specific error.
+ */
+struct lvm_property_value lvm_vg_get_property(const vg_t vg, const char *name);
+
+/**
+ * Set the value of a VG property.  Note that the property must be
+ * a 'settable' property, as evidenced by the 'is_settable' flag
+ * when querying the property.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example (integer):
+ *      lvm_property_value copies;
+ *
+ *      if (lvm_vg_get_property(vg, "vg_mda_copies", &copies) < 0) {
+ *              // Error - unable to query property
+ *      }
+ *      if (!copies.is_settable) {
+ *              // Error - property not settable
+ *      }
+ *      copies.value.integer = 2;
+ *      if (lvm_vg_set_property(vg, "vg_mda_copies", &copies) < 0) {
+ *              // handle error
+ *      }
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_set_property(const vg_t vg, const char *name,
+                       struct lvm_property_value *value);
+
+/************************** logical volume handling *************************/
+
+/**
+ * Create a linear logical volume.
+ * This function commits the change to disk and does _not_ require calling
+ * lvm_vg_write().
+ * NOTE: The commit behavior of this function is subject to change
+ * as the API is developed.
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   name
+ * Name of logical volume to create.
+ *
+ * \param   size
+ * Size of logical volume in extents.
+ *
+ * \return
+ * non-NULL handle to an LV object created, or NULL if creation fails.
+ *
+ */
+lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size);
+
+/**
+ * Return a list of lvseg handles for a given LV handle.
+ *
+ * \memberof lv_t
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * A list of lvm_lvseg_list structures containing lvseg handles for this lv.
+ */
+struct dm_list *lvm_lv_list_lvsegs(lv_t lv);
+
+/**
+ * Lookup an LV handle in a VG by the LV name.
+ *
+ * \memberof lv_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   name
+ * Name of LV to lookup.
+ *
+ * \return
+ * non-NULL handle to the LV 'name' attached to the VG.
+ * NULL is returned if the LV name is not associated with the VG handle.
+ */
+lv_t lvm_lv_from_name(vg_t vg, const char *name);
+
+/**
+ * Lookup an LV handle in a VG by the LV uuid.
+ * The form of the uuid may be either the formatted, human-readable form,
+ * or the non-formatted form.
+ *
+ * \memberof lv_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   uuid
+ * UUID of LV to lookup.
+ *
+ * \return
+ * non-NULL handle to the LV with 'uuid' attached to the VG.
+ * NULL is returned if the LV uuid is not associated with the VG handle.
+ */
+lv_t lvm_lv_from_uuid(vg_t vg, const char *uuid);
+
+/**
+ * Activate a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * This function is the equivalent of the lvm command "lvchange -ay".
+ *
+ * NOTE: This function cannot currently handle LVs with an in-progress pvmove or
+ * lvconvert.
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_activate(lv_t lv);
+
+/**
+ * Deactivate a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * This function is the equivalent of the lvm command "lvchange -an".
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_deactivate(lv_t lv);
+
+/**
+ * Remove a logical volume from a volume group.
+ *
+ * \memberof lv_t
+ *
+ * This function commits the change to disk and does _not_ require calling
+ * lvm_vg_write().
+ * NOTE: The commit behavior of this function is subject to change
+ * as the API is developed.
+ * Currently only removing linear LVs are possible.
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_remove_lv(lv_t lv);
+
+/**
+ * Get the current name of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * The memory allocated for the uuid is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * Copy of the uuid string.
+ */
+const char *lvm_lv_get_uuid(const lv_t lv);
+
+/**
+ * Get the current uuid of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * The memory allocated for the name is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * Copy of the name.
+ */
+const char *lvm_lv_get_name(const lv_t lv);
+
+/**
+ * Get the current size in bytes of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_lv_get_size(const lv_t lv);
+
+/**
+ * Get the value of a LV property
+ *
+ * \memberof lv_t
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \param   name
+ * Name of property to query.  See lvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ *      lvm_property_value v;
+ *      char *prop_name = "seg_count";
+ *
+ *      v = lvm_lv_get_property(lv, prop_name);
+ *      if (!v.is_valid) {
+ *           printf("Invalid property name or unable to query"
+ *                  "'%s', errno = %d.\n", prop_name, lvm_errno(libh));
+ *           return;
+ *      }
+ *      if (v.is_string)
+ *           printf(", value = %s\n", v.value.string);
+ *     if (v.is_integer)
+ *           printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property.  Caller should check 'is_valid' flag before using
+ * the value.  If 'is_valid' is not set, caller should check lvm_errno()
+ * for specific error.
+ */
+struct lvm_property_value lvm_lv_get_property(const lv_t lv, const char *name);
+
+/**
+ * Get the value of a LV segment property
+ *
+ * \memberof lv_t
+ *
+ * \param   lvseg
+ * Logical volume segment handle.
+ *
+ * \param   name
+ * Name of property to query.  See lvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ *      lvm_property_value v;
+ *      char *prop_name = "seg_start_pe";
+ *
+ *      v = lvm_lvseg_get_property(lvseg, prop_name);
+ *      if (lvm_errno(libh) || !v.is_valid) {
+ *           // handle error
+ *           printf("Invalid property name or unable to query"
+ *                  "'%s'.\n", prop_name);
+ *           return;
+ *      }
+ *      if (v.is_string)
+ *           printf(", value = %s\n", v.value.string);
+ *     else
+ *           printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property.  Caller should check lvm_errno() as well
+ * as 'is_valid' flag before using the value.
+ */
+struct lvm_property_value lvm_lvseg_get_property(const lvseg_t lvseg,
+                                                const char *name);
+
+/**
+ * Get the current activation state of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * 1 if the LV is active in the kernel, 0 if not
+ */
+uint64_t lvm_lv_is_active(const lv_t lv);
+
+/**
+ * Get the current suspended state of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \return
+ * 1 if the LV is suspended in the kernel, 0 if not
+ */
+uint64_t lvm_lv_is_suspended(const lv_t lv);
+
+/**
+ * Add a tag to an LV.
+ *
+ * \memberof lv_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully adding a tag, use lvm_vg_write() to commit the
+ * new VG to disk.  Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \param   tag
+ * Tag to add to an LV.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_add_tag(lv_t lv, const char *tag);
+
+/**
+ * Remove a tag from an LV.
+ *
+ * \memberof lv_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully removing a tag, use lvm_vg_write() to commit the
+ * new VG to disk.  Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \param   tag
+ * Tag to remove from LV.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_remove_tag(lv_t lv, const char *tag);
+
+/**
+ * Return the list of logical volume tags.
+ *
+ * \memberof lv_t
+ *
+ * The memory allocated for the list is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * To process the list, use the dm_list iterator functions.  For example:
+ *      lv_t lv;
+ *      struct dm_list *tags;
+ *      struct lvm_str_list *strl;
+ *
+ *      tags = lvm_lv_get_tags(lv);
+ *     dm_list_iterate_items(strl, tags) {
+ *             tag = strl->str;
+ *              // do something with tag
+ *      }
+ *
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * tag strings attached to volume group.
+ * If no tags are attached to the LV, an empty list is returned
+ * (check with dm_list_empty()).
+ * If there is a problem obtaining the list of tags, NULL is returned.
+ */
+struct dm_list *lvm_lv_get_tags(const lv_t lv);
+
+
+/**
+ * Resize logical volume to new_size bytes.
+ *
+ * \memberof lv_t
+ *
+ * NOTE: This function is currently not implemented.
+ *
+ * \param   lv
+ * Logical volume handle.
+ *
+ * \param   new_size
+ * New size in bytes.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ *
+ */
+int lvm_lv_resize(const lv_t lv, uint64_t new_size);
+
+/************************** physical volume handling ************************/
+
+/**
+ * Physical volume handling should not be needed anymore. Only physical volumes
+ * bound to a vg contain useful information. Therefore the creation,
+ * modification and the removal of orphan physical volumes is not suported.
+ */
+
+/**
+ * Get the current uuid of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * The memory allocated for the uuid is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * Copy of the uuid string.
+ */
+const char *lvm_pv_get_uuid(const pv_t pv);
+
+/**
+ * Get the current name of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * The memory allocated for the name is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * Copy of the name.
+ */
+const char *lvm_pv_get_name(const pv_t pv);
+
+/**
+ * Get the current number of metadata areas in the physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * Number of metadata areas in the PV.
+ */
+uint64_t lvm_pv_get_mda_count(const pv_t pv);
+
+/**
+ * Get the current size in bytes of a device underlying a
+ * physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_pv_get_dev_size(const pv_t pv);
+
+/**
+ * Get the current size in bytes of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_pv_get_size(const pv_t pv);
+
+/**
+ * Get the current unallocated space in bytes of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * Free size in bytes.
+ */
+uint64_t lvm_pv_get_free(const pv_t pv);
+
+/**
+ * Get the value of a PV property
+ *
+ * \memberof pv_t
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \param   name
+ * Name of property to query.  See pvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t handle
+ * and will be released when lvm_vg_close() is called. For "percent" values
+ * (those obtained for copy_percent and snap_percent properties), please see
+ * percent_range_t and lvm_percent_to_float().
+ *
+ * Example:
+ *      lvm_property_value value;
+ *      char *prop_name = "pv_mda_count";
+ *
+ *      v = lvm_pv_get_property(pv, prop_name);
+ *      if (!v.is_valid) {
+ *           printf("Invalid property name or unable to query"
+ *                  "'%s', errno = %d.\n", prop_name, lvm_errno(libh));
+ *           return;
+ *      }
+ *      if (v.is_string)
+ *           printf(", value = %s\n", v.value.string);
+ *     if (v.is_integer)
+ *           printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property.  Caller should check 'is_valid' flag before using
+ * the value.  If 'is_valid' is not set, caller should check lvm_errno()
+ * for specific error.
+ */
+struct lvm_property_value lvm_pv_get_property(const pv_t pv, const char *name);
+
+/**
+ * Get the value of a PV segment property
+ *
+ * \memberof pv_t
+ *
+ * \param   pvseg
+ * Physical volume segment handle.
+ *
+ * \param   name
+ * Name of property to query.  See pvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ *      lvm_property_value v;
+ *      char *prop_name = "pvseg_start";
+ *
+ *      v = lvm_pvseg_get_property(pvseg, prop_name);
+ *      if (lvm_errno(libh) || !v.is_valid) {
+ *           // handle error
+ *           printf("Invalid property name or unable to query"
+ *                  "'%s'.\n", prop_name);
+ *           return;
+ *      }
+ *      if (v.is_string)
+ *           printf(", value = %s\n", v.value.string);
+ *     else
+ *           printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property.  Caller should check lvm_errno() as well
+ * as 'is_valid' flag before using the value.
+ */
+struct lvm_property_value lvm_pvseg_get_property(const pvseg_t pvseg,
+                                                const char *name);
+
+/**
+ * Return a list of pvseg handles for a given PV handle.
+ *
+ * \memberof pv_t
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \return
+ * A list of lvm_pvseg_list structures containing pvseg handles for this pv.
+ */
+struct dm_list *lvm_pv_list_pvsegs(pv_t pv);
+
+/**
+ * Lookup an PV handle in a VG by the PV name.
+ *
+ * \memberof pv_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   name
+ * Name of PV to lookup.
+ *
+ * \return
+ * non-NULL handle to the PV 'name' attached to the VG.
+ * NULL is returned if the PV name is not associated with the VG handle.
+ */
+pv_t lvm_pv_from_name(vg_t vg, const char *name);
+
+/**
+ * Lookup an PV handle in a VG by the PV uuid.
+ * The form of the uuid may be either the formatted, human-readable form,
+ * or the non-formatted form.
+ *
+ * \memberof pv_t
+ *
+ * \param   vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param   uuid
+ * UUID of PV to lookup.
+ *
+ * \return
+ * non-NULL handle to the PV with 'uuid' attached to the VG.
+ * NULL is returned if the PV uuid is not associated with the VG handle.
+ */
+pv_t lvm_pv_from_uuid(vg_t vg, const char *uuid);
+
+/**
+ * Resize physical volume to new_size bytes.
+ *
+ * \memberof pv_t
+ *
+ * NOTE: This function is currently not implemented.
+ *
+ * \param   pv
+ * Physical volume handle.
+ *
+ * \param   new_size
+ * New size in bytes.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_pv_resize(const pv_t pv, uint64_t new_size);
+
+#ifndef _LVM_PERCENT_H
+
+/**
+ * This type defines a couple of special percent values. The PERCENT_0 and
+ * PERCENT_100 constants designate *exact* percentages: values are never
+ * rounded to either of these two.
+ */
+typedef enum {
+       PERCENT_0 = 0,
+       PERCENT_1 = 1000000,
+       PERCENT_100 = 100 * PERCENT_1,
+       PERCENT_INVALID = -1
+} percent_range_t;
+
+typedef int32_t percent_t;
+
+#endif
+
+/**
+ * Convert a (fixed-point) value obtained from the percent-denominated
+ * *_get_property functions into a floating-point value.
+ */
+float lvm_percent_to_float(percent_t v);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LIB_LVM2APP_H */
diff --git a/liblvm/lvm_base.c b/liblvm/lvm_base.c
new file mode 100644 (file)
index 0000000..9e1a8ec
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "locking.h"
+#include "lvm-version.h"
+#include "metadata-exported.h"
+#include "lvm2app.h"
+
+const char *lvm_library_get_version(void)
+{
+       return LVM_VERSION;
+}
+
+lvm_t lvm_init(const char *system_dir)
+{
+       struct cmd_context *cmd;
+
+       /* FIXME: logging bound to handle
+        */
+
+       /* create context */
+       /* FIXME: split create_toolcontext */
+       /* FIXME: make all globals configurable */
+       cmd = create_toolcontext(0, system_dir);
+       if (!cmd)
+               return NULL;
+
+       if (stored_errno())
+               return (lvm_t) cmd;
+
+       /*
+        * FIXME: if an non memory error occured, return the cmd (maybe some
+        * cleanup needed).
+        */
+
+       /* initialization from lvm_run_command */
+       init_error_message_produced(0);
+
+       /* FIXME: locking_type config option needed? */
+       /* initialize locking */
+       if (!init_locking(-1, cmd, 0)) {
+               /* FIXME: use EAGAIN as error code here */
+               lvm_quit((lvm_t) cmd);
+               return NULL;
+       }
+       /*
+        * FIXME: Use cmd->cmd_line as audit trail for liblvm calls.  Used in
+        * archive() call.  Possible example:
+        * cmd_line = "lvm_vg_create: vg1\nlvm_vg_extend vg1 /dev/sda1\n"
+        */
+       cmd->cmd_line = "liblvm";
+
+       return (lvm_t) cmd;
+}
+
+void lvm_quit(lvm_t libh)
+{
+       destroy_toolcontext((struct cmd_context *)libh);
+}
+
+int lvm_config_reload(lvm_t libh)
+{
+       /* FIXME: re-init locking needed here? */
+       if (!refresh_toolcontext((struct cmd_context *)libh))
+               return -1;
+       return 0;
+}
+
+/*
+ * FIXME: submit a patch to document the --config option
+ */
+int lvm_config_override(lvm_t libh, const char *config_settings)
+{
+       struct cmd_context *cmd = (struct cmd_context *)libh;
+       if (override_config_tree_from_string(cmd, config_settings))
+               return -1;
+       return 0;
+}
+
+int lvm_errno(lvm_t libh)
+{
+       return stored_errno();
+}
+
+const char *lvm_errmsg(lvm_t libh)
+{
+       return stored_errmsg();
+}
+
+const char *lvm_vgname_from_pvid(lvm_t libh, const char *pvid)
+{
+       struct cmd_context *cmd = (struct cmd_context *)libh;
+       struct id id;
+
+       if (!id_read_format(&id, pvid)) {
+               log_error(INTERNAL_ERROR "Unable to convert uuid");
+               return NULL;
+       }
+       return find_vgname_from_pvid(cmd, (char *)id.uuid);
+}
+
+const char *lvm_vgname_from_device(lvm_t libh, const char *device)
+{
+       struct cmd_context *cmd = (struct cmd_context *)libh;
+       return find_vgname_from_pvname(cmd, device);
+}
diff --git a/liblvm/lvm_lv.c b/liblvm/lvm_lv.c
new file mode 100644 (file)
index 0000000..b77f78c
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "lvm-string.h"
+#include "defaults.h"
+#include "segtype.h"
+#include "locking.h"
+#include "activate.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+static int _lv_check_handle(const lv_t lv, const int vg_writeable)
+{
+       if (!lv || !lv->vg || vg_read_error(lv->vg))
+               return -1;
+       if (vg_writeable && !vg_check_write_mode(lv->vg))
+               return -1;
+       return 0;
+}
+
+/* FIXME: have lib/report/report.c _disp function call lv_size()? */
+uint64_t lvm_lv_get_size(const lv_t lv)
+{
+       return SECTOR_SIZE * lv_size(lv);
+}
+
+const char *lvm_lv_get_uuid(const lv_t lv)
+{
+       return lv_uuid_dup(lv);
+}
+
+const char *lvm_lv_get_name(const lv_t lv)
+{
+       return dm_pool_strndup(lv->vg->vgmem, (const char *)lv->name,
+                              NAME_LEN+1);
+}
+
+struct lvm_property_value lvm_lv_get_property(const lv_t lv, const char *name)
+{
+       return get_property(NULL, NULL, lv, NULL, NULL, name);
+}
+
+struct lvm_property_value lvm_lvseg_get_property(const lvseg_t lvseg,
+                                                const char *name)
+{
+       return get_property(NULL, NULL, NULL, lvseg, NULL, name);
+}
+
+uint64_t lvm_lv_is_active(const lv_t lv)
+{
+       struct lvinfo info;
+       if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) &&
+           info.exists && info.live_table)
+               return 1;
+       return 0;
+}
+
+uint64_t lvm_lv_is_suspended(const lv_t lv)
+{
+       struct lvinfo info;
+       if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) &&
+           info.exists && info.suspended)
+               return 1;
+       return 0;
+}
+
+int lvm_lv_add_tag(lv_t lv, const char *tag)
+{
+       if (_lv_check_handle(lv, 1))
+               return -1;
+       if (!lv_change_tag(lv, tag, 1))
+               return -1;
+       return 0;
+}
+
+
+int lvm_lv_remove_tag(lv_t lv, const char *tag)
+{
+       if (_lv_check_handle(lv, 1))
+               return -1;
+       if (!lv_change_tag(lv, tag, 0))
+               return -1;
+       return 0;
+}
+
+
+struct dm_list *lvm_lv_get_tags(const lv_t lv)
+{
+       return tag_list_copy(lv->vg->vgmem, &lv->tags);
+}
+
+/* Set defaults for non-segment specific LV parameters */
+static void _lv_set_default_params(struct lvcreate_params *lp,
+                                  vg_t vg, const char *lvname,
+                                  uint64_t extents)
+{
+       lp->zero = 1;
+       lp->major = -1;
+       lp->minor = -1;
+       lp->activation_monitoring = DEFAULT_DMEVENTD_MONITOR;
+       lp->vg_name = vg->name;
+       lp->lv_name = lvname; /* FIXME: check this for safety */
+       lp->pvh = &vg->pvs;
+
+       lp->extents = extents;
+       lp->permission = LVM_READ | LVM_WRITE;
+       lp->read_ahead = DM_READ_AHEAD_NONE;
+       lp->alloc = ALLOC_INHERIT;
+       dm_list_init(&lp->tags);
+}
+
+/* Set default for linear segment specific LV parameters */
+static void _lv_set_default_linear_params(struct cmd_context *cmd,
+                                         struct lvcreate_params *lp)
+{
+       lp->segtype = get_segtype_from_string(cmd, "striped");
+       lp->stripes = 1;
+       lp->stripe_size = DEFAULT_STRIPESIZE * 2;
+}
+
+/*
+ * FIXME: This function should probably not commit to disk but require calling
+ * lvm_vg_write.  However, this appears to be non-trivial change until
+ * lv_create_single is refactored by segtype.
+ */
+lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size)
+{
+       struct lvcreate_params lp;
+       uint64_t extents;
+       struct lv_list *lvl;
+
+       if (vg_read_error(vg))
+               return NULL;
+       if (!vg_check_write_mode(vg))
+               return NULL;
+       memset(&lp, 0, sizeof(lp));
+       extents = extents_from_size(vg->cmd, size / SECTOR_SIZE,
+                                   vg->extent_size);
+       _lv_set_default_params(&lp, vg, name, extents);
+       _lv_set_default_linear_params(vg->cmd, &lp);
+       if (!lv_create_single(vg, &lp))
+               return NULL;
+       lvl = find_lv_in_vg(vg, name);
+       if (!lvl)
+               return NULL;
+       return (lv_t) lvl->lv;
+}
+
+/*
+ * FIXME: This function should probably not commit to disk but require calling
+ * lvm_vg_write.
+ */
+int lvm_vg_remove_lv(lv_t lv)
+{
+       if (!lv || !lv->vg || vg_read_error(lv->vg))
+               return -1;
+       if (!vg_check_write_mode(lv->vg))
+               return -1;
+       if (!lv_remove_single(lv->vg->cmd, lv, DONT_PROMPT))
+               return -1;
+       return 0;
+}
+
+int lvm_lv_activate(lv_t lv)
+{
+       if (!lv || !lv->vg || vg_read_error(lv->vg) || !lv->vg->cmd)
+               return -1;
+
+       /* FIXME: handle pvmove stuff later */
+       if (lv->status & LOCKED) {
+               log_error("Unable to activate locked LV");
+               return -1;
+       }
+
+       /* FIXME: handle lvconvert stuff later */
+       if (lv->status & CONVERTING) {
+               log_error("Unable to activate LV with in-progress lvconvert");
+               return -1;
+       }
+
+       if (lv_is_origin(lv)) {
+               log_verbose("Activating logical volume \"%s\" "
+                           "exclusively", lv->name);
+               if (!activate_lv_excl(lv->vg->cmd, lv)) {
+                       log_error("Activate exclusive failed.");
+                       return -1;
+               }
+       } else {
+               log_verbose("Activating logical volume \"%s\"",
+                           lv->name);
+               if (!activate_lv(lv->vg->cmd, lv)) {
+                       log_error("Activate failed.");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int lvm_lv_deactivate(lv_t lv)
+{
+       if (!lv || !lv->vg || vg_read_error(lv->vg) || !lv->vg->cmd)
+               return -1;
+
+       log_verbose("Deactivating logical volume \"%s\"", lv->name);
+       if (!deactivate_lv(lv->vg->cmd, lv)) {
+               log_error("Deactivate failed.");
+               return -1;
+       }
+       return 0;
+}
+
+struct dm_list *lvm_lv_list_lvsegs(lv_t lv)
+{
+       struct dm_list *list;
+       lvseg_list_t *lvseg;
+       struct lv_segment *lvl;
+
+       if (dm_list_empty(&lv->segments))
+               return NULL;
+
+       if (!(list = dm_pool_zalloc(lv->vg->vgmem, sizeof(*list)))) {
+               log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+               return NULL;
+       }
+       dm_list_init(list);
+
+       dm_list_iterate_items(lvl, &lv->segments) {
+               if (!(lvseg = dm_pool_zalloc(lv->vg->vgmem, sizeof(*lvseg)))) {
+                       log_errno(ENOMEM,
+                               "Memory allocation fail for lvm_lvseg_list.");
+                       return NULL;
+               }
+               lvseg->lvseg = lvl;
+               dm_list_add(list, &lvseg->list);
+       }
+       return list;
+}
+
+lv_t lvm_lv_from_name(vg_t vg, const char *name)
+{
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (!strcmp(name, lvl->lv->name))
+                       return lvl->lv;
+       }
+       return NULL;
+}
+
+lv_t lvm_lv_from_uuid(vg_t vg, const char *uuid)
+{
+       struct lv_list *lvl;
+       struct id id;
+
+       if (strlen(uuid) < ID_LEN) {
+               log_errno (EINVAL, "Invalid UUID string length");
+               return NULL;
+       }
+       if (strlen(uuid) >= ID_LEN) {
+               if (!id_read_format(&id, uuid)) {
+                       log_errno(EINVAL, "Invalid UUID format");
+                       return NULL;
+               }
+       }
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (id_equal(&vg->id, &lvl->lv->lvid.id[0]) &&
+                   id_equal(&id, &lvl->lv->lvid.id[1]))
+                       return lvl->lv;
+       }
+       return NULL;
+}
+int lvm_lv_resize(const lv_t lv, uint64_t new_size)
+{
+       /* FIXME: add lv resize code here */
+       log_error("NOT IMPLEMENTED YET");
+       return -1;
+}
diff --git a/liblvm/lvm_misc.c b/liblvm/lvm_misc.c
new file mode 100644 (file)
index 0000000..62fef61
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008,2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "properties.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+struct dm_list *tag_list_copy(struct dm_pool *p, struct dm_list *tag_list)
+{
+       struct dm_list *list;
+       lvm_str_list_t *lsl;
+       struct str_list *sl;
+
+       if (!(list = dm_pool_zalloc(p, sizeof(*list)))) {
+               log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+               return NULL;
+       }
+       dm_list_init(list);
+
+       dm_list_iterate_items(sl, tag_list) {
+               if (!(lsl = dm_pool_zalloc(p, sizeof(*lsl)))) {
+                       log_errno(ENOMEM,
+                               "Memory allocation fail for lvm_lv_list.");
+                       return NULL;
+               }
+               if (!(lsl->str = dm_pool_strdup(p, sl->str))) {
+                       log_errno(ENOMEM,
+                               "Memory allocation fail for lvm_lv_list->str.");
+                       return NULL;
+               }
+               dm_list_add(list, &lsl->list);
+       }
+       return list;
+}
+
+struct lvm_property_value get_property(const pv_t pv, const vg_t vg,
+                                      const lv_t lv, const lvseg_t lvseg,
+                                      const pvseg_t pvseg, const char *name)
+{
+       struct lvm_property_type prop;
+       struct lvm_property_value v;
+
+       prop.id = name;
+       if (pv) {
+               if (!pv_get_property(pv, &prop)) {
+                       v.is_valid = 0;
+                       return v;
+               }
+       } else if (vg) {
+               if (!vg_get_property(vg, &prop)) {
+                       v.is_valid = 0;
+                       return v;
+               }
+       } else if (lv) {
+               if (!lv_get_property(lv, &prop)) {
+                       v.is_valid = 0;
+                       return v;
+               }
+       } else if (lvseg) {
+               if (!lvseg_get_property(lvseg, &prop)) {
+                       v.is_valid = 0;
+                       return v;
+               }
+       } else if (pvseg) {
+               if (!pvseg_get_property(pvseg, &prop)) {
+                       v.is_valid = 0;
+                       return v;
+               }
+       }
+       v.is_settable = prop.is_settable;
+       v.is_string = prop.is_string;
+       v.is_integer = prop.is_integer;
+       if (v.is_string)
+               v.value.string = prop.value.string;
+       if (v.is_integer)
+               v.value.integer = prop.value.integer;
+       v.is_valid = 1;
+       return v;
+}
+
+
+int set_property(const pv_t pv, const vg_t vg, const lv_t lv,
+                const char *name, struct lvm_property_value *v)
+{
+       struct lvm_property_type prop;
+
+       prop.id = name;
+       if (v->is_string)
+               prop.value.string = v->value.string;
+       else
+               prop.value.integer = v->value.integer;
+       if (pv) {
+               if (!pv_set_property(pv, &prop)) {
+                       v->is_valid = 0;
+                       return -1;
+               }
+       } else if (vg) {
+               if (!vg_set_property(vg, &prop)) {
+                       v->is_valid = 0;
+                       return -1;
+               }
+       } else if (lv) {
+               if (!lv_set_property(lv, &prop)) {
+                       v->is_valid = 0;
+                       return -1;
+               }
+       }
+       return 0;
+}
diff --git a/liblvm/lvm_misc.h b/liblvm/lvm_misc.h
new file mode 100644 (file)
index 0000000..a0324b8
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008,2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#ifndef _LVM2APP_MISC_H
+#define _LVM2APP_MISC_H
+
+#include "libdevmapper.h"
+#include "lvm2app.h"
+
+struct dm_list *tag_list_copy(struct dm_pool *p, struct dm_list *tag_list);
+struct lvm_property_value get_property(const pv_t pv, const vg_t vg,
+                                      const lv_t lv, const lvseg_t lvseg,
+                                      const pvseg_t pvseg, const char *name);
+int set_property(const pv_t pv, const vg_t vg, const lv_t lv,
+                const char *name, struct lvm_property_value *value);
+
+#endif
diff --git a/liblvm/lvm_pv.c b/liblvm/lvm_pv.c
new file mode 100644 (file)
index 0000000..4fe2d3e
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "lvm-string.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+const char *lvm_pv_get_uuid(const pv_t pv)
+{
+       return pv_uuid_dup(pv);
+}
+
+const char *lvm_pv_get_name(const pv_t pv)
+{
+       return dm_pool_strndup(pv->vg->vgmem,
+                              (const char *)pv_dev_name(pv), NAME_LEN + 1);
+}
+
+uint64_t lvm_pv_get_mda_count(const pv_t pv)
+{
+       return (uint64_t) pv_mda_count(pv);
+}
+
+uint64_t lvm_pv_get_dev_size(const pv_t pv)
+{
+       return (uint64_t) SECTOR_SIZE * pv_dev_size(pv);
+}
+
+uint64_t lvm_pv_get_size(const pv_t pv)
+{
+       return (uint64_t) SECTOR_SIZE * pv_size_field(pv);
+}
+
+uint64_t lvm_pv_get_free(const pv_t pv)
+{
+       return (uint64_t) SECTOR_SIZE * pv_free(pv);
+}
+
+struct lvm_property_value lvm_pv_get_property(const pv_t pv, const char *name)
+{
+       return get_property(pv, NULL, NULL, NULL, NULL, name);
+}
+
+struct lvm_property_value lvm_pvseg_get_property(const pvseg_t pvseg,
+                                                const char *name)
+{
+       return get_property(NULL, NULL, NULL, NULL, pvseg, name);
+}
+
+struct dm_list *lvm_pv_list_pvsegs(pv_t pv)
+{
+       struct dm_list *list;
+       pvseg_list_t *pvseg;
+       struct pv_segment *pvl;
+
+       if (dm_list_empty(&pv->segments))
+               return NULL;
+
+       if (!(list = dm_pool_zalloc(pv->vg->vgmem, sizeof(*list)))) {
+               log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+               return NULL;
+       }
+       dm_list_init(list);
+
+       dm_list_iterate_items(pvl, &pv->segments) {
+               if (!(pvseg = dm_pool_zalloc(pv->vg->vgmem, sizeof(*pvseg)))) {
+                       log_errno(ENOMEM,
+                               "Memory allocation fail for lvm_pvseg_list.");
+                       return NULL;
+               }
+               pvseg->pvseg = pvl;
+               dm_list_add(list, &pvseg->list);
+       }
+       return list;
+}
+
+pv_t lvm_pv_from_name(vg_t vg, const char *name)
+{
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (!strcmp(name, pv_dev_name(pvl->pv)))
+                       return pvl->pv;
+       }
+       return NULL;
+}
+
+pv_t lvm_pv_from_uuid(vg_t vg, const char *uuid)
+{
+       struct pv_list *pvl;
+       struct id id;
+
+       if (strlen(uuid) < ID_LEN) {
+               log_errno (EINVAL, "Invalid UUID string length");
+               return NULL;
+       }
+       if (strlen(uuid) >= ID_LEN) {
+               if (!id_read_format(&id, uuid)) {
+                       log_errno(EINVAL, "Invalid UUID format");
+                       return NULL;
+               }
+       }
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (id_equal(&id, &pvl->pv->id))
+                       return pvl->pv;
+       }
+       return NULL;
+}
+
+
+int lvm_pv_resize(const pv_t pv, uint64_t new_size)
+{
+       /* FIXME: add pv resize code here */
+       log_error("NOT IMPLEMENTED YET");
+       return -1;
+}
diff --git a/liblvm/lvm_vg.c b/liblvm/lvm_vg.c
new file mode 100644 (file)
index 0000000..f087ba4
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "archiver.h"
+#include "locking.h"
+#include "lvmcache.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+int lvm_vg_add_tag(vg_t vg, const char *tag)
+{
+       if (vg_read_error(vg))
+               return -1;
+
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (!vg_change_tag(vg, tag, 1))
+               return -1;
+       return 0;
+}
+
+
+int lvm_vg_remove_tag(vg_t vg, const char *tag)
+{
+       if (vg_read_error(vg))
+               return -1;
+
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (!vg_change_tag(vg, tag, 0))
+               return -1;
+       return 0;
+}
+
+
+vg_t lvm_vg_create(lvm_t libh, const char *vg_name)
+{
+       struct volume_group *vg;
+
+       vg = vg_create((struct cmd_context *)libh, vg_name);
+       /* FIXME: error handling is still TBD */
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               return NULL;
+       }
+       vg->open_mode = 'w';
+       return (vg_t) vg;
+}
+
+int lvm_vg_extend(vg_t vg, const char *device)
+{
+       struct pvcreate_params pp;
+
+       if (vg_read_error(vg))
+               return -1;
+
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+               log_error("Can't get lock for orphan PVs");
+               return -1;
+       }
+
+       pvcreate_params_set_defaults(&pp);
+       if (!vg_extend(vg, 1, (char **) &device, &pp)) {
+               unlock_vg(vg->cmd, VG_ORPHANS);
+               return -1;
+       }
+       /*
+        * FIXME: Either commit to disk, or keep holding VG_ORPHANS and
+        * release in lvm_vg_close().
+        */
+       unlock_vg(vg->cmd, VG_ORPHANS);
+       return 0;
+}
+
+int lvm_vg_reduce(vg_t vg, const char *device)
+{
+       if (vg_read_error(vg))
+               return -1;
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (!vg_reduce(vg, (char *)device))
+               return -1;
+       return 0;
+}
+
+int lvm_vg_set_extent_size(vg_t vg, uint32_t new_size)
+{
+       if (vg_read_error(vg))
+               return -1;
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (!vg_set_extent_size(vg, new_size / SECTOR_SIZE))
+               return -1;
+       return 0;
+}
+
+int lvm_vg_write(vg_t vg)
+{
+       struct pv_list *pvl;
+
+       if (vg_read_error(vg))
+               return -1;
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (dm_list_empty(&vg->pvs)) {
+               if (!vg_remove(vg))
+                       return -1;
+               return 0;
+       }
+
+       if (! dm_list_empty(&vg->removed_pvs)) {
+               if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+                       log_error("Can't get lock for orphan PVs");
+                       return 0;
+               }
+       }
+
+       if (!archive(vg))
+               return -1;
+
+       /* Store VG on disk(s) */
+       if (!vg_write(vg) || !vg_commit(vg))
+               return -1;
+
+       if (! dm_list_empty(&vg->removed_pvs)) {
+               dm_list_iterate_items(pvl, &vg->removed_pvs) {
+                       pv_write_orphan(vg->cmd, pvl->pv);
+                       /* FIXME: do pvremove / label_remove()? */
+               }
+               dm_list_init(&vg->removed_pvs);
+               unlock_vg(vg->cmd, VG_ORPHANS);
+       }
+
+       return 0;
+}
+
+int lvm_vg_close(vg_t vg)
+{
+       if (vg_read_error(vg) == FAILED_LOCKING)
+               free_vg(vg);
+       else
+               unlock_and_free_vg(vg->cmd, vg, vg->name);
+       return 0;
+}
+
+int lvm_vg_remove(vg_t vg)
+{
+       if (vg_read_error(vg))
+               return -1;
+       if (!vg_check_write_mode(vg))
+               return -1;
+
+       if (!vg_remove_check(vg))
+               return -1;
+
+       vg_remove_pvs(vg);
+
+       return 0;
+}
+
+vg_t lvm_vg_open(lvm_t libh, const char *vgname, const char *mode,
+                 uint32_t flags)
+{
+       uint32_t internal_flags = 0;
+       struct volume_group *vg;
+
+       if (!strncmp(mode, "w", 1))
+               internal_flags |= READ_FOR_UPDATE;
+       else if (strncmp(mode, "r", 1)) {
+               log_errno(EINVAL, "Invalid VG open mode");
+               return NULL;
+       }
+
+       vg = vg_read((struct cmd_context *)libh, vgname, NULL, internal_flags);
+       if (vg_read_error(vg)) {
+               /* FIXME: use log_errno either here in inside vg_read */
+               free_vg(vg);
+               return NULL;
+       }
+       /* FIXME: combine this with locking ? */
+       vg->open_mode = mode[0];
+
+       return (vg_t) vg;
+}
+
+struct dm_list *lvm_vg_list_pvs(vg_t vg)
+{
+       struct dm_list *list;
+       pv_list_t *pvs;
+       struct pv_list *pvl;
+
+       if (dm_list_empty(&vg->pvs))
+               return NULL;
+
+       if (!(list = dm_pool_zalloc(vg->vgmem, sizeof(*list)))) {
+               log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+               return NULL;
+       }
+       dm_list_init(list);
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (!(pvs = dm_pool_zalloc(vg->vgmem, sizeof(*pvs)))) {
+                       log_errno(ENOMEM,
+                               "Memory allocation fail for lvm_pv_list.");
+                       return NULL;
+               }
+               pvs->pv = pvl->pv;
+               dm_list_add(list, &pvs->list);
+       }
+       return list;
+}
+
+struct dm_list *lvm_vg_list_lvs(vg_t vg)
+{
+       struct dm_list *list;
+       lv_list_t *lvs;
+       struct lv_list *lvl;
+
+       if (dm_list_empty(&vg->lvs))
+               return NULL;
+
+       if (!(list = dm_pool_zalloc(vg->vgmem, sizeof(*list)))) {
+               log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+               return NULL;
+       }
+       dm_list_init(list);
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (!(lvs = dm_pool_zalloc(vg->vgmem, sizeof(*lvs)))) {
+                       log_errno(ENOMEM,
+                               "Memory allocation fail for lvm_lv_list.");
+                       return NULL;
+               }
+               lvs->lv = lvl->lv;
+               dm_list_add(list, &lvs->list);
+       }
+       return list;
+}
+
+struct dm_list *lvm_vg_get_tags(const vg_t vg)
+{
+       return tag_list_copy(vg->vgmem, &vg->tags);
+}
+
+uint64_t lvm_vg_get_seqno(const vg_t vg)
+{
+       return vg_seqno(vg);
+}
+
+uint64_t lvm_vg_is_clustered(const vg_t vg)
+{
+       return vg_is_clustered(vg);
+}
+
+uint64_t lvm_vg_is_exported(const vg_t vg)
+{
+       return vg_is_exported(vg);
+}
+
+uint64_t lvm_vg_is_partial(const vg_t vg)
+{
+       return (vg_missing_pv_count(vg) != 0);
+}
+
+/* FIXME: invalid handle? return INTMAX? */
+uint64_t lvm_vg_get_size(const vg_t vg)
+{
+       return SECTOR_SIZE * vg_size(vg);
+}
+
+uint64_t lvm_vg_get_free_size(const vg_t vg)
+{
+       return SECTOR_SIZE * vg_free(vg);
+}
+
+uint64_t lvm_vg_get_extent_size(const vg_t vg)
+{
+       return SECTOR_SIZE * vg_extent_size(vg);
+}
+
+uint64_t lvm_vg_get_extent_count(const vg_t vg)
+{
+       return vg_extent_count(vg);
+}
+
+uint64_t lvm_vg_get_free_extent_count(const vg_t vg)
+{
+       return vg_free_count(vg);
+}
+
+uint64_t lvm_vg_get_pv_count(const vg_t vg)
+{
+       return vg_pv_count(vg);
+}
+
+uint64_t lvm_vg_get_max_pv(const vg_t vg)
+{
+       return vg_max_pv(vg);
+}
+
+uint64_t lvm_vg_get_max_lv(const vg_t vg)
+{
+       return vg_max_lv(vg);
+}
+
+const char *lvm_vg_get_uuid(const vg_t vg)
+{
+       return vg_uuid_dup(vg);
+}
+
+const char *lvm_vg_get_name(const vg_t vg)
+{
+       return dm_pool_strndup(vg->vgmem, (const char *)vg->name, NAME_LEN+1);
+}
+
+
+struct lvm_property_value lvm_vg_get_property(const vg_t vg, const char *name)
+{
+       return get_property(NULL, vg, NULL, NULL, NULL, name);
+}
+
+int lvm_vg_set_property(const vg_t vg, const char *name,
+                       struct lvm_property_value *value)
+{
+       return set_property(NULL, vg, NULL, name, value);
+}
+
+struct dm_list *lvm_list_vg_names(lvm_t libh)
+{
+       return get_vgnames((struct cmd_context *)libh, 0);
+}
+
+struct dm_list *lvm_list_vg_uuids(lvm_t libh)
+{
+       return get_vgids((struct cmd_context *)libh, 0);
+}
+
+/*
+ * FIXME: Elaborate on when to use, side-effects, .cache file, etc
+ */
+int lvm_scan(lvm_t libh)
+{
+       if (!lvmcache_label_scan((struct cmd_context *)libh, 2))
+               return -1;
+       return 0;
+}
diff --git a/make.tmpl.in b/make.tmpl.in
new file mode 100644 (file)
index 0000000..806f77a
--- /dev/null
@@ -0,0 +1,390 @@
+# @configure_input@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+SHELL = /bin/sh
+
+@SET_MAKE@
+
+CC ?= @CC@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+LCOV = @LCOV@
+GENHTML = @GENHTML@
+LN_S = @LN_S@
+SED = @SED@
+CFLOW_CMD = @CFLOW_CMD@
+AWK = @AWK@
+
+LIBS = @LIBS@
+# Extra libraries always linked with static binaries
+STATIC_LIBS = $(SELINUX_LIBS) $(UDEV_LIBS)
+DEFS += @DEFS@
+CFLAGS += @CFLAGS@
+CLDFLAGS += @CLDFLAGS@
+LDDEPS += @LDDEPS@
+LDFLAGS += @LDFLAGS@
+LIB_SUFFIX = @LIB_SUFFIX@
+LVMINTERNAL_LIBS = -llvm-internal $(DL_LIBS)
+DL_LIBS = @DL_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+READLINE_LIBS = @READLINE_LIBS@
+SELINUX_LIBS = @SELINUX_LIBS@
+UDEV_LIBS = @UDEV_LIBS@
+TESTING = @TESTING@
+
+# Setup directory variables
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+udev_prefix = @udev_prefix@
+bindir = $(DESTDIR)@bindir@
+confdir = $(DESTDIR)@CONFDIR@/lvm
+includedir = $(DESTDIR)@includedir@
+libdir = $(DESTDIR)@libdir@
+usrlibdir = $(DESTDIR)@usrlibdir@
+sbindir = $(DESTDIR)@sbindir@
+usrsbindir = $(DESTDIR)@usrsbindir@
+datarootdir = $(DESTDIR)@datarootdir@
+infodir = $(datarootdir)/info
+mandir = $(datarootdir)/man
+localedir = $(DESTDIR)@LOCALEDIR@
+staticdir = $(DESTDIR)@STATICDIR@
+udevdir = $(DESTDIR)@udevdir@
+pkgconfigdir = $(usrlibdir)/pkgconfig
+initdir = $(DESTDIR)@sysconfdir@/rc.d/init.d
+ocf_scriptdir = $(DESTDIR)@prefix@/usr/lib/ocf/resource.d/lvm2
+
+USRLIB_RELPATH = $(shell echo $(abspath $(usrlibdir) $(libdir)) | \
+  $(AWK) -f $(top_srcdir)/scripts/relpath.awk)
+
+DEFAULT_SYS_DIR = @DEFAULT_SYS_DIR@
+DEFAULT_ARCHIVE_DIR = $(DEFAULT_SYS_DIR)/@DEFAULT_ARCHIVE_SUBDIR@
+DEFAULT_BACKUP_DIR = $(DEFAULT_SYS_DIR)/@DEFAULT_BACKUP_SUBDIR@
+DEFAULT_CACHE_DIR = $(DEFAULT_SYS_DIR)/@DEFAULT_CACHE_SUBDIR@
+DEFAULT_LOCK_DIR = @DEFAULT_LOCK_DIR@
+DEFAULT_RUN_DIR = @DEFAULT_RUN_DIR@
+
+# Setup vpath search paths for some suffixes
+vpath %.c $(srcdir)
+vpath %.in $(srcdir)
+vpath %.po $(srcdir)
+vpath %.exported_symbols $(srcdir)
+
+interface = @interface@
+interfacebuilddir = $(top_builddir)/libdm/$(interface)
+
+# The number of jobs to run, if blank, defaults to the make standard
+ifndef MAKEFLAGS
+MAKEFLAGS = @JOBS@
+endif
+
+# Handle installation of files
+ifeq ("@WRITE_INSTALL@", "yes")
+# leaving defaults
+M_INSTALL_SCRIPT =
+M_INSTALL_DATA = -m 644
+else
+M_INSTALL_PROGRAM = -m 555
+M_INSTALL_DATA = -m 444
+endif
+INSTALL_PROGRAM = $(INSTALL) $(M_INSTALL_PROGRAM) $(STRIP)
+INSTALL_DATA = $(INSTALL) -p $(M_INSTALL_DATA)
+INSTALL_WDATA = $(INSTALL) -p -m 644
+
+INSTALL_DIR = $(INSTALL) -m 0755 -d
+INSTALL_ROOT_DIR = $(INSTALL) -m 0700 -d
+INSTALL_ROOT_DATA = $(INSTALL) -m 0600
+INSTALL_SCRIPT = $(INSTALL) -p $(M_INSTALL_PROGRAM)
+
+.SUFFIXES: .c .d .o .so .a .po .pot .mo .dylib
+
+CFLAGS += -fPIC -Wall -Wundef -Wshadow -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -Wmissing-noreturn -Wformat-security -Wredundant-decls
+
+#CFLAGS += -W -Wconversion -Wpointer-arith -Wbad-function-cast -Wcast-qual
+#CFLAGS += -pedantic -std=gnu99
+#CFLAGS += -DDEBUG_CRC32
+
+CFLAGS += @COPTIMISE_FLAG@
+
+ifeq ("@DEBUG@", "yes")
+  CFLAGS += -g -fno-omit-frame-pointer
+  DEFS += -DDEBUG
+  # memory debugging is not thread-safe yet
+  ifneq ("@DMEVENTD@", "yes")
+    DEFS += -DDEBUG_MEM
+  endif
+endif
+
+ifeq ("@INTL@", "yes")
+  DEFS += -DINTL_PACKAGE=\"@INTL_PACKAGE@\" -DLOCALEDIR=\"@LOCALEDIR@\"
+endif
+
+LDFLAGS += -L$(top_builddir)/libdm -L$(top_builddir)/lib
+CLDFLAGS += -L$(top_builddir)/libdm -L$(top_builddir)/lib
+
+ifeq ("@DMEVENTD@", "yes")
+  LDFLAGS += -L$(top_builddir)/daemons/dmeventd
+  CLDFLAGS += -L$(top_builddir)/daemons/dmeventd
+endif
+
+ifeq ("@DM_COMPAT@", "yes")
+  DEFS += -DDM_COMPAT
+endif
+
+ifeq ("@DM_IOCTLS@", "yes")
+  DEFS += -DDM_IOCTLS
+endif
+
+#DEFS += -DDEBUG_POOL
+#DEFS += -DBOUNDS_CHECK
+
+#CFLAGS += -pg
+#LDFLAGS += -pg
+
+STRIP=
+#STRIP = -s
+
+LVM_VERSION := $(shell cat $(top_srcdir)/VERSION)
+
+LIB_VERSION_LVM := $(shell $(AWK) -F '.' '{printf "%s.%s",$$1,$$2}' $(top_srcdir)/VERSION)
+
+LIB_VERSION_DM := $(shell $(AWK) -F '.' '{printf "%s.%s",$$1,$$2}' $(top_srcdir)/VERSION_DM)
+
+LIB_VERSION_APP := $(shell $(AWK) -F '[(). ]' '{printf "%s.%s",$$1,$$4}' $(top_srcdir)/VERSION)
+
+INCLUDES += -I. -I$(top_builddir)/include
+
+INC_LNS = $(top_builddir)/include/.symlinks_created
+
+DEPS = $(top_builddir)/make.tmpl $(top_srcdir)/VERSION \
+       $(top_builddir)/Makefile $(INC_LNS)
+
+OBJECTS = $(SOURCES:%.c=%.o)
+POTFILES = $(SOURCES:%.c=%.pot)
+
+.PHONY: all pofile distclean clean cleandir cflow device-mapper
+.PHONY: install install_cluster install_device-mapper install_lvm2
+.PHONY: install_lib_shared install_dm_plugin install_lvm2_plugin
+.PHONY: install_ocf
+.PHONY: $(SUBDIRS) $(SUBDIRS.install) $(SUBDIRS.clean) $(SUBDIRS.distclean)
+.PHONY: $(SUBDIRS.pofile) $(SUBDIRS.install_cluster) $(SUBDIRS.cflow)
+.PHONY: $(SUBDIRS.device-mapper) $(SUBDIRS.install-device-mapper)
+
+SUBDIRS.device-mapper := $(SUBDIRS:=.device-mapper)
+SUBDIRS.install := $(SUBDIRS:=.install)
+SUBDIRS.install_cluster := $(SUBDIRS:=.install_cluster)
+SUBDIRS.install_device-mapper := $(SUBDIRS:=.install_device-mapper)
+SUBDIRS.install_lvm2 := $(SUBDIRS:=.install_lvm2)
+SUBDIRS.install_ocf := $(SUBDIRS:=.install_ocf)
+SUBDIRS.pofile := $(SUBDIRS:=.pofile)
+SUBDIRS.cflow := $(SUBDIRS:=.cflow)
+SUBDIRS.clean := $(SUBDIRS:=.clean)
+SUBDIRS.distclean := $(SUBDIRS:=.distclean)
+
+TARGETS += $(LIB_SHARED) $(LIB_STATIC)
+
+all: $(SUBDIRS) $(TARGETS)
+
+install: all $(SUBDIRS.install)
+install_cluster: all $(SUBDIRS.install_cluster)
+install_device-mapper: $(SUBDIRS.install_device-mapper)
+install_lvm2: $(SUBDIRS.install_lvm2)
+install_ocf: $(SUBDIRS.install_ocf)
+cflow: $(SUBDIRS.cflow)
+
+$(SUBDIRS): $(SUBDIRS.device-mapper)
+       $(MAKE) -C $@
+
+$(SUBDIRS.device-mapper):
+       $(MAKE) -C $(@:.device-mapper=) device-mapper
+
+$(SUBDIRS.install): $(SUBDIRS)
+       $(MAKE) -C $(@:.install=) install
+
+$(SUBDIRS.install_cluster): $(SUBDIRS)
+       $(MAKE) -C $(@:.install_cluster=) install_cluster
+
+$(SUBDIRS.install_device-mapper): device-mapper
+       $(MAKE) -C $(@:.install_device-mapper=) install_device-mapper
+
+$(SUBDIRS.install_lvm2): $(SUBDIRS)
+       $(MAKE) -C $(@:.install_lvm2=) install_lvm2
+
+$(SUBDIRS.install_ocf):
+       $(MAKE) -C $(@:.install_ocf=) install_ocf
+
+$(SUBDIRS.clean):
+       -$(MAKE) -C $(@:.clean=) clean
+
+$(SUBDIRS.distclean):
+       -$(MAKE) -C $(@:.distclean=) distclean
+
+$(SUBDIRS.cflow):
+       $(MAKE) -C $(@:.cflow=) cflow
+
+ifeq ("@INTL@", "yes")
+pofile: $(SUBDIRS.pofile) $(POTFILES)
+
+$(SUBDIRS.pofile):
+       $(MAKE) -C $(@:.pofile=) pofile
+endif
+
+ifneq ("$(CFLOW_LIST_TARGET)", "")
+CLEAN_CFLOW += $(CFLOW_LIST_TARGET)
+$(CFLOW_LIST_TARGET): $(CFLOW_LIST)
+       echo "CFLOW_SOURCES += $(addprefix \
+               \$$(top_srcdir)$(subst $(top_srcdir),,$(srcdir))/, $(CFLOW_LIST))" > $@
+cflow: $(CFLOW_LIST_TARGET)
+endif
+
+ifneq ("$(CFLOW_TARGET)", "")
+CLEAN_CFLOW += \
+       $(CFLOW_TARGET).cflow \
+       $(CFLOW_TARGET).xref \
+       $(CFLOW_TARGET).tree \
+       $(CFLOW_TARGET).rtree \
+       $(CFLOW_TARGET).rxref
+
+ifneq ("$(CFLOW_CMD)", "")
+CFLOW_FLAGS +=\
+ --cpp="$(CC) -E" \
+ --symbol _ISbit:wrapper \
+ --symbol __attribute__:wrapper \
+ --symbol __const__:wrapper \
+ --symbol __const:type \
+ --symbol __restrict:type \
+ --symbol __extension__:wrapper \
+ --symbol __nonnull:wrapper \
+ --symbol __nothrow__:wrapper \
+ --symbol __pure__:wrapper \
+ --symbol __REDIRECT:wrapper \
+ --symbol __REDIRECT_NTH:wrapper \
+ --symbol __wur:wrapper \
+ -I$(top_srcdir)/libdm \
+ -I$(top_srcdir)/libdm/ioctl \
+ -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2/ \
+ $(INCLUDES) $(DEFS)
+
+$(CFLOW_TARGET).cflow: $(CFLOW_SOURCES)
+       $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) $(CFLOW_SOURCES)
+$(CFLOW_TARGET).rxref: $(CFLOW_SOURCES)
+       $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) -r --omit-arguments $(CFLOW_SOURCES)
+$(CFLOW_TARGET).tree: $(CFLOW_SOURCES)
+       $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) --omit-arguments -T -b $(CFLOW_SOURCES)
+$(CFLOW_TARGET).xref: $(CFLOW_SOURCES)
+       $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) --omit-arguments -x $(CFLOW_SOURCES)
+#$(CFLOW_TARGET).rtree:  $(CFLOW_SOURCES)
+#      $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) -r --omit-arguments -T -b $(CFLOW_SOURCES)
+cflow: $(CFLOW_TARGET).cflow $(CFLOW_TARGET).tree $(CFLOW_TARGET).rxref $(CFLOW_TARGET).xref
+#$(CFLOW_TARGET).rtree
+endif
+endif
+
+$(TARGETS): $(OBJECTS)
+
+%.o: %.c
+       $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $< -o $@
+
+%.pot: %.c Makefile
+       $(CC) -E $(INCLUDES) -include $(top_srcdir)/include/pogen.h \
+               $(DEFS) $(CFLAGS) $< > $@
+
+%.so: %.o
+       $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(CLDFLAGS) $< $(LIBS) -o $@
+
+ifneq (,$(LIB_SHARED))
+
+TARGETS += $(LIB_SHARED).$(LIB_VERSION)
+$(LIB_SHARED).$(LIB_VERSION): $(OBJECTS) $(LDDEPS)
+ifeq ("@LIB_SUFFIX@","so")
+       $(CC) -shared -Wl,-soname,$(notdir $@) \
+               $(CFLAGS) $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
+endif
+ifeq ("@LIB_SUFFIX@","dylib")
+       $(CC) -dynamiclib -dylib_current_version,$(LIB_VERSION) \
+               $(CFLAGS) $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
+endif
+
+$(LIB_SHARED): $(LIB_SHARED).$(LIB_VERSION)
+       $(LN_S) -f $(<F) $@
+
+install_lib_shared: $(LIB_SHARED)
+       $(INSTALL_PROGRAM) -D $< $(libdir)/$(<F).$(LIB_VERSION)
+       $(INSTALL_DIR) $(usrlibdir)
+       $(LN_S) -f $(USRLIB_RELPATH)$(<F).$(LIB_VERSION) $(usrlibdir)/$(<F)
+
+# FIXME: plugins are installed to subdirs 
+#        and for compatibility links in libdir are created
+#        when the code is fixed links could be removed.
+install_dm_plugin: $(LIB_SHARED)
+       $(INSTALL_PROGRAM) -D $< $(libdir)/device-mapper/$(<F)
+       $(LN_S) -f device-mapper/$(<F) $(libdir)/$(<F)
+
+install_lvm2_plugin: $(LIB_SHARED)
+       $(INSTALL_PROGRAM) -D $< $(libdir)/lvm2/$(<F)
+       $(LN_S) -f lvm2/$(<F) $(libdir)/$(<F)
+       $(LN_S) -f $(<F) $(libdir)/$(<F).$(LIB_VERSION)
+endif
+
+$(LIB_STATIC): $(OBJECTS)
+       $(RM) $@
+       $(AR) rs $@ $(OBJECTS)
+
+%.d: %.c
+       $(MKDIR_P) $(dir $@); \
+       set -e; \
+       FILE=`echo $@ | sed 's/\\//\\\\\\//g;s/\\.d//g'`; \
+       DEPS=`echo $(DEPS) | sed -e 's/\\//\\\\\\//g'`; \
+       $(CC) -MM $(INCLUDES) $(DEFS) $(CFLAGS) -o $@ $<; \
+       sed -i "s/\(.*\)\.o[ :]*/$$FILE.o $$FILE.d $$FILE.pot: $$DEPS /g" $@; \
+       [ -s $@ ] || $(RM) $@
+
+%.mo: %.po
+       $(MSGFMT) -o $@ $<
+
+cleandir:
+       $(RM) $(OBJECTS) $(TARGETS) $(CLEAN_TARGETS) $(CLEAN_CFLOW) $(LDDEPS) \
+         $(POTFILES) $(SOURCES:%.c=%.d) $(SOURCES:%.c=%.gcno) $(SOURCES:%.c=%.gcda) \
+         $(SOURCES2:%.c=%.o) $(SOURCES2:%.c=%.d) $(SOURCES2:%.c=%.gcno) $(SOURCES2:%.c=%.gcda)
+
+clean: $(SUBDIRS.clean) cleandir
+
+distclean: cleandir $(SUBDIRS.distclean)
+       test -z "$(DISTCLEAN_DIRS)" || $(RM) -r $(DISTCLEAN_DIRS)
+       $(RM) $(DISTCLEAN_TARGETS) Makefile core
+
+.exported_symbols_generated: $(EXPORTED_HEADER) .exported_symbols
+       set -e; \
+       ( cat $(srcdir)/.exported_symbols; \
+         if test x$(EXPORTED_HEADER) != x; then \
+               $(CC) -E -P $(INCLUDES) $(DEFS) $(CFLAGS) $(EXPORTED_HEADER) | \
+               $(SED) -ne "/^typedef|}/!s/.*[ \*]\(\$(EXPORTED_FN_PREFIX)_[a-z0-9_]*\)(.*/\1/p"; \
+         fi \
+       ) > $@
+
+.export.sym: .exported_symbols_generated
+       set -e; (echo "Base {"; echo "  global:"; \
+                sed "s/^/              /;s/$$/;/" < $<; \
+                echo " local:"; echo "         *;"; echo "};") > $@
+
+ifeq (,$(findstring $(MAKECMDGOALS),cscope.out cflow clean distclean lcov))
+    ifdef SOURCES
+       -include $(SOURCES:.c=.d)
+    endif
+    ifdef SOURCES2
+       -include $(SOURCES2:.c=.d)
+    endif
+endif
diff --git a/man/Makefile.in b/man/Makefile.in
new file mode 100644 (file)
index 0000000..2506fbd
--- /dev/null
@@ -0,0 +1,97 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+ifeq ("@FSADM@", "yes")
+FSADMMAN = fsadm.8
+else
+FSADMMAN =
+endif
+
+ifeq ("@DMEVENTD@", "yes")
+DMEVENTDMAN = dmeventd.8
+else
+DMEVENTDMAN =
+endif
+
+MAN5=lvm.conf.5
+MAN8=lvchange.8 lvconvert.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 \
+       lvmchange.8 lvmconf.8 lvmdiskscan.8 lvmdump.8 lvmsadc.8 lvmsar.8 \
+       lvreduce.8 lvremove.8 lvrename.8 lvresize.8 lvs.8 \
+       lvscan.8 pvchange.8 pvck.8 pvcreate.8 pvdisplay.8 pvmove.8 pvremove.8 \
+       pvresize.8 pvs.8 pvscan.8 vgcfgbackup.8 vgcfgrestore.8 vgchange.8 \
+       vgck.8 vgcreate.8 vgconvert.8 vgdisplay.8 vgexport.8 vgextend.8 \
+       vgimport.8 vgimportclone.8 vgmerge.8 vgmknodes.8 vgreduce.8 vgremove.8 \
+       vgrename.8 vgs.8 vgscan.8 vgsplit.8 $(FSADMMAN)
+
+ifneq ("@CLVMD@", "none")
+  MAN8CLUSTER=clvmd.8
+else
+  MAN8CLUSTER=
+endif
+ifeq ("@BUILD_CMIRRORD@", "yes")
+  MAN8CLUSTER+=cmirrord.8
+endif
+
+MAN8DM=dmsetup.8 $(DMEVENTDMAN)
+MAN5DIR=$(mandir)/man5
+MAN8DIR=$(mandir)/man8
+
+CLEAN_TARGETS=$(MAN5) $(MAN8) $(MAN8CLUSTER) $(FSADMMAN) $(DMEVENTDMAN) $(MAN8DM)
+DISTCLEAN_TARGETS=fsadm.8 clvmd.8 cmirrord.8 dmeventd.8
+
+include $(top_builddir)/make.tmpl
+
+ifneq ("@CLVMD@", "none")
+  install: install_cluster
+endif
+
+all: man
+
+.PHONY: man install_man5 install_man8
+
+device-mapper: $(MAN8DM)
+
+man: $(MAN5) $(MAN8) $(MAN8CLUSTER)
+
+$(MAN5) $(MAN8) $(MAN8CLUSTER):        Makefile
+
+%: %.in
+       @case "$@" in \
+         */*) ;; \
+         *) echo "Creating $@" ; $(SED) -e "s+#VERSION#+$(LVM_VERSION)+;s+#DEFAULT_SYS_DIR#+$(DEFAULT_SYS_DIR)+;s+#DEFAULT_ARCHIVE_DIR#+$(DEFAULT_ARCHIVE_DIR)+;s+#DEFAULT_BACKUP_DIR#+$(DEFAULT_BACKUP_DIR)+;s+#DEFAULT_CACHE_DIR#+$(DEFAULT_CACHE_DIR)+;s+#DEFAULT_LOCK_DIR#+$(DEFAULT_LOCK_DIR)+" $< > $@ ;; \
+       esac
+
+install_man5: $(MAN5)
+       $(INSTALL) -d $(MAN5DIR)
+       $(INSTALL_DATA) $(MAN5) $(MAN5DIR)/
+
+install_man8: $(MAN8)
+       $(INSTALL) -d $(MAN8DIR)
+       $(INSTALL_DATA) $(MAN8) $(MAN8DIR)/
+
+install_lvm2: install_man5 install_man8
+
+install_cluster: $(MAN8CLUSTER)
+       $(INSTALL) -d $(MAN8DIR)
+       $(INSTALL_DATA) $(MAN8CLUSTER) $(MAN8DIR)/
+
+install_device-mapper: $(MAN8DM)
+       $(INSTALL) -d $(MAN8DIR)
+       $(INSTALL_DATA) $(MAN8DM) $(MAN8DIR)/
+
+install: install_lvm2 install_device-mapper
diff --git a/man/clvmd.8.in b/man/clvmd.8.in
new file mode 100644 (file)
index 0000000..53a9113
--- /dev/null
@@ -0,0 +1,96 @@
+.TH CLVMD 8 "LVM TOOLS #VERSION#" "Red Hat Inc" \" -*- nroff -*-
+.SH NAME
+clvmd \- cluster LVM daemon
+.SH SYNOPSIS
+.B clvmd
+[\-d [<value>]] [\-C] [\-h]
+[\-R]
+[\-S]
+[\-t <timeout>]
+[\-T <start timeout>]
+[\-V]
+.SH DESCRIPTION
+clvmd is the daemon that distributes LVM metadata updates around a cluster.
+It must be running on all nodes in the cluster and will give an error 
+if a node in the cluster does not have this daemon running.
+.SH OPTIONS
+.TP
+.I \-d [<value>]
+Enable debug logging. Value can be 0, 1 or 2.
+.br
+0 disables debug logging in a running clvmd
+.br
+1 sends debug logs to stderr (clvmd will not fork in this case)
+.br
+2 sends debug logs to syslog
+.br
+If 
+.B -d 
+is specified without a value then 1 is assumed if you are starting a
+new clvmd, 2 if you are enabling debug in a running clvmd.
+.TP
+.I \-C
+Only valid if 
+.B -d 
+is also specified. Tells all clvmds in a cluster to enable/disable debug logging.
+Without this switch, only the local clvmd will change its debug level to that
+given with 
+.B -d.
+.br
+This does not work correctly if specified on the command-line that starts clvmd.
+If you want to start clvmd 
+.B and 
+enable cluster-wide logging then the command needs to be issued twice, eg:
+.br
+clvmd
+.br
+clvmd -d2
+.br
+.TP
+.I \-t <timeout>
+Specifies the timeout for commands to run around the cluster. This should not
+be so small that commands with many disk updates to do will fail, so you
+may need to increase this on systems with very large disk farms. 
+The default is 30 seconds.
+.TP
+.I \-T <start timeout>
+Specifies the timeout for clvmd daemon startup. If the daemon does not report 
+that it has started up within this time then the parent command will exit with 
+status of 5. This does NOT mean that clvmd has not started! What it means is 
+that the startup of clvmd has been delayed for some reason; the most likely 
+cause of this is an inquorate cluster though it could be due to locking 
+latencies on a cluster with large numbers of logical volumes. If you get the 
+return code of 5 it is usually not necessary to restart clvmd - it will start
+as soon as that blockage has cleared. This flag is to allow startup scripts
+to exit in a timely fashion even if the cluster is stalled for some reason.
+.br
+The default is 0 (no timeout) and the value is in seconds. Don't set this too
+small or you will experience spurious errors. 10 or 20 seconds might be
+sensible.
+.br
+This timeout will be ignored if you start clvmd with the -d switch.
+.TP
+.I \-R
+Tells all the running clvmds in the cluster to reload their device cache and
+re-read the lvm configuration file. This command should be run whenever the
+devices on a cluster system are changed.
+.TP
+.I \-S
+Tells the running clvmd to exit and reexecute itself, for example at the
+end of a package upgrade.  The new instance is instructed to reacquire
+any locks in the same state as they were previously held.  (Alternative
+methods of restarting the daemon have the side effect of changing
+exclusive LV locks into shared locks.)
+.TP
+.I \-I
+Selects the cluster manager to use for locking and internal communications,
+the available managers will be listed as part of the 'clvmd -h' output.
+clvmd will use the first cluster manager that succeeds, and it checks them
+in the order cman,gulm,corosync,openais. As it is quite possible to have
+(eg) corosync and cman available on the same system you might have to
+manually specify this option to override the search.
+.TP
+.I \-V
+Display the version of the cluster LVM daemon.
+.SH SEE ALSO
+.BR lvm (8)
diff --git a/man/cmirrord.8.in b/man/cmirrord.8.in
new file mode 100644 (file)
index 0000000..035fa43
--- /dev/null
@@ -0,0 +1,30 @@
+.TH CMIRRORD 8 "LVM TOOLS #VERSION#" "Red Hat Inc" \" -*- nroff -*-
+.SH NAME
+cmirrord \- cluster mirror log daemon
+
+.SH SYNOPSIS
+.B cmirrord
+
+.SH DESCRIPTION
+cmirrord is the daemon that tracks mirror log information in a cluster.
+It is specific to device-mapper based mirrors (and by extension, LVM
+cluster mirrors).  Cluster mirrors are not possible without this daemon
+running.
+
+This daemon relies on the cluster infrastructure provided by the
+Cluster MANager (CMAN), which must be set up and running in order for
+cmirrord to function.  (The cluster infrastructure is also required for
+clvmd.)
+
+Output is logged via syslog.  The USR1 signal can be issued to cmirrord
+to gather current status information for debugging purposes.
+
+Once started, cmirrord will run until it is shutdown via INT signal.  If
+there are still active cluster mirrors, however, the signal will be
+ignored.  Active cluster mirrors should be shutdown before stopping the
+cluster mirror log daemon.
+
+.SH SEE ALSO
+.BR lvm (8)
+.BR clvmd (8)
+.BR cluster.conf (5)
\ No newline at end of file
diff --git a/man/dmeventd.8.in b/man/dmeventd.8.in
new file mode 100644 (file)
index 0000000..cc5a741
--- /dev/null
@@ -0,0 +1,44 @@
+.TH DMEVENTD 8 "DM TOOLS #VERSION#" "Red Hat Inc" \" -*- nroff -*-
+.SH NAME
+dmeventd \- Device-mapper event daemon
+.SH SYNOPSIS
+.B dmeventd
+[\-d]
+[\-f]
+[\-h]
+[\-V]
+[\-?]
+.SH DESCRIPTION
+dmeventd is the event monitoring daemon for device-mapper devices.
+Library plugins can register and carry out actions triggered when
+particular events occur.
+.SH
+LVM PLUGINS
+.TP
+.I Mirror
+Attempts to handle device failure automatically.  See \fBlvm.conf\fP(5).
+.TP
+.I Snapshot
+Monitors how full a snapshot is becoming and emits a warning to
+syslog when it exceeds 80% full.
+The warning is repeated when 85%, 90% and 95% of the snapshot is filled.
+See \fBlvm.conf\fP(5).
+.SH OPTIONS
+.TP
+.I \-d
+Repeat from 1 to 3 times (-d, -dd, -ddd) to increase the detail of
+debug messages sent to syslog.
+Each extra d adds more debugging information.
+.TP
+.I \-f
+Don't fork, run in the foreground.
+.TP
+.I \-h, \-?
+Show help information.
+.TP
+.I \-V
+Show version of dmeventd.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvm.conf (5)
diff --git a/man/dmsetup.8.in b/man/dmsetup.8.in
new file mode 100644 (file)
index 0000000..68e9819
--- /dev/null
@@ -0,0 +1,471 @@
+.TH DMSETUP 8 "Apr 06 2006" "Linux" "MAINTENANCE COMMANDS"
+.SH NAME
+dmsetup \- low level logical volume management
+.SH SYNOPSIS
+.ad l
+.B dmsetup help
+.I [-c|-C|--columns]
+.br
+.B dmsetup create 
+.I device_name [-u uuid] [--notable | --table <table> | table_file]
+.br
+.B dmsetup remove
+.I [-f|--force] device_name
+.br
+.B dmsetup remove_all
+.I [-f|--force]
+.br
+.B dmsetup suspend
+.I [--nolockfs] [--noflush] device_name
+.br
+.B dmsetup resume
+.I device_name
+.br
+.B dmsetup load
+.I device_name [--table <table> | table_file]
+.br
+.B dmsetup clear
+.I device_name
+.br
+.B dmsetup reload
+.I device_name [--table <table> | table_file]
+.br
+.B dmsetup rename
+.I device_name new_name
+.br
+.B dmsetup rename
+.I device_name --setuuid uuid
+.br
+.B dmsetup message
+.I device_name sector message
+.br
+.B dmsetup ls
+.I [--target target_type] [--exec command] [--tree [-o options]]
+.br
+.B dmsetup info 
+.I [device_name]
+.br
+.B dmsetup info -c|-C|--columns
+.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
+.I [device_name]
+.br
+.B dmsetup deps
+.I [device_name]
+.br
+.B dmsetup status
+.I [--target target_type]
+.I [device_name]
+.br
+.B dmsetup table
+.I [--target target_type] [--showkeys]
+.I [device_name]
+.br
+.B dmsetup wait
+.I device_name
+.I [event_nr]
+.br
+.B dmsetup mknodes
+.I [device_name]
+.br
+.B dmsetup udevcreatecookie
+.br
+.B dmsetup udevreleasecookie
+.I [cookie]
+.br
+.B dmsetup udevflags
+.I cookie
+.br
+.B dmsetup udevcomplete
+.I cookie
+.br
+.B dmsetup udevcomplete_all
+.br
+.B dmsetup udevcookies
+.br
+.B dmsetup targets
+.br
+.B dmsetup version
+.br
+.B dmsetup setgeometry
+.I device_name cyl head sect start
+.br
+.B dmsetup splitname
+.I device_name
+.I [subsystem]
+.br
+
+.B devmap_name
+.I major minor
+.br
+.B devmap_name 
+.I major:minor
+.ad b
+.SH DESCRIPTION
+dmsetup manages logical devices that use the device-mapper driver.  
+Devices are created by loading a table that specifies a target for
+each sector (512 bytes) in the logical device.
+
+The first argument to dmsetup is a command. 
+The second argument is the logical device name or uuid.
+
+Invoking the command as \fBdevmap_name\fP is equivalent to
+.br
+\fBdmsetup info -c --noheadings -j \fImajor\fB -m \fIminor\fP.
+.SH OPTIONS
+.IP \fB-c|-C|--columns
+.br
+Display output in columns rather than as Field: Value lines.
+.IP \fB-h|--help
+.br
+Outputs a summary of the commands available, optionally including
+the list of report fields (synonym with \fBhelp\fP command).
+.IP \fB--inactive
+.br
+When returning any table information from the kernel report on the 
+inactive table instead of the live table.
+Requires kernel driver version 4.16.0 or above.
+.IP \fB-j|--major\ \fImajor
+.br
+Specify the major number.
+.IP \fB-m|--minor\ \fIminor
+.br
+Specify the minor number.
+.IP \fB-n|--noheadings
+.br
+Suppress the headings line when using columnar output.
+.IP \fB--noopencount
+.br
+Tell the kernel not to supply the open reference count for the device.
+.IP \fB--notable
+.br
+When creating a device, don't load any table.
+.IP \fB--udevcookie\ \fIcookie
+.br
+Use cookie for udev synchronisation.
+.IP \fB--noudevrules
+Do not allow udev to manage nodes for devices in device-mapper directory.
+.br
+.IP \fB--noudevsync
+Do not synchronise with udev when creating, renaming or removing devices.
+.br
+.IP \fB-o|--options
+.br
+Specify which fields to display.
+.IP \fB-r|--readonly
+.br
+Set the table being loaded read-only.
+.IP \fB--readahead\ [+]<sectors>|auto|none
+.br    
+Specify read ahead size in units of sectors.
+The default value is "auto" which allows the kernel to choose
+a suitable value automatically.  The + prefix lets you
+specify a minimum value which will not be used if it is
+smaller than the value chosen by the kernel.
+"None" is equivalent to specifying zero.
+.IP \fB--table\ <table>
+.br
+Specify a one-line table directly on the command line.
+.IP \fB-u|--uuid
+.br
+Specify the uuid.
+.IP \fB-y|--yes
+.br
+Answer yes to all prompts automatically.
+.IP \fB-v|--verbose\ [-v|--verbose]
+.br
+Produce additional output.
+.IP \fB--version
+.br
+Display the library and kernel driver version.
+.SH COMMANDS
+.IP \fBclear
+.I device_name
+.br
+Destroys the table in the inactive table slot for device_name.
+.IP \fBcreate
+.I device_name [-u uuid] [--notable | --table <table> | table_file]
+.br
+Creates a device with the given name.
+If table_file or <table> is supplied, the table is loaded and made live.
+Otherwise a table is read from standard input unless --notable is used.
+The optional uuid can be used in place of
+device_name in subsequent dmsetup commands.  
+If successful a device will appear as
+/dev/device-mapper/<device-name>.  
+See below for information on the table format.
+.IP \fBdeps
+.I [device_name]
+.br
+Outputs a list of (major, minor) pairs for devices referenced by the
+live table for the specified device.
+.IP \fBhelp
+.I [-c|-C|--columns]
+.br
+Outputs a summary of the commands available, optionally including
+the list of report fields.
+.IP \fBinfo
+.I [device_name]
+.br
+Outputs some brief information about the device in the form:
+.br
+    State: SUSPENDED|ACTIVE, READ-ONLY
+.br
+    Tables present: LIVE and/or INACTIVE
+.br
+    Open reference count
+.br
+    Last event sequence number (used by \fBwait\fP)
+.br
+    Major and minor device number
+.br
+    Number of targets in the live table
+.br
+    UUID
+.IP \fBinfo
+.I -c|-C|--columns
+.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
+.I [device_name]
+.br
+Output you can customise.
+Fields are comma-separated and chosen from the following list:
+name, major, minor, attr, open, segments, events, uuid.
+Attributes are: (L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.
+Precede the list with '+' to append
+to the default selection of columns instead of replacing it.
+Precede any sort_field with - for a reverse sort on that column.
+.IP \fBls
+.I [--target target_type]
+.I [--exec command]
+.I [--tree [-o options]]
+.br
+List device names.  Optionally only list devices that have at least
+one target of the specified type.  Optionally execute a command for
+each device.  The device name is appended to the supplied command.
+--tree displays dependencies between devices as a tree.
+It accepts a comma-separate list of options.
+Some specify the information displayed against each node:
+device/nodevice; active, open, rw, uuid.
+Others specify how the tree is displayed:
+ascii, utf, vt100; compact, inverted, notrunc.
+.IP \fBload|reload
+.I device_name [--table <table> | table_file]
+.br
+Loads <table> or table_file into the inactive table slot for device_name.
+If neither is supplied, reads a table from standard input.
+.IP \fBmessage
+.I device_name sector message
+.br
+Send message to target. If sector not needed use 0.
+.IP \fBmknodes
+.I [device_name]
+.br
+Ensure that the node in /dev/mapper for device_name is correct.
+If no device_name is supplied, ensure that all nodes in /dev/mapper 
+correspond to mapped devices currently loaded by the device-mapper kernel
+driver, adding, changing or removing nodes as necessary.
+.IP \fBremove
+.I [-f|--force] device_name
+.br
+Removes a device.  It will no longer be visible to dmsetup.
+Open devices cannot be removed except with older kernels
+that contain a version of device-mapper prior to 4.8.0.
+In this case the device will be deleted when its open_count 
+drops to zero.  From version 4.8.0 onwards, if a device can't
+be removed because an uninterruptible process is waiting for
+I/O to return from it, adding --force will replace the table 
+with one that fails all I/O, which might allow the 
+process to be killed.
+.IP \fBremove_all
+.I [-f|--force]
+.br
+Attempts to remove all device definitions i.e. reset the driver.
+Use with care!  From version 4.8.0 onwards, if devices can't
+be removed because uninterruptible processes are waiting for
+I/O to return from them, adding --force will replace the table 
+with one that fails all I/O, which might allow the 
+process to be killed.  This also runs \fBmknodes\fP afterwards.
+.IP \fBrename
+.I device_name new_name
+.br
+Renames a device.
+.IP \fBrename
+.I device_name --setuuid uuid
+.br
+Sets the uuid of a device that was created without a uuid.
+After a uuid has been set it cannot be changed.
+.IP \fBresume
+.I device_name
+.br
+Un-suspends a device.  
+If an inactive table has been loaded, it becomes live.
+Postponed I/O then gets re-queued for processing.
+.IP \fBsetgeometry
+.I device_name cyl head sect start
+.br
+Sets the device geometry to C/H/S.
+.IP \fBsplitname
+.I device_name
+.I [subsystem]
+.br
+Splits given device name into subsystem constituents.
+Default subsystem is LVM.
+.IP \fBstatus
+.I [--target target_type]
+.I [device_name]
+.br
+Outputs status information for each of the device's targets.
+With --target, only information relating to the specified target type
+is displayed.
+.IP \fBsuspend
+.I [--nolockfs] [--noflush]
+.I device_name
+.br
+Suspends a device.  Any I/O that has already been mapped by the device
+but has not yet completed will be flushed.  Any further I/O to that
+device will be postponed for as long as the device is suspended.
+If there's a filesystem on the device which supports the operation, 
+an attempt will be made to sync it first unless --nolockfs is specified.
+Some targets such as recent (October 2006) versions of multipath may support
+the --noflush option.  This lets outstanding I/O that has not yet reached the
+device to remain unflushed.
+.IP \fBtable
+.I [--target target_type] [--showkeys]
+.I [device_name]
+.br
+Outputs the current table for the device in a format that can be fed
+back in using the create or load commands.
+With --target, only information relating to the specified target type
+is displayed.
+Encryption keys are suppressed in the table output for the crypt
+target unless the --showkeys parameter is supplied.
+.IP \fBtargets
+.br
+Displays the names and versions of the currently-loaded targets.
+.br
+.IP \fBudevcreatecookie
+.br
+Creates a new cookie to synchronize actions with udev processing.
+The output is a cookie value. Normally we don't need to create cookies since
+dmsetup creates and destroys them for each action automatically. However, we can
+generate one explicitly to group several actions together and use only one
+cookie instead. We can define a cookie to use for each relevant command by using
+--udevcookie option. Alternatively, we can export this value into the environment
+of the dmsetup process as DM_UDEV_COOKIE variable and it will be used automatically
+with all subsequent commands until it is unset.
+Invoking this command will create system-wide semaphore that needs to be cleaned
+up explicitly by calling udevreleasecookie command.
+.br
+.IP \fBudevreleasecookie
+.I [cookie]
+.br
+Waits for all pending udev processing bound to given cookie value and clean up
+the cookie with underlying semaphore. If the cookie is not given directly,
+the command will try to use a value defined by DM_UDEV_COOKIE environment variable.
+.br
+.IP \fBudevflags
+.I cookie
+.br
+Parses given cookie value and extracts any udev control flags encoded.
+The output is in environment key format that is suitable for use in udev
+rules. If the flag has its symbolic name assigned then the ouput is
+DM_UDEV_FLAG_<flag_name>='1', DM_UDEV_FLAG<flag_position>='1' otherwise.
+Subsystem udev flags don't have symbolic names assigned and these ones are
+always reported as DM_SUBSYSTEM_UDEV_FLAG<flag_position>='1'. There are
+16 udev flags altogether.
+.br
+.IP \fBudevcomplete
+.I cookie
+.br
+Wake any processes that are waiting for udev to complete processing the specified cookie.
+.br
+.IP \fBudevcomplete_all
+Remove all cookies. Any process waiting on a cookie will be resumed immediately.
+.br
+.IP \fBudevcookies
+List all existing cookies. Cookies are system-wide semaphores with keys
+prefixed by two predefined bytes (0x0D4D).
+.br
+.IP \fBversion
+.br
+Outputs version information.
+.IP \fBwait
+.I device_name
+.I [event_nr]
+.br
+Sleeps until the event counter for device_name exceeds event_nr.
+Use -v to see the event number returned.
+To wait until the next event is triggered, use \fBinfo\fP to find
+the last event number.  
+.SH TABLE FORMAT
+Each line of the table specifies a single target and is of the form:
+.br
+    logical_start_sector num_sectors target_type target_args
+.br
+.br
+
+There are currently three simple target types available together 
+with more complex optional ones that implement snapshots and mirrors.
+
+.IP \fBlinear
+.I destination_device start_sector
+.br
+The traditional linear mapping.
+
+.IP \fBstriped
+.I num_stripes chunk_size [destination start_sector]+
+.br
+Creates a striped area.
+.br
+e.g. striped 2 32 /dev/hda1 0 /dev/hdb1 0
+will map the first chunk (16k) as follows:
+.br
+    LV chunk 1 -> hda1, chunk 1
+.br
+    LV chunk 2 -> hdb1, chunk 1
+.br
+    LV chunk 3 -> hda1, chunk 2
+.br
+    LV chunk 4 -> hdb1, chunk 2
+.br
+    etc.
+
+.IP \fBerror
+.br
+Errors any I/O that goes to this area.  Useful for testing or
+for creating devices with holes in them.
+
+.SH EXAMPLES
+
+# A table to join two disks together
+.br
+.br
+0 1028160 linear /dev/hda 0
+.br
+1028160 3903762 linear /dev/hdb 0
+
+
+# A table to stripe across the two disks, 
+.br
+# and add the spare space from
+.br
+# hdb to the back of the volume
+
+0 2056320 striped 2 32 /dev/hda 0 /dev/hdb 0
+.br
+2056320 2875602 linear /dev/hdb 1028160
+
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBDM_DEV_DIR\fP
+The device directory name.
+Defaults to "/dev" and must be an absolute path.
+.TP
+\fBDM_UDEV_COOKIE\fP
+A cookie to use for all relevant commands to synchronize with udev processing.
+It is an alternative to using --udevcookie option.
+
+.SH AUTHORS
+Original version: Joe Thornber (thornber@sistina.com)
+
+.SH SEE ALSO
+Device-mapper resource page: http://sources.redhat.com/dm/
diff --git a/man/fsadm.8.in b/man/fsadm.8.in
new file mode 100644 (file)
index 0000000..eeec2d3
--- /dev/null
@@ -0,0 +1,68 @@
+.TH "FSADM" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+.SH "NAME"
+fsadm \- utility to resize or check filesystem on a device
+.SH "SYNOPSIS"
+.B fsadm
+.RI [options]\ check\ device
+
+.B fsadm
+.RI [options]\ resize\ device\ [new_size[BKMGTEP]]
+
+.SH "DESCRIPTION"
+\fBfsadm\fR utility resizes or checks the filesystem on a device.
+It tries to use the same API for \fBext2/ext3/ext4\fR,
+\fBReiserFS\fR and \fBXFS\fR filesystem.
+.SH "OPTIONS"
+.TP
+\fB\-h \-\-help\fR
+\(em print help message
+.TP
+\fB\-v \-\-verbose\fR
+\(em be more verbose
+.TP
+\fB\-e \-\-ext\-offline\fR
+\(em unmount ext2/ext3/ext4 filesystem before doing resize
+.TP
+\fB\-f \-\-force\fR
+\(em bypass some sanity checks
+.TP
+\fB\-n \-\-dry\-run\fR
+\(em print commands without running them
+.TP
+\fB\-y \-\-yes\fR
+\(em answer "yes" at any prompts
+.TP
+\fBnew_size\fR
+\(em Absolute number of filesystem blocks to be in the filesystem,
+or an absolute size using a suffix (in powers of 1024).
+If new_size is not supplied, the whole device is used.
+
+.SH "DIAGNOSTICS"
+On successful completion, the status code is 0.
+A status code of 2 indicates the operation was interrupted by the user.
+A status code of 3 indicates the requested check operation could not be performed
+because the filesystem is mounted and does not support an online fsck.
+A status code of 1 is used for other failures.
+
+.SH "EXAMPLES"
+"fsadm \-e \-y resize /dev/vg/test 1000M" tries to resize the filesystem
+on logical volume /dev/vg/test. If /dev/vg/test contains ext2/ext3/ext4
+filesystem it will be unmounted prior the resize.
+All [y|n] questions will be answered 'y'.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+\fBTMPDIR\fP
+Where the temporary directory should be created.
+.TP
+.BR
+.SH "SEE ALSO"
+.BR lvm (8),
+.BR lvresize (8),
+.BR lvm.conf (5),
+.BR tune2fs (8),
+.BR resize2fs (8),
+.BR reiserfstune (8),
+.BR resize_reiserfs (8),
+.BR xfs_info (8),
+.BR xfs_growfs (8),
+.BR xfs_check (8)
diff --git a/man/lvchange.8.in b/man/lvchange.8.in
new file mode 100644 (file)
index 0000000..e269802
--- /dev/null
@@ -0,0 +1,122 @@
+.TH LVCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvchange \- change attributes of a logical volume
+.SH SYNOPSIS
+.B lvchange
+[\-\-addtag Tag]
+[\-A|\-\-autobackup y|n] [\-a|\-\-available y|n|ey|en|ly|ln]
+[\-\-alloc AllocationPolicy]
+[\-C|\-\-contiguous y|n] [\-d|\-\-debug] [\-\-deltag Tag]
+[\-\-resync]
+[\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-ignoremonitoring]
+[\-\-monitor {y|n}]
+[\-\-poll {y|n}]
+[\-\-sysinit]
+[\-\-noudevsync]
+[\-M|\-\-persistent y|n] [\-\-minor minor]
+[\-P|\-\-partial]
+[\-p|\-\-permission r|rw] [\-r/\-\-readahead ReadAheadSectors|auto|none]
+[\-\-refresh]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [LogicalVolumePath...]
+.SH DESCRIPTION
+lvchange allows you to change the attributes of a logical volume
+including making them known to the kernel ready for use.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-available y|n|ey|en|ly|ln
+Controls the availability of the logical volumes for use.
+Communicates with the kernel device-mapper driver via
+libdevmapper to activate (-ay) or deactivate (-an) the 
+logical volumes.
+.IP
+If clustered locking is enabled, -aey will activate exclusively
+on one node and -aly will activate only on the local node.
+To deactivate only on the local node use -aln.
+Logical volumes with single-host snapshots are always activated 
+exclusively because they can only be used on one node at once.
+.TP
+.I \-C, \-\-contiguous y|n
+Tries to set or reset the contiguous allocation policy for
+logical volumes. It's only possible to change a non-contiguous
+logical volume's allocation policy to contiguous, if all of the
+allocated physical extents are already contiguous.
+.TP
+.I \-\-resync
+Forces the complete resynchronization of a mirror.  In normal
+circumstances you should not need this option because synchronization
+happens automatically.  Data is read from the primary mirror device
+and copied to the others, so this can take a considerable amount of
+time - and during this time you are without a complete redundant copy
+of your data.
+.TP
+.I \-\-minor minor
+Set the minor number.
+.TP
+.I \-\-monitor y|n
+Start or stop monitoring a mirrored or snapshot logical volume with
+dmeventd, if it is installed.
+If a device used by a monitored mirror reports an I/O error,
+the failure is handled according to 
+\fBmirror_image_fault_policy\fP and \fBmirror_log_fault_policy\fP
+set in \fBlvm.conf\fP.
+.TP
+.I \-\-poll y|n
+Without polling a logical volume's backgrounded transformation process
+will never complete.  If there is an incomplete pvmove or lvconvert (for
+example, on rebooting after a crash), use \fB--poll y\fP to restart the
+process from its last checkpoint.  However, it may not be appropriate to
+immediately poll a logical volume when it is activated, use \fB--poll
+n\fP to defer and then \fB--poll y\fP to restart the process.
+.TP
+.I \-\-sysinit
+Indicates that lvchange(8) is being invoked from early system initialisation
+scripts (e.g. rc.sysinit or an initrd), before writeable filesystems are
+available. As such, some functionality needs to be disabled and this option
+acts as a shortcut which selects an appropriate set of options. Currently
+this is equivalent to using  \fB--ignorelockingfailure\fP, \fB--ignoremonitoring\fP,
+\fB--poll n\fP and setting \fBLVM_SUPPRESS_LOCKING_FAILURE_MESSAGES\fP
+environment variable.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-\-ignoremonitoring
+Make no attempt to interact with dmeventd unless \-\-monitor
+is specified.
+Do not use this if dmeventd is already monitoring a device.
+.TP
+.I \-M, \-\-persistent y|n
+Set to y to make the minor number specified persistent.
+.TP
+.I \-p, \-\-permission r|rw
+Change access permission to read-only or read/write.
+.TP
+.I \-r, \-\-readahead ReadAheadSectors|auto|none
+Set read ahead sector count of this logical volume.
+For volume groups with metadata in lvm1 format, this must
+be a value between 2 and 120 sectors.
+The default value is "auto" which allows the kernel to choose
+a suitable value automatically.
+"None" is equivalent to specifying zero.
+.TP
+.I \-\-refresh
+If the logical volume is active, reload its metadata.
+This is not necessary in normal operation, but may be useful
+if something has gone wrong or if you're doing clustering 
+manually without a clustered lock manager.
+.SH Examples
+"lvchange -pr vg00/lvol1" changes the permission on 
+volume lvol1 in volume group vg00 to be read-only.
+
+.SH SEE ALSO
+.BR lvm (8), 
+.BR lvcreate (8),
+.BR vgchange (8)
diff --git a/man/lvconvert.8.in b/man/lvconvert.8.in
new file mode 100644 (file)
index 0000000..8e48793
--- /dev/null
@@ -0,0 +1,224 @@
+.TH LVCONVERT 8 "LVM TOOLS #VERSION#" "Red Hat, Inc" \" -*- nroff -*-
+.SH NAME
+lvconvert \- convert a logical volume from linear to mirror or snapshot
+.SH SYNOPSIS
+.B lvconvert
+\-m|\-\-mirrors Mirrors [\-\-mirrorlog {disk|core|mirrored}] [\-\-corelog] [\-R|\-\-regionsize MirrorLogRegionSize]
+[\-A|\-\-alloc AllocationPolicy]
+[\-b|\-\-background] [\-f|\-\-force] [\-i|\-\-interval Seconds]
+[\-h|\-?|\-\-help]
+[\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+[\-\-noudevsync]
+[\-v|\-\-verbose] [\-y|\-\-yes]
+[\-\-version]
+.br
+LogicalVolume[Path] [PhysicalVolume[Path][:PE[-PE]]...]
+.br
+
+.br
+.B lvconvert
+\-\-splitmirrors Images \-\-name SplitLogicalVolumeName
+.br
+MirrorLogicalVolume[Path] [SplittablePhysicalVolume[Path][:PE[-PE]]...]
+.br
+
+.br
+.B lvconvert
+\-s|\-\-snapshot [\-c|\-\-chunksize ChunkSize]
+[\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-v|\-\-verbose]
+[\-Z|\-\-zero y|n]
+[\-\-version]
+.br
+OriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]
+.br
+
+.br
+.B lvconvert
+\-\-merge [\-b|\-\-background] [\-i|\-\-interval Seconds]
+[\-h|\-?|\-\-help]
+[\-v|\-\-verbose]
+[\-\-version]
+SnapshotLogicalVolume[Path]...
+.br
+
+.br
+.B lvconvert
+\-\-repair
+[\-h|\-?|\-\-help]
+[\-v|\-\-verbose]
+[\-\-version]
+LogicalVolume[Path] [PhysicalVolume[Path]...]
+.SH DESCRIPTION
+lvconvert will change a linear logical volume to a mirror
+logical volume or to a snapshot of linear volume and vice versa.
+It is also used to add and remove disk logs from mirror devices.
+.br
+If the conversion requires allocation of physical extents (for
+example, when converting from linear to mirror) and you specify
+one or more PhysicalVolumes (optionally with ranges of physical
+extents), allocation of physical extents will be restricted to
+these physical extents.  If the conversion frees physical extents
+(for example, when converting from a mirror to a linear, or reducing
+mirror legs) and you specify one or more PhysicalVolumes,
+the freed extents come first from the specified PhysicalVolumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.br
+Exactly one of \-\-splitmirrors, \-\-mirrors, \-\-repair, \-\-snapshot
+or \-\-merge arguments is required.
+.br
+.TP
+.I \-m, \-\-mirrors Mirrors
+Specifies the degree of the mirror you wish to create.
+For example, "-m 1" would convert the original logical
+volume to a mirror volume with 2-sides; that is, a
+linear volume plus one copy.
+.TP
+.I \-\-mirrorlog {disk|core|mirrored}
+Specifies the type of log to use.
+The default is disk, which is persistent and requires
+a small amount of storage space, usually on a separate device
+from the data being mirrored. 
+Core may be useful for short-lived mirrors: It means the mirror is
+regenerated by copying the data from the first device again every
+time the device is activated - perhaps, for example, after every reboot.
+Using "mirrored" will create a persistent log that is itself mirrored.
+.TP
+.I \-\-corelog
+The optional argument "--corelog" is the same as specifying "--mirrorlog core".
+.TP
+.I \-R, \-\-regionsize MirrorLogRegionSize
+A mirror is divided into regions of this size (in MB), and the mirror log
+uses this granularity to track which regions are in sync.
+.TP
+.I \-b, \-\-background
+Run the daemon in the background.
+.TP
+.I \-i, \-\-interval Seconds
+Report progress as a percentage at regular intervals.
+.br
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.br
+
+
+.TP
+.I \-\-splitmirrors Images
+The number of redundant Images of a mirror to be split off and used
+to form a new logical volume.  A name must be supplied for the
+newly-split-off logical volume using the \-\-name argument.
+
+.TP
+.I \-n Name
+The name to apply to a logical volume which has been split off from
+a mirror logical volume.
+.br
+
+
+.TP
+.I \-s, \-\-snapshot
+Create a snapshot from existing logical volume using another
+existing logical volume as its origin.
+.TP
+.I \-c, \-\-chunksize ChunkSize
+Power of 2 chunk size for the snapshot logical volume between 4k and 512k.
+.TP
+.I \-Z, \-\-zero y|n
+Controls zeroing of the first KB of data in the snapshot.
+If the volume is read-only the snapshot will not be zeroed.
+.TP
+.I \-\-merge
+Merges a snapshot into its origin volume.  To check if your kernel
+supports this feature, look for 'snapshot-merge' in the output
+of 'dmsetup targets'.  If both the origin and snapshot volume are not
+open the merge will start immediately.  Otherwise, the merge will start
+the first time either the origin or snapshot are activated and both are closed.
+Merging a snapshot into an origin that cannot be closed, for example a root
+filesystem, is deferred until the next time the origin volume is activated.
+When merging starts, the resulting logical volume will have the origin's name,
+minor number and UUID.  While the merge is in progress, reads or writes to the
+origin appear as they were directed to the snapshot being merged.  When the
+merge finishes, the merged snapshot is removed.  Multiple snapshots may
+be specified on the commandline or a @tag may be used to specify
+multiple snapshots be merged to their respective origin.
+.br
+
+
+.TP
+.I \-\-repair
+Repair a mirror after suffering a disk failure. The mirror will be brought back
+into a consistent state.  By default, the original number of mirrors will be
+restored if possible.  Specify \-y on the command line to skip the prompts.
+Use \-f if you do not want any replacement.  Additionally, you may use
+\-\-use-policies to use the device replacement policy specified in lvm.conf,
+viz. activation/mirror_log_fault_policy or
+activation/mirror_device_fault_policy.
+.br
+.SH Examples
+"lvconvert -m1 vg00/lvol1"
+.br
+converts the linear logical volume "vg00/lvol1" to
+a two-way mirror logical volume.
+
+"lvconvert --mirrorlog core vg00/lvol1"
+.br
+converts a mirror with a disk log to a
+mirror with an in-memory log.
+
+"lvconvert --mirrorlog disk vg00/lvol1"
+.br
+converts a mirror with an in-memory log
+to a mirror with a disk log.
+
+"lvconvert -m0 vg00/lvol1"
+.br
+converts a mirror logical volume to a linear logical
+volume.
+.br
+
+.br
+"lvconvert -s vg00/lvol1 vg00/lvol2"
+.br
+converts logical volume "vg00/lvol2" to snapshot of original volume "vg00/lvol1"
+
+.br
+"lvconvert -m1 vg00/lvol1 /dev/sda:0-15 /dev/sdb:0-15"
+.br
+converts linear logical volume "vg00/lvol1" to a two-way mirror, using physical
+extents /dev/sda:0-15 and /dev/sdb:0-15 for allocation of new extents.
+
+.br
+"lvconvert -m0 vg00/lvmirror1 /dev/sda
+.br
+converts mirror logical volume "vg00/lvmirror1" to linear, freeing physical
+extents from /dev/sda.
+
+.br
+"lvconvert --merge vg00/lvol1_snap"
+.br
+merges "vg00/lvol1_snap" into its origin.
+
+.br
+"lvconvert --merge @some_tag"
+.br
+If vg00/lvol1, vg00/lvol2, and vg00/lvol3 are all tagged with "some_tag"
+each snapshot logical volume will be merged serially, e.g.: vg00/lvol1,
+then vg00/lvol2, then vg00/lvol3.  If --background were used it would start
+all snapshot logical volume merges in parallel.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR lvremove (8),
+.BR lvrename (8),
+.BR lvextend (8),
+.BR lvreduce (8),
+.BR lvdisplay (8),
+.BR lvscan (8)
diff --git a/man/lvcreate.8.in b/man/lvcreate.8.in
new file mode 100644 (file)
index 0000000..0d0976f
--- /dev/null
@@ -0,0 +1,258 @@
+.TH LVCREATE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvcreate \- create a logical volume in an existing volume group
+.SH SYNOPSIS
+.B lvcreate
+[\-\-addtag Tag]
+[\-\-alloc AllocationPolicy]
+[\-A|\-\-autobackup y|n] [\-C|\-\-contiguous y|n] [\-d|\-\-debug]
+[\-h|\-?|\-\-help] [\-\-noudevsync]
+[\-\-ignoremonitoring]
+[\-\-monitor {y|n}]
+[\-i|\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+{\-l|\-\-extents LogicalExtentsNumber[%{VG|PVS|FREE}] |
+ \-L|\-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-M|\-\-persistent y|n] [\-\-minor minor]
+[\-m|\-\-mirrors Mirrors [\-\-nosync] [\-\-mirrorlog {disk|core|mirrored}] [\-\-corelog]
+[\-R|\-\-regionsize MirrorLogRegionSize]]
+[\-n|\-\-name LogicalVolumeName]
+[\-p|\-\-permission r|rw] [\-r|\-\-readahead ReadAheadSectors|auto|none]
+[\-t|\-\-test]
+[\-\-type SegmentType]
+[\-v|\-\-verbose] [\-Z|\-\-zero y|n]
+VolumeGroupName [PhysicalVolumePath[:PE[-PE]]...]
+.br
+
+.br
+.B lvcreate
+{\-l|\-\-extents LogicalExtentsNumber[%{VG|FREE|ORIGIN}] |
+ \-L|\-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-c|\-\-chunksize ChunkSize]
+[\-\-noudevsync]
+[\-\-ignoremonitoring]
+[\-\-monitor {y|n}]
+\-n|\-\-name SnapshotLogicalVolumeName
+{{\-s|\-\-snapshot}
+OriginalLogicalVolumePath | 
+[\-s|\-\-snapshot]
+VolumeGroupName \-\-virtualsize VirtualSize}
+.SH DESCRIPTION
+lvcreate creates a new logical volume in a volume group ( see
+.B vgcreate(8), vgchange(8)
+) by allocating logical extents from the free physical extent pool
+of that volume group.  If there are not enough free physical extents then
+the volume group can be extended ( see
+.B vgextend(8)
+) with other physical volumes or by reducing existing logical volumes
+of this volume group in size ( see
+.B lvreduce(8)
+). If you specify one or more PhysicalVolumes, allocation of physical
+extents will be restricted to these volumes.
+.br
+.br
+The second form supports the creation of snapshot logical volumes which 
+keep the contents of the original logical volume for backup purposes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-c, \-\-chunksize ChunkSize
+Power of 2 chunk size for the snapshot logical volume between 4k and 512k.
+.TP
+.I \-C, \-\-contiguous y|n
+Sets or resets the contiguous allocation policy for
+logical volumes. Default is no contiguous allocation based
+on a next free principle.
+.TP
+.I \-i, \-\-stripes Stripes
+Gives the number of stripes.
+This is equal to the number of physical volumes to scatter
+the logical volume.
+.TP
+.I \-I, \-\-stripesize StripeSize
+Gives the number of kilobytes for the granularity of the stripes.
+.br
+StripeSize must be 2^n (n = 2 to 9) for metadata in LVM1 format.
+For metadata in LVM2 format, the stripe size may be a larger
+power of 2 but must not exceed the physical extent size.
+.TP
+.I \-l, \-\-extents LogicalExtentsNumber[%{VG|PVS|FREE|ORIGIN}]
+Gives the number of logical extents to allocate for the new
+logical volume.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, as a percentage of the
+remaining free space in the Volume Group with the suffix %FREE, as a
+percentage of the remaining free space for the specified
+PhysicalVolume(s) with the suffix %PVS, or (for a snapshot) as a
+percentage of the total space in the Origin Logical Volume with the
+suffix %ORIGIN.
+.TP
+.I \-L, \-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Gives the size to allocate for the new logical volume.
+A size suffix of K for kilobytes, M for megabytes,
+G for gigabytes, T for terabytes, P for petabytes
+or E for exabytes is optional.
+.br
+Default unit is megabytes.
+.TP
+.I \-\-minor minor
+Set the minor number.
+.TP
+.I \-M, \-\-persistent y|n
+Set to y to make the minor number specified persistent.
+.TP
+.I \-m, \-\-mirrors Mirrors
+Creates a mirrored logical volume with Mirrors copies.  For example,
+specifying "-m 1" would result in a mirror with two-sides; that is, a
+linear volume plus one copy.
+
+Specifying the optional argument --nosync will cause the creation
+of the mirror to skip the initial resynchronization.  Any data written
+afterwards will be mirrored, but the original contents will not be
+copied.  This is useful for skipping a potentially long and resource
+intensive initial sync of an empty device.
+
+The optional argument --mirrorlog specifies the type of log to be used.
+The default is disk, which is persistent and requires
+a small amount of storage space, usually on a separate device from the
+data being mirrored.  Using core means the mirror is regenerated
+by copying the data from the first device again each time the
+device is activated, for example, after every reboot.  Using "mirrored"
+will create a persistent log that is itself mirrored.
+
+The optional argument --corelog is equivalent to --mirrorlog core.
+
+.TP
+.I \-n, \-\-name LogicalVolumeName
+The name for the new logical volume.
+.br
+Without this option a default names of "lvol#" will be generated where
+# is the LVM internal number of the logical volume.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-\-monitor y|n
+Start or avoid monitoring a mirrored or snapshot logical volume with
+dmeventd, if it is installed. 
+If a device used by a monitored mirror reports an I/O error,
+the failure is handled according to 
+\fBmirror_image_fault_policy\fP and \fBmirror_log_fault_policy\fP
+set in \fBlvm.conf\fP.
+.TP
+.I \-\-ignoremonitoring
+Make no attempt to interact with dmeventd unless \-\-monitor
+is specified.
+.TP
+.I \-p, \-\-permission r|rw
+Set access permissions to read only or read and write.
+.br
+Default is read and write.
+.TP
+.I \-r, \-\-readahead ReadAheadSectors|auto|none
+Set read ahead sector count of this logical volume.
+For volume groups with metadata in lvm1 format, this must
+be a value between 2 and 120.
+The default value is "auto" which allows the kernel to choose
+a suitable value automatically.
+"None" is equivalent to specifying zero.
+.TP
+.I \-R, \-\-regionsize MirrorLogRegionSize
+A mirror is divided into regions of this size (in MB), and the mirror log 
+uses this granularity to track which regions are in sync.
+.TP
+.I \-s, \-\-snapshot
+Create a snapshot logical volume (or snapshot) for an existing, so called
+original logical volume (or origin).
+Snapshots provide a 'frozen image' of the contents of the origin
+while the origin can still be updated. They enable consistent
+backups and online recovery of removed/overwritten data/files. The snapshot
+does not need the same amount of storage the origin has. In a typical scenario,
+15-20% might be enough. In case the snapshot runs out of storage, use
+.B lvextend(8)
+to grow it. Shrinking a snapshot is supported by
+.B lvreduce(8)
+as well. Run
+.B lvdisplay(8)
+on the snapshot in order to check how much data is allocated to it.
+Note that a small amount of the space you allocate to the snapshot is
+used to track the locations of the chunks of data, so you should
+allocate slightly more space than you actually need and monitor the
+rate at which the snapshot data is growing so you can avoid running out
+of space.
+.TP
+.I \-\-type SegmentType
+Create a logical volume that uses the specified segment type
+(e.g. "mirror", "snapshot", "striped").  Especially useful when no
+existing commandline switch alias enables the use of the desired type
+(e.g. "error" or "zero" types).  Many segment types already have a
+commandline switch alias that will enable their use (-s is an alias for
+--type snapshot).
+.TP
+.I \-\-virtualsize VirtualSize
+Create a sparse device of the given size (in MB by default) using a snapshot.  
+Anything written to the device will be returned when reading from it.
+Reading from other areas of the device will return blocks of zeros.
+It is implemented by creating a hidden virtual device of the
+requested size using the zero target.  A suffix of _vorigin is used for
+this device.
+.TP
+.I \-Z, \-\-zero y|n
+Controls zeroing of the first KB of data in the new logical volume.
+.br
+Default is yes.
+.br
+Volume will not be zeroed if read only flag is set.
+.br
+Snapshot volumes are zeroed always.
+
+.br
+Warning: trying to mount an unzeroed logical volume can cause the system to
+hang.
+.SH Examples
+"lvcreate -i 3 -I 8 -L 100M vg00" tries to create a striped logical
+volume with 3 stripes, a stripesize of 8KB and a size of 100MB in the volume
+group named vg00. The logical volume name will be chosen by lvcreate.
+
+"lvcreate -m1 -L 500M vg00" tries to create a mirror logical volume
+with 2 sides with a useable size of 500 MiB.  This operation would
+require 3 devices - two for the mirror devices and one for the disk
+log.
+
+"lvcreate -m1 --mirrorlog core -L 500M vg00" tries to create a mirror logical volume
+with 2 sides with a useable size of 500 MiB.  This operation would
+require 2 devices - the log is "in-memory".
+
+"lvcreate --size 100m --snapshot --name snap /dev/vg00/lvol1"
+.br
+creates a snapshot logical volume named /dev/vg00/snap which has access to the
+contents of the original logical volume named /dev/vg00/lvol1
+at snapshot logical volume creation time. If the original logical volume
+contains a file system, you can mount the snapshot logical volume on an
+arbitrary directory in order to access the contents of the filesystem to run
+a backup while the original filesystem continues to get updated.
+
+"lvcreate --virtualsize 1T --size 100M --snapshot --name sparse vg1"
+.br
+creates a sparse device named /dev/vg1/sparse of size 1TB with space for just
+under 100MB of actual data on it.
+.br
+
+"lvcreate -L 64M -n lvol1 vg00 /dev/sda:0-7 /dev/sdb:0-7"
+.br
+creates a linear logical volume "vg00/lvol1" using physical extents
+/dev/sda:0-7 and /dev/sdb:0-7 for allocation of extents.
+
+
+.SH SEE ALSO
+.BR lvm (8), 
+.BR vgcreate (8), 
+.BR lvremove (8), 
+.BR lvrename (8)
+.BR lvextend (8), 
+.BR lvreduce (8), 
+.BR lvdisplay (8), 
+.BR lvscan (8)
diff --git a/man/lvdisplay.8.in b/man/lvdisplay.8.in
new file mode 100644 (file)
index 0000000..477e934
--- /dev/null
@@ -0,0 +1,102 @@
+.TH LVDISPLAY 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvdisplay \- display attributes of a logical volume
+.SH SYNOPSIS
+.B lvdisplay
+[\-a|\-\-all]
+[\-c|\-\-colon] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-maps]
+[\-\-nosuffix]
+[\-P|\-\-partial]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-v|\-\-verbose]
+[\-\-version] [LogicalVolumePath [LogicalVolumePath...]]
+.br
+
+.br
+.B lvdisplay \-\-columns | \-C
+[\-\-aligned]
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-noheadings]
+[\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial]
+[\-\-segments]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-v|\-\-verbose]
+[\-\-version] [LogicalVolumePath [LogicalVolumePath...]]
+.SH DESCRIPTION
+lvdisplay allows you to see the attributes of a logical volume
+like size, read/write status, snapshot information etc.
+.P
+\fBlvs\fP (8) is an alternative that provides the same information 
+in the style of \fBps\fP (1).  \fBlvs\fP is recommended over
+\fBlvdisplay\fP.
+
+.SH OPTIONS
+See \fBlvm\fP for common options and \fBlvs\fP for options given with
+\fB\-\-columns\fP.
+.TP
+.I \-\-all
+Include information in the output about internal Logical Volumes that
+are components of normally-accessible Logical Volumes, such as mirrors,
+but which are not independently accessible (e.g. not mountable).
+For example, after creating a mirror using 'lvcreate -m1 --mirrorlog disk',
+this option will reveal three internal Logical Volumes, with suffixes
+mimage_0, mimage_1, and mlog.
+.TP
+.I \-c, \-\-colon
+Generate colon separated output for easier parsing in scripts or programs.
+N.B. \fBlvs\fP (8) provides considerably more control over the output.
+.nf
+
+The values are:
+
+* logical volume name
+* volume group name
+* logical volume access
+* logical volume status
+* internal logical volume number
+* open count of logical volume
+* logical volume size in sectors
+* current logical extents associated to logical volume
+* allocated logical extents of logical volume
+* allocation policy of logical volume
+* read ahead sectors of logical volume
+* major device number of logical volume
+* minor device number of logical volume
+
+.fi
+.TP
+.I \-m, \-\-maps
+Display the mapping of logical extents to physical volumes and
+physical extents.  To map physical extents
+to logical extents use
+.BR
+\fBpvs --segments -o+lv_name,seg_start_pe,segtype\fP.
+.TP
+.I \-\-columns | \-C
+Display output in columns, the equivalent of \fBlvs\fP.  Options listed
+are the same as options given in \fBlvs (8)\fP.
+.SH Examples
+"lvdisplay -v /dev/vg00/lvol2" shows attributes of that logical volume.
+If snapshot
+logical volumes have been created for this original logical volume,
+this command shows a list of all snapshot logical volumes and their
+status (active or inactive) as well.
+
+"lvdisplay /dev/vg00/snapshot" shows the attributes of this snapshot
+logical volume and also which original logical volume
+it is associated with.
+
+.SH SEE ALSO
+.BR lvm (8), 
+.BR lvcreate (8), 
+.BR lvscan (8),
+.BR pvs (8)
diff --git a/man/lvextend.8.in b/man/lvextend.8.in
new file mode 100644 (file)
index 0000000..0b273ab
--- /dev/null
@@ -0,0 +1,101 @@
+.TH LVEXTEND 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvextend \- extend the size of a logical volume
+.SH SYNOPSIS
+.B lvextend
+[\-\-alloc AllocationPolicy]
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-i|\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+{\-l|\-\-extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |
+\-L|\-\-size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-f|\-\-force]
+[\-n|\-\-nofsck]
+[\-r|\-\-resizefs]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [PhysicalVolumePath[:PE[-PE]]...]
+.SH DESCRIPTION
+lvextend allows you to extend the size of a logical volume.
+Extension of snapshot logical volumes (see
+.B lvcreate(8)
+for information to create snapshots) is supported as well.
+But to change the number of copies in a mirrored logical
+volume use 
+.BR lvconvert (8).
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-l, \-\-extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}]
+Extend or set the logical volume size in units of logical extents.
+With the + sign the value is added to the actual size
+of the logical volume and without it, the value is taken as an absolute one.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, relative to the existing
+size of the Logical Volume with the suffix %LV, of the remaining
+free space for the specified PhysicalVolume(s) with the suffix %PVS,
+as a percentage of the remaining free space in the Volume Group
+with the suffix %FREE, or (for a snapshot) as a percentage of the total
+space in the Origin Logical Volume with the suffix %ORIGIN.
+.TP
+.I \-L, \-\-size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Extend or set the logical volume size in units of megabytes.
+A size suffix of M for megabytes,
+G for gigabytes, T for terabytes, P for petabytes 
+or E for exabytes is optional.
+With the + sign the value is added to the actual size
+of the logical volume and without it, the value is taken as an absolute one.
+.TP
+.I \-i, \-\-stripes Stripes
+Gives the number of stripes for the extension.
+Not applicable to LVs using the original metadata LVM format, which must
+use a single value throughout.
+.TP
+.I \-I, \-\-stripesize StripeSize
+Gives the number of kilobytes for the granularity of the stripes.
+Not applicable to LVs using the original metadata LVM format, which must
+use a single value throughout.
+.br
+StripeSize must be 2^n (n = 2 to 9)
+.TP
+.I \-f, \-\-force
+Proceed with size extension without prompting.
+.TP
+.I \-n, \-\-nofsck
+Do not perform fsck before extending filesystem when filesystem
+requires it. You may need to use \fB--force\fR to proceed with 
+this option.
+.TP
+.I \-r, \-\-resizefs
+Resize underlying filesystem together with the logical volume using 
+\fBfsadm\fR(8).
+.SH Examples
+"lvextend -L +54 /dev/vg01/lvol10 /dev/sdk3" tries to extend the size of
+that logical volume by 54MB on physical volume /dev/sdk3.
+This is only possible if /dev/sdk3 is a member of volume group vg01 and
+there are enough free physical extents in it.
+
+"lvextend /dev/vg01/lvol01 /dev/sdk3" tries to extend the size of that
+logical volume by the amount of free space on physical volume /dev/sdk3.
+This is equivalent to specifying "-l +100%PVS" on the command line.
+
+.br
+"lvextend -L+16M vg01/lvol01 /dev/sda:8-9 /dev/sdb:8-9"
+.br
+tries to extend a logical volume "vg01/lvol01" by 16MB using physical extents
+/dev/sda:8-9 and /dev/sdb:8-9 for allocation of extents.
+
+.SH SEE ALSO
+.BR fsadm (8),
+.BR lvm (8), 
+.BR lvcreate (8), 
+.BR lvconvert (8), 
+.BR lvreduce (8), 
+.BR lvresize (8), 
+.BR lvchange (8)
diff --git a/man/lvm.8.in b/man/lvm.8.in
new file mode 100644 (file)
index 0000000..37ebdab
--- /dev/null
@@ -0,0 +1,328 @@
+.TH LVM 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvm \- LVM2 tools
+.SH SYNOPSIS
+.B lvm
+[command | file]
+.SH DESCRIPTION
+\fBlvm\fP provides the command-line tools for LVM2.  A separate
+manual page describes each command in detail.
+.LP
+If \fBlvm\fP is invoked with no arguments it presents a readline prompt
+(assuming it was compiled with readline support).
+LVM commands may be entered interactively at this prompt with 
+readline facilities including history and command name and option 
+completion.  Refer to \fBreadline\fP(3) for details.
+.LP
+If \fBlvm\fP is invoked with argv[0] set to the name of a specific
+LVM command (for example by using a hard or soft link) it acts as 
+that command.  
+.LP
+On invocation, \fBlvm\fP requires that only the standard file descriptors
+stdin, stdout and stderr are available.  If others are found, they
+get closed and messages are issued warning about the leak.
+.LP
+Where commands take VG or LV names as arguments, the full path name is
+optional.  An LV called "lvol0" in a VG called "vg0" can be specified
+as "vg0/lvol0".  Where a list of VGs is required but is left empty, 
+a list of all VGs will be substituted.  Where a list of LVs is required
+but a VG is given, a list of all the LVs in that VG will be substituted.
+So "lvdisplay vg0" will display all the LVs in "vg0".
+Tags can also be used - see \fBaddtag\fP below.
+.LP
+One advantage of using the built-in shell is that configuration 
+information gets cached internally between commands.  
+.LP
+A file containing a simple script with one command per line 
+can also be given on the command line.  The script can also be
+executed directly if the first line is #! followed by the absolute
+path of \fBlvm\fP.
+.SH BUILT-IN COMMANDS
+The following commands are built into lvm without links normally
+being created in the filesystem for them.
+.TP
+\fBdumpconfig\fP \(em Display the configuration information after 
+loading \fBlvm.conf\fP (5) and any other configuration files.
+.TP
+\fBformats\fP \(em Display recognised metadata formats.
+.TP
+\fBhelp\fP \(em Display the help text.
+.TP
+\fBpvdata\fP \(em Not implemented in LVM2.
+.TP
+\fBsegtypes\fP \(em Display recognised logical volume segment types.
+.TP
+\fBversion\fP \(em Display version information.
+.LP
+.SH COMMANDS
+The following commands implement the core LVM functionality.
+.TP
+\fBpvchange\fP \(em Change attributes of a physical volume.
+.TP
+\fBpvck\fP \(em Check physical volume metadata.
+.TP
+\fBpvcreate\fP \(em Initialize a disk or partition for use by LVM.
+.TP
+\fBpvdisplay\fP \(em Display attributes of a physical volume.
+.TP
+\fBpvmove\fP \(em Move physical extents.
+.TP
+\fBpvremove\fP \(em Remove a physical volume.
+.TP
+\fBpvresize\fP \(em Resize a disk or partition in use by LVM2.
+.TP
+\fBpvs\fP \(em Report information about physical volumes.
+.TP
+\fBpvscan\fP \(em Scan all disks for physical volumes.
+.TP
+\fBvgcfgbackup\fP \(em Backup volume group descriptor area.
+.TP
+\fBvgcfgrestore\fP \(em Restore volume group descriptor area.
+.TP
+\fBvgchange\fP \(em Change attributes of a volume group.
+.TP
+\fBvgck\fP \(em Check volume group metadata.
+.TP
+\fBvgconvert\fP \(em Convert volume group metadata format.
+.TP
+\fBvgcreate\fP \(em Create a volume group.
+.TP
+\fBvgdisplay\fP \(em Display attributes of volume groups.
+.TP
+\fBvgexport\fP \(em Make volume groups unknown to the system.
+.TP
+\fBvgextend\fP \(em Add physical volumes to a volume group.
+.TP
+\fBvgimport\fP \(em Make exported volume groups known to the system.
+.TP
+\fBvgimportclone\fP \(em Import and rename duplicated volume group (e.g. a hardware snapshot).
+.TP
+\fBvgmerge\fP \(em Merge two volume groups.
+.TP
+\fBvgmknodes\fP \(em Recreate volume group directory and logical volume special files
+.TP
+\fBvgreduce\fP \(em Reduce a volume group by removing one or more physical volumes.
+.TP
+\fBvgremove\fP \(em Remove a volume group.
+.TP
+\fBvgrename\fP \(em Rename a volume group.
+.TP
+\fBvgs\fP \(em Report information about volume groups.
+.TP
+\fBvgscan\fP \(em Scan all disks for volume groups and rebuild caches.
+.TP
+\fBvgsplit\fP \(em Split a volume group into two, moving any logical volumes from one volume group to another by moving entire physical volumes.
+.TP
+\fBlvchange\fP \(em Change attributes of a logical volume.
+.TP
+\fBlvconvert\fP \(em Convert a logical volume from linear to mirror or snapshot.
+.TP
+\fBlvcreate\fP \(em Create a logical volume in an existing volume group.
+.TP
+\fBlvdisplay\fP \(em Display attributes of a logical volume.
+.TP
+\fBlvextend\fP \(em Extend the size of a logical volume.
+.TP
+\fBlvmchange\fP \(em Change attributes of the logical volume manager.
+.TP
+\fBlvmdiskscan\fP \(em Scan for all devices visible to LVM2.
+.TP
+\fBlvmdump\fP \(em Create lvm2 information dumps for diagnostic purposes.
+.TP
+\fBlvreduce\fP \(em Reduce the size of a logical volume.
+.TP
+\fBlvremove\fP \(em Remove a logical volume.
+.TP
+\fBlvrename\fP \(em Rename a logical volume.
+.TP
+\fBlvresize\fP \(em Resize a logical volume.
+.TP
+\fBlvs\fP \(em Report information about logical volumes.
+.TP
+\fBlvscan\fP \(em Scan (all disks) for logical volumes.
+.TP
+The following commands are not implemented in LVM2 but might be in the future: lvmsadc, lvmsar, pvdata.
+.SH OPTIONS
+The following options are available for many of the commands. 
+They are implemented generically and documented here rather 
+than repeated on individual manual pages.
+.TP
+\fB-h | --help\fP \(em Display the help text.
+.TP
+\fB--version\fP \(em Display version information.
+.TP
+\fB-v | --verbose\fP \(em Set verbose level.
+Repeat from 1 to 3 times to increase the detail of messages 
+sent to stdout and stderr.  Overrides config file setting.
+.TP
+\fB-d | --debug\fP \(em Set debug level.
+Repeat from 1 to 6 times to increase the detail of messages sent 
+to the log file and/or syslog (if configured).
+Overrides config file setting.
+.TP
+\fB--quiet\fP \(em Suppress output and log messages.
+Overrides -d and -v.
+.TP
+\fB-t | --test\fP \(em Run in test mode.
+Commands will not update metadata.
+This is implemented by disabling all metadata writing but nevertheless
+returning success to the calling function.  This may lead to unusual
+error messages in multi-stage operations if a tool relies on reading
+back metadata it believes has changed but hasn't.
+.TP
+\fB--driverloaded\fP { \fBy\fP | \fBn\fP }
+Whether or not the device-mapper kernel driver is loaded.
+If you set this to \fBn\fP, no attempt will be made to contact the driver.
+.TP
+\fB-A | --autobackup\fP { \fBy\fP | \fBn\fP }
+Whether or not to metadata should be backed up automatically after a change.  
+You are strongly advised not to disable this!
+See
+.B vgcfgbackup (8).
+.TP
+\fB-P | --partial\fP
+When set, the tools will do their best to provide access to volume groups
+that are only partially available (one or more physical volumes belonging
+to the volume group are missing from the system).  Where part of a logical
+volume is missing, \fB/dev/ioerror\fP will be substituted, and you could use
+\fBdmsetup (8)\fP to set this up to return I/O errors when accessed,
+or create it as a large block device of nulls.  Metadata may not be
+changed with this option. To insert a replacement physical volume
+of the same or large size use \fBpvcreate -u\fP to set the uuid to 
+match the original followed by \fBvgcfgrestore (8)\fP.
+.TP
+\fB-M | --metadatatype type\fP
+Specifies which type of on-disk metadata to use, such as \fBlvm1\fP 
+or \fBlvm2\fP, which can be abbreviated to \fB1\fP or \fB2\fP respectively.
+The default (lvm2) can be changed by setting \fBformat\fP in the \fBglobal\fP
+section of the config file.
+.TP
+\fB--ignorelockingfailure\fP
+This lets you proceed with read-only metadata operations such as
+\fBlvchange -ay\fP and \fBvgchange -ay\fP even if the locking module fails.
+One use for this is in a system init script if the lock directory 
+is mounted read-only when the script runs.
+.TP
+\fB--addtag tag\fP
+Add the tag \fBtag\fP to a PV, VG or LV.  
+Supply this argument multiple times to add more than one tag at once.
+A tag is a word that can be used to group LVM2 objects of the same type 
+together. 
+Tags can be given on the command line in place of PV, VG or LV 
+arguments.  Tags should be prefixed with @ to avoid ambiguity.
+Each tag is expanded by replacing it with all objects possessing
+that tag which are of the type expected by its position on the command line.
+PVs can only possess tags while they are part of a Volume Group:
+PV tags are discarded if the PV is removed from the VG.
+As an example, you could tag some LVs as \fBdatabase\fP and others 
+as \fBuserdata\fP and then activate the database ones 
+with \fBlvchange -ay @database\fP.
+Objects can possess multiple tags simultaneously.
+Only the new LVM2 metadata format supports tagging: objects using the
+LVM1 metadata format cannot be tagged because the on-disk format does not
+support it.
+Snapshots cannot be tagged.
+Characters allowed in tags are: A-Z a-z 0-9 _ + . - and 
+as of version 2.02.78 the following characters are also
+accepted: / = ! : # &
+.TP
+\fB--deltag tag\fP
+Delete the tag \fBtag\fP from a PV, VG or LV, if it's present.
+Supply this argument multiple times to remove more than one tag at once.
+.TP
+\fB--alloc AllocationPolicy\fP
+The allocation policy to use: \fBcontiguous\fP, \fBcling\fP, \fBnormal\fP, \fBanywhere\fP or \fBinherit\fP.
+When a command needs to allocate physical extents from the volume group,
+the allocation policy controls how they are chosen.  
+Each volume group and logical volume has an allocation policy.
+The default for a volume group is \fBnormal\fP which applies
+common-sense rules such as not placing parallel stripes on the same
+physical volume.  The default for a logical volume is \fBinherit\fP
+which applies the same policy as for the volume group.  These policies can
+be changed using \fBlvchange\fP (8) and \fBvgchange\fP (8) or over-ridden
+on the command line of any command that performs allocation.
+The \fBcontiguous\fP policy requires that new extents be placed adjacent
+to existing extents. 
+The \fBcling\fP policy places new extents on the same physical
+volume as existing extents in the same stripe of the Logical Volume.
+If there are sufficient free extents to satisfy
+an allocation request but \fBnormal\fP doesn't use them,
+\fBanywhere\fP will - even if that reduces performance by
+placing two stripes on the same physical volume.
+.IP
+N.B. The policies described above are not implemented fully yet.
+In particular, contiguous free space cannot be broken up to
+satisfy allocation attempts.
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBLVM_SYSTEM_DIR\fP 
+Directory containing lvm.conf and other LVM
+system files.
+Defaults to "#DEFAULT_SYS_DIR#".
+.TP
+\fBHOME\fP 
+Directory containing .lvm_history if the internal readline shell
+is invoked.
+.TP
+\fBLVM_VG_NAME\fP 
+The volume group name that is assumed for 
+any reference to a logical volume that doesn't specify a path.
+Not set by default.
+.SH VALID NAMES
+The following characters are valid for VG and LV names:
+\fBa-z A-Z 0-9 + _ . -\fP
+.LP
+VG and LV names cannot begin with a hyphen.
+There are also various reserved names that are used internally by lvm that can not be used as LV or VG names.
+A VG cannot be called anything that exists in /dev/ at the time of creation, nor can it be called '.' or '..'.
+A LV cannot be called '.' '..' 'snapshot' or 'pvmove'. The LV name may also not contain the strings '_mlog' or '_mimage'
+
+
+.SH DIAGNOSTICS
+All tools return a status code of zero on success or non-zero on failure.
+.SH FILES
+.I #DEFAULT_SYS_DIR#/lvm.conf
+.br
+.I $HOME/.lvm_history
+.SH SEE ALSO
+.BR clvmd (8),
+.BR lvchange (8),
+.BR lvcreate (8),
+.BR lvdisplay (8),
+.BR lvextend (8),
+.BR lvmchange (8),
+.BR lvmdiskscan (8),
+.BR lvreduce (8),
+.BR lvremove (8),
+.BR lvrename (8),
+.BR lvresize (8),
+.BR lvs (8),
+.BR lvscan (8),
+.BR pvchange (8),
+.BR pvck (8),
+.BR pvcreate (8),
+.BR pvdisplay (8),
+.BR pvmove (8),
+.BR pvremove (8),
+.BR pvs (8),
+.BR pvscan (8),
+.BR vgcfgbackup (8),
+.BR vgchange  (8),
+.BR vgck (8),
+.BR vgconvert (8),
+.BR vgcreate (8),
+.BR vgdisplay (8),
+.BR vgextend (8),
+.BR vgimport (8),
+.BR vgimportclone (8),
+.BR vgmerge (8),
+.BR vgmknodes (8),
+.BR vgreduce (8),
+.BR vgremove (8),
+.BR vgrename (8),
+.BR vgs (8),
+.BR vgscan (8),
+.BR vgsplit (8),
+.BR readline (3),
+.BR lvm.conf (5)
+
diff --git a/man/lvm.conf.5.in b/man/lvm.conf.5.in
new file mode 100644 (file)
index 0000000..ac16b1d
--- /dev/null
@@ -0,0 +1,497 @@
+.TH LVM.CONF 5 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvm.conf \- Configuration file for LVM2
+.SH SYNOPSIS
+.B #DEFAULT_SYS_DIR#/lvm.conf
+.SH DESCRIPTION
+lvm.conf is loaded during the initialisation phase of 
+\fBlvm\fP (8).  This file can in turn lead to other files
+being loaded - settings read in later override earlier
+settings.  File timestamps are checked between commands and if 
+any have changed, all the files are reloaded.  
+.LP
+Use \fBlvm dumpconfig\fP to check what settings are in use.
+.SH SYNTAX
+.LP
+This section describes the configuration file syntax.
+.LP
+Whitespace is not significant unless it is within quotes. 
+This provides a wide choice of acceptable indentation styles.
+Comments begin with # and continue to the end of the line.
+They are treated as whitespace.
+.LP
+Here is an informal grammar:
+.TP
+\fBfile = value*\fP
+.br
+A configuration file consists of a set of values.
+.TP
+\fBvalue = section | assignment\fP
+.br
+A value can either be a new section, or an assignment.
+.TP
+\fBsection = identifier '{' value* '}'\fP
+.br
+A section is groups associated values together.  
+.br
+It is denoted by a name and delimited by curly brackets.
+.br
+e.g.   backup {
+.br
+               ...
+.br
+       }
+.TP
+\fBassignment = identifier '=' (array | type)\fP
+.br
+An assignment associates a type with an identifier.
+.br
+e.g.   max_archives = 42
+.br
+.TP
+\fBarray = '[' (type ',')* type ']' | '[' ']'\fP
+.br
+Inhomogeneous arrays are supported. 
+.br
+Elements must be separated by commas.  
+.br
+An empty array is acceptable.
+.TP
+\fBtype = integer | float | string\fP
+\fBinteger = [0-9]*\fP
+.br
+\fBfloat = [0-9]*\.[0-9]*\fP
+.br
+\fBstring = '"' .* '"'\fP
+.IP
+Strings must be enclosed in double quotes.
+
+.SH SECTIONS
+.LP
+The sections that may be present in the file are:
+.TP
+\fBdevices\fP \(em Device settings
+.IP
+\fBdir\fP \(em Directory in which to create volume group device nodes.
+Defaults to "/dev".  Commands also accept this as a prefix on volume 
+group names.
+.IP
+\fBscan\fP \(em List of directories to scan recursively for 
+LVM physical volumes.
+Devices in directories outside this hierarchy will be ignored.
+Defaults to "/dev".
+.IP
+\fBpreferred_names\fP \(em List of patterns compared in turn against
+all the pathnames referencing the same device in in the scanned directories.
+The pathname that matches the earliest pattern in the list is the
+one used in any output.  As an example, if device-mapper multipathing
+is used, the following will select multipath device names:
+.br
+\fBdevices { preferred_names = [ "^/dev/mapper/mpath" ] }\fP
+.IP
+\fBfilter\fP \(em List of patterns to apply to devices found by a scan.
+Patterns are regular expressions delimited by any character and preceded
+by \fBa\fP (for accept) or \fBr\fP (for reject).  The list is traversed
+in order, and the first regex that matches determines if the device
+will be accepted or rejected (ignored).  Devices that don't match
+any patterns are accepted. If you want to reject patterns that
+don't match, end the list with "r/.*/".
+If there are several names for the same device (e.g. symbolic links
+in /dev), if any name matches any \fBa\fP pattern, the
+device is accepted; otherwise if any name matches any \fBr\fP
+pattern it is rejected; otherwise it is accepted.
+As an example, to ignore /dev/cdrom you could use:
+.br
+\fBdevices { filter=["r|cdrom|"] }\fP 
+.IP
+\fBcache_dir\fP \(em Persistent filter cache file directory.
+Defaults to "#DEFAULT_CACHE_DIR#".
+.IP
+\fBwrite_cache_state\fP \(em Set to 0 to disable the writing out of the 
+persistent filter cache file when \fBlvm\fP exits.
+Defaults to 1.
+.IP
+\fBtypes\fP \(em List of pairs of additional acceptable block device types
+found in /proc/devices together with maximum (non-zero) number of
+partitions (normally 16).  By default, LVM2 supports ide, sd, md, loop, 
+dasd, dac960, nbd, ida, cciss, ubd, ataraid, drbd, power2, i2o_block
+and iseries/vd.  Block devices with major
+numbers of different types are ignored by LVM2.  
+Example: \fBtypes = ["fd", 16]\fP.
+To create physical volumes on device-mapper volumes
+created outside LVM2, perhaps encrypted ones from \fBcryptsetup\fP,
+you'll need \fBtypes = ["device-mapper", 16]\fP.  But if you do this,
+be careful to avoid recursion within LVM2.  The figure for number 
+of partitions is not currently used in LVM2 - and might never be.
+.IP
+\fBsysfs_scan\fP \(em If set to 1 and your kernel supports sysfs and 
+it is mounted, sysfs will be used as a quick way of filtering out
+block devices that are not present.
+.IP
+\fBmd_component_detection\fP \(em If set to 1, LVM2 will ignore devices
+used as components of software RAID (md) devices by looking for md
+superblocks. This doesn't always work satisfactorily e.g. if a device 
+has been reused without wiping the md superblocks first.
+.IP
+\fBmd_chunk_alignment\fP \(em If set to 1, and a Physical Volume is placed
+directly upon an md device, LVM2 will align its data blocks with the
+md device's stripe-width.
+.IP
+\fBdata_alignment_detection\fP \(em If set to 1, and your kernel provides
+topology information in sysfs for the Physical Volume, the start of data
+area will be aligned on a multiple of the ’minimum_io_size’ or
+’optimal_io_size’ exposed in sysfs.  minimum_io_size is the smallest
+request the device can perform without incurring a read-modify-write
+penalty (e.g. MD's chunk size).  optimal_io_size is the device's
+preferred unit of receiving I/O (e.g. MD's stripe width).  minimum_io_size
+is used if optimal_io_size is undefined (0).  If both \fBmd_chunk_alignment\fP
+and \fBdata_alignment_detection\fP are enabled the result of
+\fBdata_alignment_detection\fP is used.
+.IP
+\fBdata_alignment\fP \(em Default alignment (in KB) of start of data area
+when creating a new Physical Volume using the \fBlvm2\fP format.
+If a Physical Volume is placed directly upon an md device and
+\fBmd_chunk_alignment\fP or \fBdata_alignment_detection\fP is enabled
+this parameter is ignored.  Set to 0 to use the default alignment of
+64KB or the page size, if larger.
+.IP
+\fBdata_alignment_offset_detection\fP \(em If set to 1, and your kernel
+provides topology information in sysfs for the Physical Volume, the
+start of the aligned data area of the Physical Volume will be shifted
+by the alignment_offset exposed in sysfs.
+.sp
+To see the location of the first Physical Extent of an existing Physical Volume
+use \fBpvs -o +pe_start\fP .  It will be a multiple of the requested
+\fBdata_alignment\fP plus the alignment_offset from
+\fBdata_alignment_offset_detection\fP (if enabled) or the pvcreate
+commandline.
+.IP
+\fBdisable_after_error_count\fP \(em During each LVM operation errors received
+from each device are counted.  If the counter of a particular device exceeds
+the limit set here, no further I/O is sent to that device for the remainder of
+the respective operation. Setting the parameter to 0 disables the counters
+altogether.
+.TP
+\fBallocation\fP \(em Space allocation policies
+.IP
+\fBcling_tag_list\fP \(em List of PV tags matched by the \fBcling\fP allocation policy.
+.IP
+When searching for free space to extend an LV, the \fBcling\fP
+allocation policy will choose space on the same PVs as the last
+segment of the existing LV.  If there is insufficient space and a
+list of tags is defined here, it will check whether any of them are
+attached to the PVs concerned and then seek to match those PV tags
+between existing extents and new extents.
+.IP 
+The @ prefix for tags is required.
+Use the special tag "@*" as a wildcard to match any PV tag and so use 
+all PV tags for this purpose.
+.IP
+For example, LVs are mirrored between two sites within a single VG.
+PVs are tagged with either @site1 or @site2 to indicate where
+they are situated and these two PV tags are selected for use with this
+allocation policy:
+.IP
+cling_tag_list = [ "@site1", "@site2" ]
+.TP
+\fBlog\fP \(em Default log settings
+.IP
+\fBfile\fP \(em Location of log file.  If this entry is not present, no
+log file is written.
+.IP
+\fBoverwrite\fP \(em Set to 1 to overwrite the log file each time a tool
+is invoked.  By default tools append messages to the log file.
+.IP
+\fBlevel\fP \(em Log level (0-9) of messages to write to the file.
+9 is the most verbose; 0 should produce no output.
+.IP
+\fBverbose\fP \(em Default level (0-3) of messages sent to stdout or stderr.
+3 is the most verbose; 0 should produce the least output.
+.IP
+\fBsyslog\fP \(em Set to 1 (the default) to send log messages through syslog.
+Turn off by setting to 0.  If you set to an integer greater than one,
+this is used - unvalidated - as the facility.  The default is LOG_USER.  
+See /usr/include/sys/syslog.h for safe facility values to use.
+For example, LOG_LOCAL0 might be 128.
+.IP
+\fBindent\fP \(em When set to 1 (the default) messages are indented 
+according to their severity, two spaces per level.  
+Set to 0 to turn off indentation.
+.IP
+\fBcommand_names\fP \(em When set to 1, the command name is used as a
+prefix for each message.
+Default is 0 (off).
+.IP
+\fBprefix\fP \(em Prefix used for all messages (after the command name).
+Default is two spaces.
+.IP
+\fBactivation\fP \(em Set to 1 to log messages while
+devices are suspended during activation.  
+Only set this temporarily while debugging a problem because
+in low memory situations this setting can cause your machine to lock up.
+.TP
+\fBbackup\fP \(em Configuration for metadata backups.
+.IP
+\fBarchive_dir\fP \(em Directory used for automatic metadata archives.
+Backup copies of former metadata for each volume group are archived here.
+Defaults to "#DEFAULT_ARCHIVE_DIR#".
+.IP
+\fBbackup_dir\fP \(em Directory used for automatic metadata backups.
+A single backup copy of the current metadata for each volume group 
+is stored here.
+Defaults to "#DEFAULT_BACKUP_DIR#".
+.IP
+\fBarchive\fP \(em Whether or not tools automatically archive existing
+metadata into \fBarchive_dir\fP before making changes to it.
+Default is 1 (automatic archives enabled).  
+Set to 0 to disable.  
+Disabling this might make metadata recovery difficult or impossible 
+if something goes wrong.
+.IP
+\fBbackup\fP \(em Whether or not tools make an automatic backup 
+into \fBbackup_dir\fP after changing metadata.  
+Default is 1 (automatic backups enabled).  Set to 0 to disable.
+Disabling this might make metadata recovery difficult or impossible 
+if something goes wrong.
+.IP
+\fBretain_min\fP \(em Minimum number of archives to keep.
+Defaults to 10.
+.IP
+\fBretain_days\fP \(em Minimum number of days to keep archive files.
+Defaults to 30.
+.TP
+\fBshell\fP \(em LVM2 built-in readline shell settings
+.IP
+\fBhistory_size\fP \(em Maximum number of lines of shell history to retain (default 100) in $HOME/.lvm_history
+.TP
+\fBglobal\fP \(em Global settings
+.IP
+\fBtest\fP \(em If set to 1, run tools in test mode i.e. no changes to
+the on-disk metadata will get made.  It's equivalent to having the
+-t option on every command.
+.IP
+\fBactivation\fP \(em Set to 0 to turn off all communication with
+the device-mapper driver.  Useful if you want to manipulate logical
+volumes while device-mapper is not present in your kernel.
+.IP
+\fBproc\fP \(em Mount point of proc filesystem.
+Defaults to /proc.
+.IP
+\fBumask\fP \(em File creation mask for any files and directories created.
+Interpreted as octal if the first digit is zero.
+Defaults to 077.  
+Use 022 to allow other users to read the files by default.
+.IP
+\fBformat\fP \(em The default value of \fB--metadatatype\fP used
+to determine which format of metadata to use when creating new 
+physical volumes and volume groups. \fBlvm1\fP or \fBlvm2\fP.
+.IP
+\fBfallback_to_lvm1\fP \(em Set this to 1 if you need to 
+be able to switch between 2.4 kernels using LVM1 and kernels
+including device-mapper.
+The LVM2 tools should be installed as normal and
+the LVM1 tools should be installed with a .lvm1 suffix e.g.
+vgscan.lvm1.  
+If an LVM2 tool is then run but unable to communicate
+with device-mapper, it will automatically invoke the equivalent LVM1 
+version of the tool.  Note that for LVM1 tools to 
+manipulate physical volumes and volume groups created by LVM2 you 
+must use \fB--metadataformat lvm1\fP when creating them.
+.IP
+\fBlibrary_dir\fP \(em A directory searched for LVM2's shared libraries
+ahead of the places \fBdlopen\fP (3) searches.
+.IP
+\fBformat_libraries\fP \(em A list of shared libraries to load that contain
+code to process different formats of metadata. For example, liblvm2formatpool.so
+is needed to read GFS pool metadata if LVM2 was configured \fB--with-pool=shared\fP.
+.IP
+\fBlocking_type\fP \(em What type of locking to use.
+1 is the default, which use flocks on files in \fBlocking_dir\fP
+(see below) to
+avoid conflicting LVM2 commands running concurrently on a single
+machine. 0 disables locking and risks corrupting your metadata.
+If set to 2, the tools will load the external \fBlocking_library\fP
+(see below).
+If the tools were configured \fB--with-cluster=internal\fP 
+(the default) then 3 means to use built-in cluster-wide locking.
+Type 4 enforces read-only metadata and forbids any operations that
+might want to modify Volume Group metadata.
+All changes to logical volumes and their states are communicated
+using locks.
+.IP
+\fBwait_for_locks\fP \(em When set to 1, the default, the tools
+wait if a lock request cannot be satisfied immediately.
+When set to 0, the operation is aborted instead.
+.IP
+\fBlocking_dir\fP \(em The directory LVM2 places its file locks
+if \fBlocking_type\fP is set to 1.  The default is \fB/var/lock/lvm\fP.
+.IP
+\fBlocking_library\fP \(em The name of the external locking
+library to load if \fBlocking_type\fP is set to 2.
+The default is \fBliblvm2clusterlock.so\fP.  If you need to write
+such a library, look at the lib/locking source code directory.
+.TP
+\fBtags\fP \(em Host tag settings
+.IP
+\fBhosttags\fP \(em If set to 1, create a host tag with the machine name.
+Setting this to 0 does nothing, neither creating nor destroying any tag.
+The machine name used is the nodename as returned by \fBuname\fP (2).
+.IP
+Additional host tags to be set can be listed here as subsections. 
+The @ prefix for tags is optional.
+Each of these host tag subsections can contain a \fBhost_list\fP 
+array of host names. If any one of these entries matches the machine 
+name exactly then the host tag gets defined on this particular host, 
+otherwise it doesn't.
+.IP
+After lvm.conf has been processed, LVM2 works through each host
+tag that has been defined in turn, and if there is a configuration
+file called lvm_\fB<host_tag>\fP.conf it attempts to load it.
+Any settings read in override settings found in earlier files.
+Any additional host tags defined get appended to the search list,
+so in turn they can lead to further configuration files being processed.
+Use \fBlvm dumpconfig\fP to check the result of config
+file processing.
+.IP
+The following example always sets host tags \fBtag1\fP and
+sets \fBtag2\fP on machines fs1 and fs2:
+.IP
+tags { tag1 { } tag2 { host_list = [ "fs1", "fs2" ] } }
+.IP
+These options are useful if you are replicating configuration files
+around a cluster.  Use of \fBhosttags = 1\fP means every machine
+can have static and identical local configuration files yet use 
+different settings and activate different logical volumes by
+default.  See also \fBvolume_list\fP below and \fB--addtag\fP 
+in \fBlvm\fP (8).
+.TP
+\fBactivation\fP \(em Settings affecting device-mapper activation
+.IP
+\fBmissing_stripe_filler\fP \(em When activating an incomplete logical
+volume in partial mode, this option dictates how the missing data is
+replaced.  A value of "error" will cause activation to create error
+mappings for the missing data, meaning that read access to missing
+portions of the volume will result in I/O errors. You can instead also
+use a device path, and in that case this device will be used in place of
+missing stripes. However, note that using anything other than
+"error" with mirrored or snapshotted volumes is likely to result in data
+corruption.  For instructions on how to create a device that always
+returns zeros, see \fBlvcreate\fP (8).
+.IP
+\fBmirror_region_size\fP \(em Unit size in KB for copy operations
+when mirroring.
+.IP
+\fBreadahead\fP \(em Used when there is no readahead value stored 
+in the volume group metadata.  Set to \fBnone\fP to disable
+readahead in these circumstances or \fBauto\fP to use the default
+value chosen by the kernel.
+.IP
+\fBreserved_memory\fP, \fBreserved_stack\fP \(em How many KB to reserve 
+for LVM2 to use while logical volumes are suspended.  If insufficient 
+memory is reserved before suspension, there is a risk of machine deadlock.
+.IP
+\fBprocess_priority\fP \(em The nice value to use while devices are
+suspended.  This is set to a high priority so that logical volumes
+are suspended (with I/O generated by other processes to those
+logical volumes getting queued) for the shortest possible time.
+.IP
+\fBvolume_list\fP \(em This acts as a filter through which
+all requests to activate a logical volume on this machine
+are passed.  A logical volume is only activated if it matches
+an item in the list.  Tags must be preceded by @ and are checked
+against all tags defined in the logical volume and volume group 
+metadata for a match.
+@* is short-hand to check every tag set on the host machine (see
+\fBtags\fP above).
+Logical volume and volume groups can also be included in the list
+by name e.g. vg00, vg00/lvol1.
+.TP
+\fBmetadata\fP \(em Advanced metadata settings
+.IP
+\fBpvmetadatacopies\fP \(em When creating a physical volume using the
+LVM2 metadata format, this is the default number of copies of metadata
+to store on each physical volume.  
+Currently it can be set to 0, 1 or 2.  The default is 1.  
+If set to 2, one copy is placed at the beginning of the disk
+and the other is placed at the end.
+It can be overridden on the command line with \fB--pvmetadatacopies\fP
+(see \fBpvcreate\fP).
+If creating a volume group with just one physical volume, it's a
+good idea to have 2 copies.  If creating a large volume group with
+many physical volumes, you may decide that 3 copies of the metadata
+is sufficient, i.e. setting it to 1 on three of the physical volumes,
+and 0 on the rest.  Every volume group must contain at least one 
+physical volume with at least 1 copy of the metadata (unless using
+the text files described below).  The disadvantage of having lots
+of copies is that every time the tools access the volume group, every
+copy of the metadata has to be accessed, and this slows down the
+tools.
+.IP
+\fBpvmetadatasize\fP \(em Approximate number of sectors to set aside
+for each copy of the metadata. Volume groups with large numbers of
+physical or logical volumes, or volumes groups containing complex 
+logical volume structures will need additional space for their metadata.
+The metadata areas are treated as circular buffers, so
+unused space becomes filled with an archive of the most recent
+previous versions of the metadata.
+.IP
+\fBpvmetadataignore\fP When creating a physical volume using the LVM2
+metadata format, this states whether metadata areas should be ignored.
+The default is "n".  If metadata areas on a physical volume are ignored,
+LVM will not not store metadata in the metadata areas present on newly
+created Physical Volumes.  The option can be overridden on the command
+line with \fB--metadataignore\fP (See \fBpvcreate\fP and \fBpvchange\fP).
+Metadata areas cannot be created or extended after Logical Volumes have
+been allocated on the device.
+If you do not want to store metadata on this device, it is still wise
+always to allocate a metadata area (use a non-zero value for
+\fB--pvmetadatacopies\fP) in case you need it in the future and to use
+this option to instruct LVM2 to ignore it.
+.IP
+\fBvgmetadatacopies\fP \(em When creating a volume group using the
+LVM2 metadata format, this is the default number of copies of metadata
+desired across all the physical volumes in the volume group.  If set to
+a non-zero value, LVM will automatically set or clear the metadataignore
+flag on the physical volumes (see \fBpvcreate\fP and \fBpvchange\fP
+\fB--metadataignore\fP) in order to achieve the desired number of metadata
+copies.  An LVM command that adds or removes physical volumes (for example,
+\fBvgextend\fP, \fBvgreduce\fP, \fBvgsplit\fP, or \fBvgmerge\fP), may cause
+LVM to automatically set or clear the metadataignore flags.  Also, if
+physical volumes go missing or reappear, or a new number of copies is
+explicitly set (see \fBvgchange --vgmetadatacopies\fP), LVM may adjust
+the metadataignore flags.
+Set \fBvgmetadatacopies\fP to 0 instructs LVM not to set or clear the
+metadataignore flags automatically.  You may set a value larger than the
+sum of all metadata areas on all physical volumes.  The value can
+be overridden on the command line with \fB--vgmetadatacopies\fP for various
+commands (for example, \fBvgcreate\fP and \fBvgchange\fP), and can be
+queryied with the \fBvg_mda_copies\fP field of \fBvgs\fP.  This option
+is useful for volume groups containing large numbers of physical volumes
+with metadata as it may be used to minimize metadata read and write overhead.
+.IP
+\fBdirs\fP \(em List of directories holding live copies of LVM2
+metadata as text files.  These directories must not be on logical
+volumes.  It is possible to use LVM2 with a couple of directories
+here, preferably on different (non-logical-volume) filesystems
+and with no other on-disk metadata, \fBpvmetadatacopies = 0\fP.
+Alternatively these directories can be in addition to the
+on-disk metadata areas.  This feature was created during the
+development of the LVM2 metadata before the new on-disk metadata
+areas were designed and no longer gets tested.  
+It is not supported under low-memory conditions, and it is 
+important never to edit these metadata files unless you fully 
+understand how things work: to make changes you should always use 
+the tools as normal, or else vgcfgbackup, edit backup, vgcfgrestore.
+.SH FILES
+.I #DEFAULT_SYS_DIR#/lvm.conf
+.I #DEFAULT_ARCHIVE_DIR#
+.I #DEFAULT_BACKUP_DIR#
+.I #DEFAULT_CACHE_DIR#/.cache
+.I #DEFAULT_LOCK_DIR#
+.SH SEE ALSO
+.BR lvm (8),
+.BR umask (2),
+.BR uname (2),
+.BR dlopen (3),
+.BR syslog (3),
+.BR syslog.conf (5)
diff --git a/man/lvmchange.8.in b/man/lvmchange.8.in
new file mode 100644 (file)
index 0000000..7c7c85a
--- /dev/null
@@ -0,0 +1,10 @@
+.TH LVMCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvmchange \- change attributes of the logical volume manager
+.SH SYNOPSIS
+.B lvmchange
+.SH DESCRIPTION
+lvmchange is not currently supported under LVM2, although
+\fBdmsetup (8)\fP has a \fBremove_all\fP command.
+.SH SEE ALSO
+.BR dmsetup (8)
diff --git a/man/lvmconf.8.in b/man/lvmconf.8.in
new file mode 100644 (file)
index 0000000..fb3d610
--- /dev/null
@@ -0,0 +1,39 @@
+.TH "LVMCONF" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH "NAME"
+.B lvmconf
+\- LVM configuration modifier
+
+.SH "SYNOPSIS"
+.B lvmconf
+[\-\-disable-cluster]
+[\-\-enable-cluster]
+[\-\-file <configfile>]
+[\-\-lockinglib <lib>]
+[\-\-lockinglibdir <dir>]
+
+.SH "DESCRIPTION"
+.B lvmconf
+is a script that modifies the locking configuration in an lvm configuration file. See \fBlvm.conf\fP(5).
+
+.SH "OPTIONS"
+.TP
+.BR \-\-disable-cluster
+Set \fBlocking_type\fR to the default non-clustered type.
+.TP
+.BR \-\-enable-cluster
+Set \fBlocking_type\fR to the default clustered type on this system.
+.TP
+.BR \-\-file " " \fI<configfile>\fR
+Apply the changes to \fBconfigfile\fR instead of the default \fB#DEFAULT_SYS_DIR#/lvm.conf\fR.
+.TP
+.BR \-\-lockinglib " " \fI<lib>\fR
+Set external \fBlocking_library\fR locking library to load if an external locking type is used.
+.TP
+.BR \-\-lockinglibdir " " \fI<dir>\fR
+.SH FILES
+.I #DEFAULT_SYS_DIR#/lvm.conf
+
+.SH "SEE ALSO"
+.BR lvm (8),
+.BR lvm.conf (5)
diff --git a/man/lvmdiskscan.8.in b/man/lvmdiskscan.8.in
new file mode 100644 (file)
index 0000000..da4188b
--- /dev/null
@@ -0,0 +1,24 @@
+.TH LVMDISKSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvmdiskscan \- scan for all devices visible to LVM2
+.SH SYNOPSIS
+.B lvmdiskscan
+[\-d|\-\-debug] [\-h|\-?|\-\-help] 
+[\-l|\-\-lvmpartition]
+[\-v|\-\-verbose]
+.SH DESCRIPTION
+\fBlvmdiskscan\fP scans all SCSI, (E)IDE disks, multiple devices and a bunch
+of other block devices in the system looking for LVM physical volumes.
+The size reported is the real device size.
+Define a filter in \fBlvm.conf\fP(5) to restrict 
+the scan to avoid a CD ROM, for example.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-l, \-\-lvmpartition
+Only reports Physical Volumes.
+.SH SEE ALSO
+.BR lvm (8), 
+.BR lvm.conf (5),
+.BR pvscan (8),
+.BR vgscan (8)
diff --git a/man/lvmdump.8.in b/man/lvmdump.8.in
new file mode 100644 (file)
index 0000000..9cce137
--- /dev/null
@@ -0,0 +1,54 @@
+.TH LVMDUMP 8 "LVM TOOLS #VERSION#" "Red Hat, Inc."
+.SH NAME
+lvmdump - create lvm2 information dumps for diagnostic purposes
+.SH SYNOPSIS
+\fBlvmdump\fP [options] [-d directory]
+.SH DESCRIPTION
+\fBlvmdump\fP is a tool to dump various information concerning LVM2. By default, it creates a tarball suitable for submission along with a problem report.
+.PP
+The content of the tarball is as follows:
+.br
+- dmsetup info
+.br
+- table of currently running processes
+.br
+- recent entries from /var/log/messages (containing system messages)
+.br
+- complete lvm configuration and cache (content of /etc/lvm)
+.br
+- list of device nodes present under /dev
+.br
+- list of files present /sys/block
+.br
+- list of files present /sys/devices/virtual/block
+.br
+- if enabled with -m, metadata dump will be also included
+.br
+- if enabled with -a, debug output of vgscan, pvscan and list of all available volume groups, physical volumes and logical volumes will be included
+.br
+- if enabled with -c, cluster status info
+.SH OPTIONS
+.TP
+\fB\-h\fR \(em print help message
+.TP
+\fB\-a\fR \(em advanced collection 
+\fBWARNING\fR: if lvm is already hung, then this script may hang as well if \fB\-a\fR is used
+.TP
+\fB\-m\fR \(em gather LVM metadata from the PVs
+This option generates a 1:1 dump of the metadata area from all PVs visible to the system, which can cause the dump to increase in size considerably. However, the metadata dump may represent a valuable diagnostic resource.
+.TP
+\fB\-d\fR directory \(em dump into a directory instead of tarball
+By default, lvmdump will produce a single compressed tarball containing all the information. Using this option, it can be instructed to only produce the raw dump tree, rooted in \fBdirectory\fP.
+.TP
+\fB\-c\fR \(em if clvmd is running, gather cluster data as well
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBLVM_BINARY\fP 
+The LVM2 binary to use.
+Defaults to "lvm".
+Sometimes you might need to set this to "/sbin/lvm.static", for example.
+.TP
+\fBDMSETUP_BINARY\fP 
+The dmsetup binary to use.
+Defaults to "dmsetup".
+.PP
diff --git a/man/lvmsadc.8.in b/man/lvmsadc.8.in
new file mode 100644 (file)
index 0000000..b78ba6e
--- /dev/null
@@ -0,0 +1,14 @@
+.TH "LVMSADC" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH "NAME"
+lvmsadc \- LVM system activity data collector
+
+.SH "SYNOPSIS"
+.B lvmsadc
+
+.SH "DESCRIPTION"
+.B lvmsadc
+is not currently supported under LVM2.
+
+.SH "SEE ALSO"
+.BR lvm (8)
diff --git a/man/lvmsar.8.in b/man/lvmsar.8.in
new file mode 100644 (file)
index 0000000..0919545
--- /dev/null
@@ -0,0 +1,14 @@
+.TH "LVMSAR" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH "NAME"
+lvmsar \- LVM system activity reporter
+
+.SH "SYNOPSIS"
+.B lvmsar
+
+.SH "DESCRIPTION"
+.B lvmsar
+is not currently supported under LVM2.
+
+.SH "SEE ALSO"
+.BR lvm (8)
diff --git a/man/lvreduce.8.in b/man/lvreduce.8.in
new file mode 100644 (file)
index 0000000..fc77223
--- /dev/null
@@ -0,0 +1,89 @@
+.TH LVREDUCE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvreduce \- reduce the size of a logical volume
+.SH SYNOPSIS
+.B lvreduce
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-f|\-\-force]
+[\-h|\-?|\-\-help]
+[\-\-noudevsync]
+{\-l|\-\-extents [\-]LogicalExtentsNumber[%{VG|LV|FREE|ORIGIN}] |
+\-L|\-\-size [\-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-n|\-\-nofsck]
+[\-r|\-\-resizefs]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolume[Path]
+.SH DESCRIPTION
+lvreduce allows you to reduce the size of a logical volume.
+Be careful when reducing a logical volume's size, because data in the
+reduced part is lost!!! 
+.br 
+You should therefore ensure that any filesystem on the volume is 
+resized
+.I before
+running lvreduce so that the extents that are to be removed are not in use.
+.br
+Shrinking snapshot logical volumes (see
+.B lvcreate(8)
+for information to create snapshots) is supported as well.
+But to change the number of copies in a mirrored logical
+volume use 
+.B lvconvert (8).
+.br
+Sizes will be rounded if necessary - for example, the volume size must
+be an exact number of extents and the size of a striped segment must
+be a multiple of the number of stripes.
+.br
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-f, \-\-force
+Force size reduction without prompting even when it may cause data loss.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-l, \-\-extents [\-]LogicalExtentsNumber[%{VG|LV|FREE|ORIGIN}]
+Reduce or set the logical volume size in units of logical extents.
+With the - sign the value will be subtracted from
+the logical volume's actual size and without it the value will be taken
+as an absolute size.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, relative to the existing
+size of the Logical Volume with the suffix %LV, as a percentage of the
+remaining free space in the Volume Group with the suffix %FREE, or (for
+a snapshot) as a percentage of the total space in the Origin Logical
+Volume with the suffix %ORIGIN.
+.TP
+.I \-L, \-\-size [\-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Reduce or set the logical volume size in units of megabytes.
+A size suffix of k for kilobyte, m for megabyte, 
+g for gigabytes, t for terabytes, p for petabytes 
+or e for exabytes is optional.
+With the - sign the value will be subtracted from
+the logical volume's actual size and without it it will be taken as
+an absolute size.
+.TP
+.I \-n, \-\-nofsck
+Do not perform fsck before resizing filesystem when filesystem
+requires it. You may need to use \fB--force\fR to proceed with 
+this option.
+.TP
+.I \-r, \-\-resizefs
+Resize underlying filesystem together with the logical volume using 
+\fBfsadm\fR(8).
+.SH Example
+"lvreduce -l -3 vg00/lvol1" reduces the size of logical volume lvol1
+in volume group vg00 by 3 logical extents.
+.SH SEE ALSO
+.BR fsadm (8),
+.BR lvchange (8),
+.BR lvconvert (8), 
+.BR lvcreate (8), 
+.BR lvextend (8), 
+.BR lvm (8), 
+.BR lvresize (8),
+.BR vgreduce (8)
diff --git a/man/lvremove.8.in b/man/lvremove.8.in
new file mode 100644 (file)
index 0000000..ac39cb4
--- /dev/null
@@ -0,0 +1,49 @@
+.TH LVREMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvremove \- remove a logical volume
+.SH SYNOPSIS
+.B lvremove
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-f|\-\-force]
+[\-h|\-?|\-\-help] 
+[\-\-noudevsync]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [LogicalVolumePath...]
+.SH DESCRIPTION
+\fBlvremove\fP removes one or more logical volumes.
+Confirmation will be requested before deactivating any active logical
+volume prior to removal.  Logical volumes cannot be deactivated
+or removed while they are open (e.g. if they contain a mounted filesystem).
+Removing an origin logical volume will also remove all dependent snapshots.
+.sp
+If the logical volume is clustered then it must be deactivated on all
+nodes in the cluster before it can be removed. A single lvchange command
+issued from one node can do this.
+.SH OPTIONS
+See \fBlvm\fP(8) for common options.
+.TP
+.I \-f, \-\-force
+Remove active logical volumes without confirmation.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.SH EXAMPLES
+Remove the active logical volume lvol1 in volume group vg00 
+without asking for confirmation:
+.sp
+\      \fBlvremove -f vg00/lvol1\fP
+.sp
+Remove all logical volumes in volume group vg00:
+.sp
+\      \fBlvremove vg00\fP
+.SH SEE ALSO
+.BR lvcreate (8), 
+.BR lvdisplay (8),
+.BR lvchange (8),  
+.BR lvm (8), 
+.BR lvs (8),
+.BR lvscan (8),
+.BR vgremove (8)
diff --git a/man/lvrename.8.in b/man/lvrename.8.in
new file mode 100644 (file)
index 0000000..8f90072
--- /dev/null
@@ -0,0 +1,55 @@
+.TH LVRENAME 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvrename \- rename a logical volume
+.SH SYNOPSIS
+.B lvrename
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-d | \-\-debug ]
+.RB [ \-f | \-\-force ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-noudevsync ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-version ]
+.TP
+.IR "OldLogicalVolumePath NewLogicalVolume" { Path | Name }
+.TP
+.I VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName
+.SH DESCRIPTION
+.B lvrename
+renames an existing logical volume from
+.IR OldLogicalVolume { Name | Path }
+to
+.IR NewLogicalVolume { Name | Path }.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.SH EXAMPLE
+To rename
+.B lvold
+in volume group
+.B vg02
+to
+.BR lvnew :
+.nf
+
+\      lvrename /dev/vg02/lvold /dev/vg02/lvnew
+
+.fi
+An alternate syntax to rename this logical volume is
+.nf
+
+\      lvrename vg02 lvold lvnew
+
+.fi
+.SH SEE ALSO
+.BR lvm (8), 
+.BR lvchange (8),
+.BR vgcreate (8), 
+.BR vgrename (8)
diff --git a/man/lvresize.8.in b/man/lvresize.8.in
new file mode 100644 (file)
index 0000000..677b2db
--- /dev/null
@@ -0,0 +1,98 @@
+.TH LVRESIZE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvresize \- resize a logical volume
+.SH SYNOPSIS
+.B lvresize
+[\-\-alloc AllocationPolicy]
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-i|\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+{\-l|\-\-extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |
+\-L|\-\-size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-f|\-\-force]
+[\-n|\-\-nofsck]
+[\-r|\-\-resizefs]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [PhysicalVolumePath[:PE[-PE]]...]
+.SH DESCRIPTION
+lvresize allows you to resize a logical volume.
+Be careful when reducing a logical volume's size, because data in the reduced
+part is lost!!!
+You should therefore ensure that any filesystem on the volume is
+shrunk first so that the extents that are to be removed are not in use.
+Resizing snapshot logical volumes (see
+.B lvcreate(8)
+for information about creating snapshots) is supported as well.
+But to change the number of copies in a mirrored logical
+volume use 
+.BR lvconvert (8).
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-f, \-\-force
+Force resize without prompting even when it may cause data loss.
+.TP
+.I \-l, \-\-extents [+|-]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}]
+Change or set the logical volume size in units of logical extents.
+With the + or - sign the value is added to or subtracted from the actual size
+of the logical volume and without it, the value is taken as an absolute one.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, relative to the existing
+size of the Logical Volume with the suffix %LV, as a percentage of
+the remaining free space of the PhysicalVolumes on the command line with the
+suffix %PVS, as a percentage of the remaining free space in the
+Volume Group with the suffix %FREE, or (for a snapshot) as a percentage
+of the total space in the Origin Logical Volume with the suffix %ORIGIN.
+.TP
+.I \-n, \-\-nofsck
+Do not perform fsck before resizing filesystem when filesystem
+requires it. You may need to use \fB--force\fR to proceed with 
+this option.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-r, \-\-resizefs
+Resize underlying filesystem together with the logical volume using 
+\fBfsadm\fR(8).
+.TP
+.I \-L, \-\-size [+|-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Change or set the logical volume size in units of megabytes.
+A size suffix of M for megabytes,
+G for gigabytes, T for terabytes, P for petabytes 
+or E for exabytes is optional.
+With the + or - sign the value is added to or subtracted from
+the actual size of the logical volume and without it, the value is taken as an
+absolute one.
+.TP
+.I \-i, \-\-stripes Stripes
+Gives the number of stripes to use when extending a Logical Volume.
+Defaults to whatever the last segment of the Logical Volume uses.
+Not applicable to LVs using the original metadata LVM format, which must
+use a single value throughout.
+.TP
+.I \-I, \-\-stripesize StripeSize
+Gives the number of kilobytes for the granularity of the stripes.
+Defaults to whatever the last segment of the Logical Volume uses.
+Not applicable to LVs using the original metadata LVM format, which
+must use a single value throughout.
+.br
+StripeSize must be 2^n (n = 2 to 9)
+.SH Examples
+.br
+"lvresize -L+16M vg1/lv1 /dev/sda:0-1 /dev/sdb:0-1"
+.br
+tries to extend a logical volume "vg1/lv1" by 16MB using physical extents
+/dev/sda:0-1 and /dev/sdb:0-1 for allocation of extents.
+
+.SH SEE ALSO
+.BR fsadm (8),
+.BR lvm (8), 
+.BR lvconvert (8),
+.BR lvcreate (8), 
+.BR lvreduce (8), 
+.BR lvchange (8)
diff --git a/man/lvs.8.in b/man/lvs.8.in
new file mode 100644 (file)
index 0000000..baa14ab
--- /dev/null
@@ -0,0 +1,117 @@
+.TH LVS 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvs \- report information about logical volumes
+.SH SYNOPSIS
+.B lvs
+[\-a|\-\-all]
+[\-\-aligned] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure] [\-\-nameprefixes] [\-\-noheadings] [\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial] [\-\-rows] [\-\-segments]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-\-unquoted]
+[\-v|\-\-verbose] 
+[\-\-version] [VolumeGroupName [VolumeGroupName...]]
+.SH DESCRIPTION
+lvs produces formatted output about logical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-all
+Include information in the output about internal Logical Volumes that
+are components of normally-accessible Logical Volumes, such as mirrors,
+but which are not independently accessible (e.g. not mountable).
+The names of such Logical Volumes are enclosed within square brackets
+in the output.  For example, after creating a mirror using 'lvcreate -m1
+--mirrorlog disk', this option will reveal three internal Logical
+Volumes, with suffixes mimage_0, mimage_1, and mlog.
+.TP
+.I \-\-aligned
+Use with \-\-separator to align the output columns.
+.TP
+.I \-\-nameprefixes
+Add an "LVM2_" prefix plus the field name to the output.  Useful
+with --noheadings to produce a list of field=value pairs that can
+be used to set environment variables (for example, in \fBudev (7)\fP rules).
+.TP
+.I \-\-noheadings
+Suppress the headings line that is normally the first line of output.
+Useful if grepping the output.
+.TP
+.I \-\-nosuffix
+Suppress the suffix on output sizes.  Use with \-\-units (except h and H)
+if processing the output.
+.TP
+.I \-o, \-\-options
+Comma-separated ordered list of columns.  Precede the list with '+' to append
+to the default selection of columns instead of replacing it.
+.IP
+Use \fb-o lv_all\fP to select all logical volume columns, and \fb-o seg_all\fP
+to select all logical volume segment columns.
+.IP
+Use \fb-o help\fP to view the full list of columns available.
+.IP
+Column names include:
+lv_uuid, lv_name, lv_path, lv_attr, lv_major, lv_minor, lv_read_ahead, lv_kernel_major,
+lv_kernel_minor, lv_kernel_read_ahead, lv_size, seg_count, origin, origin_size,
+snap_percent, copy_percent, move_pv, convert_lv, lv_tags, mirror_log, modules,
+segtype, stripes, stripesize, regionsize, chunksize, seg_start, seg_start_pe,
+seg_size, seg_tags, seg_pe_ranges, devices.
+.IP
+With \-\-segments, any "seg_" prefixes are optional; otherwise any "lv_"
+prefixes are optional.  Columns mentioned in \fBvgs (8)\fP 
+can also be chosen.
+.IP
+The lv_attr bits are: 
+.RS
+.IP 1 3
+Volume type: (m)irrored, (M)irrored without initial sync, (o)rigin,
+(O)rigin with merging snapshot, (s)napshot, merging (S)napshot, (p)vmove,
+(v)irtual, mirror (i)mage, mirror (I)mage out-of-sync, under (c)onversion
+.IP 2 3
+Permissions: (w)riteable, (r)ead-only
+.IP 3 3
+Allocation policy: (c)ontiguous, c(l)ing, (n)ormal, (a)nywhere, (i)nherited
+This is capitalised if the volume is currently locked against allocation
+changes, for example during \fBpvmove\fP (8).
+.IP 4 3
+fixed (m)inor
+.IP 5 3
+State: (a)ctive, (s)uspended, (I)nvalid snapshot, invalid (S)uspended snapshot,
+mapped (d)evice present without tables, mapped device present with (i)nactive table
+.IP 6 3
+device (o)pen
+.RE
+.TP
+.I \-\-segments
+Use default columns that emphasize segment information.
+.TP
+.I \-O, \-\-sort
+Comma-separated ordered list of columns to sort by.  Replaces the default
+selection. Precede any column with - for a reverse sort on that column.
+.TP
+.I \-\-rows
+Output columns as rows.
+.TP
+.I \-\-separator Separator
+String to use to separate each column.  Useful if grepping the output.
+.TP
+.I \-\-unbuffered
+Produce output immediately without sorting or aligning the columns properly.
+.TP
+.I \-\-units hHbBsSkKmMgGtTpPeE
+All sizes are output in these units: (h)uman-readable, (b)ytes, (s)ectors,
+(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes.  
+Capitalise to use multiples of 1000 (S.I.) instead of 1024.  Can also specify 
+custom units e.g. \-\-units 3M
+.TP
+.I \-\-unquoted
+When used with --nameprefixes, output values in the field=value pairs are not quoted.
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvdisplay (8),
+.BR pvs (8),
+.BR vgs (8)
diff --git a/man/lvscan.8.in b/man/lvscan.8.in
new file mode 100644 (file)
index 0000000..3dbe9d8
--- /dev/null
@@ -0,0 +1,34 @@
+.TH LVSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvscan \- scan (all disks) for logical volumes
+.SH SYNOPSIS
+.B lvscan
+.RB [ \-a | \-\-all]
+.RB [ \-b | \-\-blockdevice ]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-ignorelockingfailure ]
+.RB [ \-P | \-\-partial ]
+.RB [ \-v | \-\-verbose ]
+.SH DESCRIPTION
+.B lvscan
+scans all known volume groups or all supported LVM block devices
+in the system for defined logical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-\-all
+Include information in the output about internal Logical Volumes that
+are components of normally-accessible Logical Volumes, such as mirrors,
+but which are not independently accessible (e.g. not mountable).
+For example, after creating a mirror using 'lvcreate -m1 --mirrorlog disk',
+this option will reveal three internal Logical Volumes, with suffixes
+mimage_0, mimage_1, and mlog.
+.TP
+.BR \-b ", " \-\-blockdevice
+Adds the device major and minor numbers to the display
+of each logical volume.
+.SH SEE ALSO
+.BR lvm (8), 
+.BR lvcreate (8),
+.BR lvdisplay (8)
diff --git a/man/pvchange.8.in b/man/pvchange.8.in
new file mode 100644 (file)
index 0000000..1bf6a4f
--- /dev/null
@@ -0,0 +1,42 @@
+.TH PVCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvchange \- change attributes of a physical volume
+.SH SYNOPSIS
+.B pvchange
+[\-\-addtag Tag]
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] 
+[\-f|\-\-force]
+[\-\-deltag Tag]
+[\-\-metadataignore y|n]
+[\-h|\-?|\-\-help]
+[\-t|\-\-test]
+[\-v|\-\-verbose] [\-a|\-\-all] [\-x|\-\-allocatable y|n]
+[\-u|\-\-uuid] [PhysicalVolumePath...]
+.SH DESCRIPTION
+pvchange allows you to change the allocation permissions of one or
+more physical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+If PhysicalVolumePath is not specified on the command line all
+physical volumes are searched for and used.
+.TP
+.I \-\-metadataignore " y|n"
+Ignore or un-ignore metadata areas on this physical volume.
+If metadata areas on a physical volume are ignored, LVM will
+not not store metadata in the metadata areas present on this Physical
+Volume.
+.TP
+.I \-u, \-\-uuid
+Generate new random UUID for specified physical volumes.
+.TP
+.I \-x, \-\-allocatable y|n
+Enable or disable allocation of physical extents on this physical volume.
+.SH Example
+"pvchange -x n /dev/sdk1" disallows the allocation of physical extents
+on this physical volume (possibly because of disk errors, or because it will
+be removed after freeing it.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8)
diff --git a/man/pvck.8.in b/man/pvck.8.in
new file mode 100644 (file)
index 0000000..de3dfac
--- /dev/null
@@ -0,0 +1,33 @@
+.TH PVCK 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvck \- check physical volume metadata
+.SH SYNOPSIS
+.B pvck
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-labelsector ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+pvck checks physical volume LVM metadata for consistency.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-\-labelsector " sector"
+By default, 4 sectors of \fBPhysicalVolume\fP are scanned for an LVM label,
+starting at sector 0.  This parameter allows you to specify a different
+starting sector for the scan and is useful for recovery situations.  For
+example, suppose the partition table is corrupted or lost on /dev/sda,
+but you suspect there was an LVM partition at approximately 100 MB.  This
+area of the disk may be scanned by using the \fB--labelsector\fP parameter
+with a value of 204800 (100 * 1024 * 1024 / 512 = 204800):
+.sp
+.BI "pvck --labelsector 204800 /dev/sda"
+.sp
+Note that a script can be used with \fB--labelsector\fP to automate the
+process of finding LVM labels.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR pvscan (8)
+.BR vgck (8)
diff --git a/man/pvcreate.8.in b/man/pvcreate.8.in
new file mode 100644 (file)
index 0000000..cb10049
--- /dev/null
@@ -0,0 +1,180 @@
+.TH PVCREATE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvcreate \- initialize a disk or partition for use by LVM
+.SH SYNOPSIS
+.B pvcreate
+.RB [ \-d | \-\-debug ]
+.RB [ \-f [ f ]| \-\-force " [" \-\-force ]]
+.RB [ \-y | \-\-yes ]
+.RB [ \-h | \-\-help ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-labelsector ]
+.RB [ \-M | \-\-metadatatype type ]
+.RB [ \-\-[pv]metadatacopies #copies ]
+.RB [ \-\-metadatasize size ]
+.RB [ \-\-metadataignore y|n ]
+.RB [ \-\-dataalignment alignment ]
+.RB [ \-\-dataalignmentoffset alignment_offset ]
+.RB [ \-\-restorefile file ]
+.RB [ \-\-norestorefile ]
+.RB [ \-\-setphysicalvolumesize size ]
+.RB [ \-u | \-\-uuid uuid ]
+.RB [ \-\-version ]
+.RB [ \-Z | \-\-zero y|n ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+.B pvcreate
+initializes
+.I PhysicalVolume
+for later use by the Logical Volume Manager (LVM).  Each
+.I PhysicalVolume
+can be a disk partition, whole disk, meta device, or loopback file.
+For DOS disk partitions, the partition id should be set to 0x8e using
+.BR fdisk "(8), " cfdisk "(8), "
+or a equivalent.  For
+.B whole disk devices only
+the partition table must be erased, which will effectively destroy all
+data on that disk.  This can be done by zeroing the first sector with:
+.sp
+.BI "dd if=/dev/zero of=" PhysicalVolume " bs=512 count=1"
+.sp
+Continue with
+.BR vgcreate (8)
+to create a new volume group on
+.IR PhysicalVolume ,
+or
+.BR vgextend (8)
+to add
+.I PhysicalVolume
+to an existing volume group.
+.SH OPTIONS
+See \fBlvm\fP(8) for common options.
+.TP
+.BR \-f ", " \-\-force
+Force the creation without any confirmation.  You can not recreate
+(reinitialize) a physical volume belonging to an existing volume group.
+In an emergency you can override this behaviour with -ff.
+.TP
+.BR \-u ", " \-\-uuid " uuid"
+Specify the uuid for the device.  
+Without this option, \fBpvcreate\fP generates a random uuid.
+All of your physical volumes must have unique uuids.
+You need to use this option before restoring a backup of LVM metadata 
+onto a replacement device - see \fBvgcfgrestore\fP(8).  As such, use of
+\fB--restorefile\fP is compulsory unless the \fB--norestorefile\fP is
+used.
+.TP
+.BR \-y ", " \-\-yes
+Answer yes to all questions.
+.TP
+.BR \-Z ", " \-\-zero " y|n"
+Whether or not the first 4 sectors (2048 bytes) of the device should be 
+wiped.
+If this option is not given, the 
+default is to wipe these sectors unless either or both of the --restorefile 
+or --uuid options were specified.
+.SH NEW METADATA OPTIONS
+LVM2 introduces a new format for storing metadata on disk.
+This new format is more efficient and resilient than the format the 
+original version of LVM used and offers the advanced user greater 
+flexibility and control.
+.sp
+The new format may be selected on the command line with \fB-M2\fP or by 
+setting \fBformat = "lvm2"\fP in the \fBglobal\fP section of \fBlvm.conf\fP.
+Each physical volume in the same volume group must use the same format, but
+different volume groups on a machine may use different formats 
+simultaneously: the tools can handle both formats.
+Additional formats can be added as shared libraries.
+.sp
+Additional tools for manipulating the locations and sizes of metadata areas 
+will be written in due course.  Use the verbose/debug options on the tools
+to see where the metadata areas are placed.
+.TP
+.BR \-\-metadatasize " size"
+The approximate amount of space to be set aside for each metadata area.
+(The size you specify may get rounded.)
+.TP
+.BR \-\-dataalignment " alignment"
+Align the start of the data to a multiple of this number.
+You should also specify an appropriate \fBPhysicalExtentSize\fP when creating
+the Volume Group with \fBvgcreate\fP.
+.sp
+To see the location of the first Physical Extent of an existing Physical Volume
+use \fBpvs -o +pe_start\fP .  It will be a multiple of the requested
+\fBalignment\fP.  In addition it may be shifted by \fBalignment_offset\fP from
+\fBdata_alignment_offset_detection\fP (if enabled in \fBlvm.conf\fP) or
+\fB--dataalignmentoffset\fP.
+.TP
+.BR \-\-dataalignmentoffset " alignment_offset"
+Shift the start of the data area by this additional \fBalignment_offset\fP.
+.TP
+.BR \-\-[pv]metadatacopies " copies"
+The number of metadata areas to set aside on each PV.  Currently
+this can be 0, 1 or 2.  
+If set to 2, two copies of the volume group metadata 
+are held on the PV, one at the front of the PV and one at the end.  
+If set to 1 (the default), one copy is kept at the front of the PV 
+(starting in the 5th sector).
+If set to 0, no copies are kept on this PV - you might wish to use this
+with VGs containing large numbers of PVs.  But if you do this and
+then later use \fBvgsplit\fP you must ensure that each VG is still going 
+to have a suitable number of copies of the metadata after the split!
+.TP
+.BR \-\-metadataignore " y|n"
+Ignore or un-ignore metadata areas on this physical volume.
+The default is "n".  This setting can be changed with \fBpvchange\fP.
+If metadata areas on a physical volume are ignored, LVM will
+not not store metadata in the metadata areas present on this Physical
+Volume.  Metadata areas cannot be created or extended after Logical
+Volumes have been allocated on the device. If you do not want to store
+metadata on this device, it is still wise always to allocate a metadata
+area in case you need it in the future and to use this option to instruct
+LVM2 to ignore it.
+.TP
+.BR \-\-restorefile " file"
+In conjunction with \fB--uuid\fP, this extracts the location and size
+of the data on the PV from the file (produced by \fBvgcfgbackup\fP)
+and ensures that the metadata that the program produces is consistent 
+with the contents of the file i.e. the physical extents will be in 
+the same place and not get overwritten by new metadata.  This provides
+a mechanism to upgrade the metadata format or to add/remove metadata
+areas. Use with care. See also \fBvgconvert\fP(8).
+.TP
+.BR \-\-norestorefile
+In conjunction with \fB--uuid\fP, this allows a uuid to be specified
+without also requiring that a backup of the metadata be provided.
+.TP
+.BR \-\-labelsector " sector"
+By default the PV is labelled with an LVM2 identifier in its second 
+sector (sector 1).  This lets you use a different sector near the
+start of the disk (between 0 and 3 inclusive - see LABEL_SCAN_SECTORS
+in the source).  Use with care.
+.TP
+.BR \-\-setphysicalvolumesize " size"
+Overrides the automatically-detected size of the PV.  Use with care.
+.SH EXAMPLES
+Initialize partition #4 on the third SCSI disk and the entire fifth
+SCSI disk for later use by LVM:
+.sp
+.B pvcreate /dev/sdc4 /dev/sde
+.sp
+If the 2nd SCSI disk is a 4KB sector drive that compensates for windows
+partitioning (sector 7 is the lowest aligned logical block, the 4KB
+sectors start at LBA -1, and consequently sector 63 is aligned on a 4KB
+boundary) manually account for this when initializing for use by LVM:
+.sp
+.B pvcreate --dataalignmentoffset 7s /dev/sdb
+.sp
+.SH SEE ALSO
+.BR lvm.conf (5),
+.BR lvm (8),
+.BR vgcreate (8), 
+.BR vgextend (8), 
+.BR lvcreate (8), 
+.BR cfdisk (8), 
+.BR fdisk (8), 
+.BR losetup (8), 
+.BR mdadm (8), 
+.BR vgcfgrestore (8), 
+.BR vgconvert (8)
diff --git a/man/pvdisplay.8.in b/man/pvdisplay.8.in
new file mode 100644 (file)
index 0000000..efa3894
--- /dev/null
@@ -0,0 +1,81 @@
+.TH PVDISPLAY 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvdisplay \- display attributes of a physical volume
+.SH SYNOPSIS
+.B pvdisplay
+[\-c|\-\-colon]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-maps]
+[\-\-nosuffix]
+[\-s|\-\-short]
+[\-\-units hsbkmgtHKMGT]
+[\-v[v]|\-\-verbose [\-\-verbose]]
+[\-\-version]
+[PhysicalVolumePath [PhysicalVolumePath...]]
+.br
+
+.br
+.B pvdisplay \-\-columns | \-C
+[\-\-aligned]
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-noheadings]
+[\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-v[v]|\-\-verbose [\-\-verbose]]
+[\-\-version]
+[PhysicalVolumePath [PhysicalVolumePath...]]
+.SH DESCRIPTION
+pvdisplay allows you to see the attributes of one or more physical volumes
+like size, physical extent size, space used for the volume group descriptor
+area and so on.
+.P
+\fBpvs\fP (8) is an alternative that provides the same information 
+in the style of \fBps\fP (1).
+.SH OPTIONS
+See \fBlvm\fP for common options and \fBpvs\fP for options given with
+\fB\-\-columns\fP.
+.TP
+.I \-c, \-\-colon
+Generate colon separated output for easier parsing in scripts or programs.
+N.B. \fBpvs\fP (8) provides considerably more control over the output.
+.nf
+
+The values are:
+
+* physical volume device name
+* volume group name
+* physical volume size in kilobytes
+* internal physical volume number (obsolete)
+* physical volume status
+* physical volume (not) allocatable
+* current number of logical volumes on this physical volume
+* physical extent size in kilobytes
+* total number of physical extents
+* free number of physical extents
+* allocated number of physical extents
+
+.fi
+.TP
+.I \-s, \-\-short
+Only display the size of the given physical volumes.
+.TP
+.I \-m, \-\-maps
+Display the mapping of physical extents to logical volumes and
+logical extents.
+.TP
+.I \-\-columns | \-C
+Display output in columns, the equivalent of \fBpvs\fP (8).  See
+\fBpvs (8)\fP for a description of other options with this form of
+\fBpvdisplay\fP.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR lvcreate (8),
+.BR vgcreate (8)
diff --git a/man/pvmove.8.in b/man/pvmove.8.in
new file mode 100644 (file)
index 0000000..83db231
--- /dev/null
@@ -0,0 +1,106 @@
+.TH PVMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvmove \- move physical extents
+.SH SYNOPSIS
+.B pvmove
+[\-\-abort]
+[\-\-alloc AllocationPolicy]
+[\-b|\-\-background]
+[\-d|\-\-debug] [\-h|\-\-help] [\-i|\-\-interval Seconds]
+[\-\-noudevsync] [\-v|\-\-verbose] [\-n|\-\-name LogicalVolume]
+[SourcePhysicalVolume[:PE[-PE]...] [DestinationPhysicalVolume[:PE[-PE]...]...]]
+.SH DESCRIPTION
+.B pvmove
+allows you to move the allocated physical extents (PEs) on
+.I SourcePhysicalVolume
+to one or more other physical volumes (PVs).
+You can optionally specify a source
+.I LogicalVolume
+in which case only extents used by that LV will be moved to 
+free (or specified) extents on
+.IR DestinationPhysicalVolume (s).
+If no
+.I DestinationPhysicalVolume
+is specifed, the normal allocation rules for the volume group are used.
+
+If \fBpvmove\fP gets interrupted for any reason (e.g. the machine crashes)
+then run \fBpvmove\fP again without any PhysicalVolume arguments to
+restart any moves that were in progress from the last checkpoint.
+Alternatively use \fBpvmove --abort\fP at any time to abort them
+at the last checkpoint.
+
+You can run more than one pvmove at once provided they are moving data
+off different SourcePhysicalVolumes, but additional pvmoves will ignore
+any logical volumes already in the process of being changed, so some
+data might not get moved.
+
+\fBpvmove\fP works as follows:
+
+1. A temporary 'pvmove' logical volume is created to store
+details of all the data movements required.
+
+2. Every logical volume in the volume group is searched
+for contiguous data that need moving
+according to the command line arguments.
+For each piece of data found, a new segment is added to the end of the
+pvmove LV.
+This segment takes the form of a temporary mirror to copy the data 
+from the original location to a newly-allocated location. 
+The original LV is updated to use the new temporary mirror segment
+in the pvmove LV instead of accessing the data directly.
+
+3. The volume group metadata is updated on disk.
+
+4. The first segment of the pvmove logical volume is activated and starts
+to mirror the first part of the data.  Only one segment is mirrored at once
+as this is usually more efficient.
+
+5. A daemon repeatedly checks progress at the specified time interval.
+When it detects that the first temporary mirror is in-sync,
+it breaks that mirror so that only the new location for that data gets used 
+and writes a checkpoint into the volume group metadata on disk.
+Then it activates the mirror for the next segment of the pvmove LV.
+
+6. When there are no more segments left to be mirrored, 
+the temporary logical volume is removed and the volume group metadata 
+is updated so that the logical volumes reflect the new data locations.
+
+Note that this new process cannot support the original LVM1
+type of on-disk metadata.  Metadata can be converted using \fBvgconvert\fP(8).
+
+.SH OPTIONS
+.TP
+.I \-\-abort
+Abort any moves in progress.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-b, \-\-background
+Run the daemon in the background.
+.TP
+.I \-i, \-\-interval Seconds
+Report progress as a percentage at regular intervals.
+.TP
+.I \-n, \-\-name " \fILogicalVolume\fR"
+Move only the extents belonging to
+.I LogicalVolume
+from
+.I SourcePhysicalVolume
+instead of all allocated extents to the destination physical volume(s).
+
+.SH EXAMPLES
+To move all logical extents of any logical volumes on
+.B /dev/hda4
+to free physical extents elsewhere in the volume group, giving verbose
+runtime information, use:
+.sp
+\      pvmove -v /dev/hda4
+.sp
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgconvert (8)
diff --git a/man/pvremove.8.in b/man/pvremove.8.in
new file mode 100644 (file)
index 0000000..b435fc7
--- /dev/null
@@ -0,0 +1,22 @@
+.TH PVREMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvremove \- remove a physical volume
+.SH SYNOPSIS
+.B pvremove
+.RB [ \-d | \-\-debug]
+.RB [ \-f [ f ]| \-\-force " [" \-\-force ]]
+.RB [\-h | \-\-help]
+.RB [ \-t | \-\-test ]
+.RB [ \-v [ v ]| \-\-verbose " [" \-\-verbose ]]
+.RB [ \-y | \-\-yes ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+.B pvremove
+wipes the label on a device so that LVM will no longer recognise it
+as a physical volume.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR pvdisplay (8)
diff --git a/man/pvresize.8.in b/man/pvresize.8.in
new file mode 100644 (file)
index 0000000..845f6d3
--- /dev/null
@@ -0,0 +1,49 @@
+.TH PVRESIZE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvresize \- resize a disk or partition in use by LVM2
+.SH SYNOPSIS
+.B pvresize
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-setphysicalvolumesize size ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+.B pvresize
+resizes
+.I PhysicalVolume
+which may already be in a volume group and have active logical volumes
+allocated on it.
+.SH OPTIONS
+See \fBlvm\fP(8) for common options.
+.TP
+.BR \-\-setphysicalvolumesize " size"
+Overrides the automatically-detected size of the PV.  Use with care, or
+prior to reducing the physical size of the device.
+.SH EXAMPLES
+Expand the PV on /dev/sda1 after enlarging the partition with fdisk:
+.sp
+.B pvresize /dev/sda1
+.sp
+Shrink the PV on /dev/sda1 prior to shrinking the partition with fdisk
+(ensure that the PV size is appropriate for your intended new partition
+size):
+.sp
+.B pvresize --setphysicalvolumesize 40G /dev/sda1
+.sp
+.SH RESTRICTIONS
+.B pvresize
+will refuse to shrink
+.I PhysicalVolume
+if it has allocated extents after where its new end would be. In the future,
+it should relocate these elsewhere in the volume group if there is sufficient
+free space, like
+.B pvmove
+does.
+.sp
+.B pvresize
+won't currently work correctly on LVM1 volumes or PVs with extra
+metadata areas.
+.SH SEE ALSO
+.BR lvm "(8), " pvmove "(8), " lvresize "(8), " fdisk "(8)"
diff --git a/man/pvs.8.in b/man/pvs.8.in
new file mode 100644 (file)
index 0000000..a5e1382
--- /dev/null
@@ -0,0 +1,94 @@
+.TH PVS 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvs \- report information about physical volumes
+.SH SYNOPSIS
+.B pvs
+[\-a|\-\-all]
+[\-\-aligned] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure] [\-\-nameprefixes] [\-\-noheadings] [\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial]
+[\-\-rows]
+[\-\-segments]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-\-unquoted]
+[\-v|\-\-verbose] 
+[\-\-version] [PhysicalVolume [PhysicalVolume...]]
+.SH DESCRIPTION
+pvs produces formatted output about physical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+\fB\-\-columns\fP.
+.TP
+.I \-\-all
+Include information in the output about devices that have not been
+initialized with \fBpvcreate\fP.
+.TP
+.I \-\-aligned
+Use with \-\-separator to align the output columns.
+.TP
+.I \-\-nameprefixes
+Add an "LVM2_" prefix plus the field name to the output.  Useful
+with --noheadings to produce a list of field=value pairs that can
+be used to set environment variables (for example, in \fBudev (7)\fP rules).
+.TP
+.I \-\-noheadings
+Suppress the headings line that is normally the first line of output.
+Useful if grepping the output.
+.TP
+.I \-\-nosuffix
+Suppress the suffix on output sizes.  Use with \-\-units (except h and H)
+if processing the output.
+.TP
+.I \-o, \-\-options
+Comma-separated ordered list of columns.  Precede the list with '+' to append
+to the default selection of columns.
+.IP
+Use \fb-o pv_all\fP to select all physical volume columns, and \fb-o pvseg_all\fP
+to select all Physical Volume segment columns.
+.IP
+Use \fb-o help\fP to view the full list of columns available.
+.IP
+Column names include: pv_fmt, pv_uuid, dev_size, pv_name, pv_mda_free,
+pv_mda_size, pe_start, pv_size, pv_free, pv_used, pv_attr, pv_pe_count,
+pv_pe_alloc_count, pv_tags, pv_mda_count, pv_mda_used_count,
+pvseg_start, and pvseg_size.
+.IP
+With --segments, any "pvseg_" prefixes are optional; otherwise any
+"pv_" prefixes are optional.  Columns mentioned in \fBvgs (8)\fP can also
+be chosen. The pv_attr bits are: (a)llocatable and e(x)ported.
+.TP
+.I \-\-segments
+Produces one line of output for each contiguous allocation of space on each
+Physical Volume, showing the start (pvseg_start) and length (pvseg_size) in
+units of physical extents.
+.TP
+.I \-O, \-\-sort
+Comma-separated ordered list of columns to sort by.  Replaces the default
+selection. Precede any column with - for a reverse sort on that column.
+.TP
+.I \-\-rows
+Output columns as rows.
+.TP
+.I \-\-separator Separator
+String to use to separate each column.  Useful if grepping the output.
+.TP
+.I \-\-unbuffered
+Produce output immediately without sorting or aligning the columns properly.
+.TP
+.I \-\-units hHbBsSkKmMgGtTpPeE
+All sizes are output in these units: (h)uman-readable, (b)ytes, (s)ectors,
+(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes.  
+Capitalise to use multiples of 1000 (S.I.) instead of 1024.  Can also specify 
+custom units e.g. \-\-units 3M
+.TP
+.I \-\-unquoted
+When used with --nameprefixes, output values in the field=value pairs are not quoted.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvdisplay (8),
+.BR lvs (8),
+.BR vgs (8)
diff --git a/man/pvscan.8.in b/man/pvscan.8.in
new file mode 100644 (file)
index 0000000..4a6bcd7
--- /dev/null
@@ -0,0 +1,34 @@
+.TH PVSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvscan \- scan all disks for physical volumes
+.SH SYNOPSIS
+.B pvscan
+.RB [ \-d | \-\-debug]
+.RB [\-e | \-\-exported]
+.RB [\-h | \-\-help]
+.RB [\-\-ignorelockingfailure]
+.RB [ \-n | \-\-novolumegroup]
+.RB [\-s | \-\-short]
+.RB [\-u | \-\-uuid]
+.RB [ \-v [ v ]| \-\-verbose " [" \-\-verbose ]]
+.SH DESCRIPTION
+.B pvscan
+scans all supported LVM block devices in the system for physical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-e ", " \-\-exported
+Only show physical volumes belonging to exported volume groups.
+.TP
+.BR \-n ", " \-\-novolumegroup
+Only show physical volumes not belonging to any volume group.
+.TP
+.BR \-s ", " \-\-short
+Short listing format.
+.TP
+.BR \-u ", " \-\-uuid
+Show UUIDs (Uniform Unique Identifiers) in addition to device special names.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR pvdisplay (8)
diff --git a/man/vgcfgbackup.8.in b/man/vgcfgbackup.8.in
new file mode 100644 (file)
index 0000000..1a72a01
--- /dev/null
@@ -0,0 +1,32 @@
+.TH VGCFGBACKUP 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgcfgbackup \- backup volume group descriptor area
+.SH SYNOPSIS
+.B vgcfgbackup
+.RB [ \-d | \-\-debug ]
+.RB [ \-f | \-\-file " filename" ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-ignorelockingfailure ]
+.RB [ \-P | \-\-partial ]
+.RB [ \-v | \-\-verbose ]
+.RI [ VolumeGroupName ...]
+.SH DESCRIPTION
+.B vgcfgbackup
+allows you to backup the metadata 
+of your volume groups.
+If you don't name any volume groups on the command line, all of them 
+will be backed up.
+.sp
+In a default installation, each volume group gets backed up into a separate
+file bearing the name of the volume group in the directory #DEFAULT_BACKUP_DIR#.
+You can write the backup to an alternative file using -f.  In this case
+if you are backing up more than one volume group the filename is
+treated as a template, and %s gets replaced by the volume group name.
+.sp
+NB. This DOESN'T backup user/system data in logical
+volume(s)!  Backup #DEFAULT_SYS_DIR# regularly too.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcfgrestore (8)
diff --git a/man/vgcfgrestore.8.in b/man/vgcfgrestore.8.in
new file mode 100644 (file)
index 0000000..c62c813
--- /dev/null
@@ -0,0 +1,44 @@
+.TH VGCFGRESTORE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgcfgrestore \- restore volume group descriptor area
+.SH SYNOPSIS
+.B vgcfgrestore
+.RB [ \-d | \-\-debug ]
+.RB [ \-f | \-\-file " filename" ]
+.RB [ \-l[l] | \-\-list ]
+.RB [ \-h | \-\-help ]
+.RB [ \-M | \-\-Metadatatype 1|2]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RI \fIVolumeGroupName\fP
+.SH DESCRIPTION
+.B vgcfgrestore
+allows you to restore the metadata of \fIVolumeGroupName\fP from a text 
+backup file produced by \fBvgcfgbackup\fP.  You can specify a backup file 
+with \fP--file\fP.  If no backup file is specified, the most recent
+one is used.  Use \fB--list\fP for a list of the available
+backup and archive files of \fIVolumeGroupName\fP.
+.SH OPTIONS
+.TP
+\fB-l | --list\fP \(em List files pertaining to \fIVolumeGroupName\fP
+List metadata backup and archive files pertaining to \fIVolumeGroupName\fP.
+May be used with the \fB-f\fP option.  Does not restore \fIVolumeGroupName\fP.
+.TP
+\fB-f | --file\fP filename \(em Name of LVM metadata backup file
+Specifies a metadata backup or archive file to be used for restoring 
+VolumeGroupName.  Often this file has been created with \fBvgcfgbackup\fP.
+.TP
+See \fBlvm\fP for common options.
+.SH REPLACING PHYSICAL VOLUMES
+\fBvgdisplay --partial --verbose\fP will show you the UUIDs and sizes of
+any PVs that are no longer present.
+If a PV in the VG is lost and you wish to substitute 
+another of the same size, use 
+\fBpvcreate --restorefile filename --uuid uuid\fP (plus additional 
+arguments as appropriate) to initialise it with the same UUID as 
+the missing PV.  Repeat for all other missing PVs in the VG. 
+Then use \fBvgcfgrestore --file filename\fP to restore the volume
+group's metadata.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8)
diff --git a/man/vgchange.8.in b/man/vgchange.8.in
new file mode 100644 (file)
index 0000000..b4313d4
--- /dev/null
@@ -0,0 +1,198 @@
+.TH VGCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgchange \- change attributes of a volume group
+.SH SYNOPSIS
+.B vgchange
+.RB [ \-\-addtag
+.IR Tag ]
+.RB [ \-\-alloc
+.IR AllocationPolicy ]
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-a | \-\-available " [e|l] {" y | n }]
+.RB [ \-\-monitor " {" y | n }]
+.RB [ \-\-poll " {" y | n }]
+.RB [ \-c | \-\-clustered " {" y | n }]
+.RB [ \-u | \-\-uuid ]
+.RB [ \-d | \-\-debug]
+.RB [ \-\-deltag
+.IR Tag ]
+.RB [ \-h | \-\-help]
+.RB [ \-\-ignorelockingfailure]
+.RB [ \-\-ignoremonitoring]
+.RB [ \-\-sysinit]
+.RB [ \-\-noudevsync ]
+.RB [ \-l | \-\-logicalvolume
+.IR MaxLogicalVolumes ]
+.RB [ -p | \-\-maxphysicalvolumes
+.IR MaxPhysicalVolumes ]
+.RB [ \-\-[vg]metadatacopies ]
+.IR NumberOfCopies|unmanaged|all ]
+.RB [ \-P | \-\-partial]
+.RB [ \-s | \-\-physicalextentsize
+.IR PhysicalExtentSize [ \fBbBsSkKmMgGtTpPeE\fR ]]
+.RB [ \-\-refresh]
+.RB [ -t | \-\-test]
+.RB [ \-v | \-\-verbose]
+.RB [ \-\-version ]
+.RB [ \-x | \-\-resizeable " {" y | n }]
+.RI [ VolumeGroupName ...]
+.SH DESCRIPTION
+.B vgchange
+allows you to change the attributes of one or more volume groups.
+Its main purpose is to activate and deactivate
+.IR VolumeGroupName ,
+or all volume groups if none is specified.  Only active volume groups
+are subject to changes and allow access to their logical volumes.
+[Not yet implemented: During volume group activation, if
+.B vgchange
+recognizes snapshot logical volumes which were dropped because they ran
+out of space, it displays a message informing the administrator that such
+snapshots should be removed (see
+.BR lvremove (8)).
+]
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-A ", " \-\-autobackup " " { y | n }
+Controls automatic backup of metadata after the change.  See
+.B vgcfgbackup (8).
+Default is yes.
+.TP
+.BR \-a ", " \-\-available " " [e|l] { y | n }
+Controls the availability of the logical volumes in the volume 
+group for input/output.
+In other words, makes the logical volumes known/unknown to the kernel.
+.IP
+If clustered locking is enabled, add 'e' to activate/deactivate
+exclusively on one node or 'l' to activate/deactivate only
+on the local node.  
+Logical volumes with single-host snapshots are always activated
+exclusively because they can only be used on one node at once.
+.TP
+.BR \-c ", " \-\-clustered " " { y | n }
+If clustered locking is enabled, this indicates whether this
+Volume Group is shared with other nodes in the cluster or whether
+it contains only local disks that are not visible on the other nodes.
+If the cluster infrastructure is unavailable on a particular node at a
+particular time, you may still be able to use Volume Groups that
+are not marked as clustered.
+.TP
+.BR \-u ", " \-\-uuid
+Generate new random UUID for specified Volume Groups.
+.TP
+.BR \-\-monitor " " { y | n }
+Start or stop monitoring a mirrored or snapshot logical volume with
+dmeventd, if it is installed.
+If a device used by a monitored mirror reports an I/O error,
+the failure is handled according to 
+.BR mirror_image_fault_policy
+and 
+.BR mirror_log_fault_policy
+set in 
+.BR lvm.conf (5).
+.TP
+.BR \-\-poll " " { y | n }
+Without polling a logical volume's backgrounded transformation process
+will never complete.  If there is an incomplete pvmove or lvconvert (for
+example, on rebooting after a crash), use \fB--poll y\fP to restart the
+process from its last checkpoint.  However, it may not be appropriate to
+immediately poll a logical volume when it is activated, use \fB--poll
+n\fP to defer and then \fB--poll y\fP to restart the process.
+.TP
+.BR \-\-sysinit
+Indicates that vgchange(8) is being invoked from early system initialisation
+scripts (e.g. rc.sysinit or an initrd), before writeable filesystems are
+available. As such, some functionality needs to be disabled and this option
+acts as a shortcut which selects an appropriate set of options. Currently
+this is equivalent to using  \fB--ignorelockingfailure\fP, \fB--ignoremonitoring\fP,
+\fB--poll n\fP and setting \fBLVM_SUPPRESS_LOCKING_FAILURE_MESSAGES\fP
+environment variable.
+.TP
+.BR \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.BR \-\-ignoremonitoring
+Make no attempt to interact with dmeventd unless 
+.BR \-\-monitor
+is specified.
+Do not use this if dmeventd is already monitoring a device.
+.TP
+.BR \-l ", " \-\-logicalvolume " " \fIMaxLogicalVolumes\fR
+Changes the maximum logical volume number of an existing inactive
+volume group.
+.TP
+.BR \-p ", " \-\-maxphysicalvolumes " " \fIMaxPhysicalVolumes\fR
+Changes the maximum number of physical volumes that can belong
+to this volume group.
+For volume groups with metadata in lvm1 format, the limit is 255.
+If the metadata uses lvm2 format, the value 0 removes this restriction:
+there is then no limit.  If you have a large number of physical volumes in
+a volume group with metadata in lvm2 format, for tool performance reasons,
+you should consider some use of \fB--pvmetadatacopies 0\fP as described in
+\fBpvcreate(8)\fP, and/or use \fB--vgmetadatacopies\fP.
+.TP
+.BR \-\-[vg]metadatacopies " " \fINumberOfCopies|unmanaged|all\fP
+Sets the desired number of metadata copies in the volume group.  If set to
+a non-zero value, LVM will automatically manage the 'metadataignore'
+flags on the physical volumes (see \fBpvchange\fP or \fBpvcreate --metadataignore\fP) in order
+to achieve \fINumberOfCopies\fP copies of metadata.  If set to \fIunmanaged\fP,
+LVM will not automatically manage the 'metadataignore' flags.  If set to
+\fIall\fP, LVM will first clear all of the 'metadataignore' flags on all
+metadata areas in the volume group, then set the value to \fIunmanaged\fP.
+The \fBvgmetadatacopies\fP option is useful for volume groups containing
+large numbers of physical volumes with metadata as it may be used to
+minimize metadata read and write overhead.
+.TP
+.BR \-s ", " \-\-physicalextentsize " " \fIPhysicalExtentSize\fR[\fBbBsSkKmMgGtTpPeE\fR]
+Changes the physical extent size on physical volumes of this volume group.
+A size suffix (k for kilobytes up to t for terabytes) is optional, megabytes
+is the default if no suffix is present.
+The default is 4 MB and it must be at least 1 KB and a power of 2.
+Before increasing the physical extent size, you might need to use lvresize,
+pvresize and/or pvmove so that everything fits.  For example, every
+contiguous range of extents used in a logical volume must start and 
+end on an extent boundary.
+If the volume group metadata uses lvm1 format, extents can vary in size from
+8KB to 16GB and there is a limit of 65534 extents in each logical volume.  The
+default of 4 MB leads to a maximum logical volume size of around 256GB.
+If the volume group metadata uses lvm2 format those restrictions do not apply,
+but having a large number of extents will slow down the tools but have no
+impact on I/O performance to the logical volume.  The smallest PE is 1KB.
+The 2.4 kernel has a limitation of 2TB per block device.
+.TP
+.BR \-\-refresh
+If any logical volume in the volume group is active, reload its metadata.
+This is not necessary in normal operation, but may be useful
+if something has gone wrong or if you're doing clustering
+manually without a clustered lock manager.
+.TP
+.BR \-x ", " \-\-resizeable " " { y | n }
+Enables or disables the extension/reduction of this volume group
+with/by physical volumes.
+.SH EXAMPLES
+To activate all known volume groups in the system:
+.nf
+
+\      vgchange -a y
+
+.fi
+To change the maximum number of logical volumes of inactive volume group
+.B vg00
+to 128.
+.nf
+
+\      vgchange -l 128 /dev/vg00
+
+.fi
+.SH SEE ALSO
+.BR lvchange (8),
+.BR lvm (8),
+.BR vgcreate (8)
diff --git a/man/vgck.8.in b/man/vgck.8.in
new file mode 100644 (file)
index 0000000..2e5d926
--- /dev/null
@@ -0,0 +1,15 @@
+.TH VGCK 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgck \- check volume group metadata
+.SH SYNOPSIS
+.B vgck
+[\-d|\-\-debug] [\-h|\-?|\-\-help] [\-v|\-\-verbose] [VolumeGroupName...]
+.SH DESCRIPTION
+vgck checks LVM metadata for each named volume group for consistency.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgchange (8),
+.BR vgscan (8)
diff --git a/man/vgconvert.8.in b/man/vgconvert.8.in
new file mode 100644 (file)
index 0000000..14e95f4
--- /dev/null
@@ -0,0 +1,39 @@
+.TH VGCONVERT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgconvert \- convert volume group metadata format
+.SH SYNOPSIS
+.B vgconvert
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-labelsector ]
+.RB [ \-M | \-\-metadatatype type ]
+.RB [ \-\-pvmetadatacopies #copies ]
+.RB [ \-\-metadatasize size ]
+.RB [ \-\-version ]
+.IR VolumeGroupName " [" VolumeGroupName ...]
+.SH DESCRIPTION
+.B vgconvert
+converts 
+.I VolumeGroupName
+metadata from one format to another provided that the metadata
+fits into the same space.
+.SH OPTIONS
+See \fBlvm\fP(8) and \fBpvcreate\fP(8) for options.
+.SH EXAMPLE
+Convert volume group vg1 from LVM1 metadata format to the new LVM2 
+metadata format.
+.sp
+.B vgconvert -M2 vg1
+.SH RECOVERY
+Use \fBpvscan\fP(8) to see which PVs lost their metadata.
+Run \fBpvcreate\fP(8) with the --uuid and --restorefile options on each
+such PV to reformat it as it was, using the archive file that
+\fBvgconvert\fP(8) created at the start of the procedure.
+Finally run \fBvgcfgrestore\fP(8) with that archive file to restore
+the original metadata.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR vgcfgrestore (8)
diff --git a/man/vgcreate.8.in b/man/vgcreate.8.in
new file mode 100644 (file)
index 0000000..5ac5e2a
--- /dev/null
@@ -0,0 +1,150 @@
+.TH VGCREATE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgcreate \- create a volume group
+.SH SYNOPSIS
+.B vgcreate
+.RB [ \-\-addtag
+.IR Tag ]
+.RB [ \-\-alloc 
+.IR AllocationPolicy ]
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-c | \-\-clustered " {" y | n }]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-l | \-\-maxlogicalvolumes
+.IR MaxLogicalVolumes ]
+.RB [ -M | \-\-metadatatype type]
+.RB [ -p | \-\-maxphysicalvolumes
+.IR MaxPhysicalVolumes ]
+.RB [ \-\-[vg]metadatacopies ]
+.IR NumberOfCopies|unmanaged|all ]
+.RB [ \-s | \-\-physicalextentsize
+.IR PhysicalExtentSize [ \fBbBsSkKmMgGtTpPeE\fR ]]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-version ]
+[ \fIPHYSICAL DEVICE OPTIONS\fP ]
+.I VolumeGroupName PhysicalDevicePath
+.RI [ PhysicalDevicePath ...]
+.SH DESCRIPTION
+.B vgcreate
+creates a new volume group called
+.I VolumeGroupName
+using the block special device \fIPhysicalDevicePath\fP.
+.sp
+If \fIPhysicalDevicePath\fP was not previously configured for LVM with
+\fBpvcreate (8)\fP, the device will be initialized with the same
+default values used with \fBpvcreate\fP.  If non-default
+\fPpvcreate\fP values are desired, they may be given on the
+commandline with the same options as \fPpvcreate\fP.  See
+\fBPHYSICAL DEVICE OPTIONS\fP for available options.  Note
+that the restore-related options such as --restorefile, --uuid,
+and --physicalvolumesize are not available.  If a restore operation
+is needed, use \fBpvcreate (8)\fP and \fBvgcfgrestore (8)\fP.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-c ", " \-\-clustered " " { y | n }
+If clustered locking is enabled, this defaults to \fBy\fP indicating that 
+this Volume Group is shared with other nodes in the cluster.
+
+If the new Volume Group contains only local disks that are not visible 
+on the other nodes, you must specify \fB\-\-clustered\ n\fP.
+If the cluster infrastructure is unavailable on a particular node at a
+particular time, you may still be able to use such Volume Groups.
+.TP
+.BR \-l ", " \-\-maxlogicalvolumes " " \fIMaxLogicalVolumes\fR
+Sets the maximum number of logical volumes allowed in this
+volume group. 
+The setting can be changed with \fBvgchange\fP.
+For volume groups with metadata in lvm1 format, the limit
+and default value is 255.  
+If the metadata uses lvm2 format, the default value is 0
+which removes this restriction: there is then no limit.
+.TP
+.BR \-p ", " \-\-maxphysicalvolumes " " \fIMaxPhysicalVolumes\fR
+Sets the maximum number of physical volumes that can belong
+to this volume group.
+The setting can be changed with \fBvgchange\fP.
+For volume groups with metadata in lvm1 format, the limit
+and default value is 255.  
+If the metadata uses lvm2 format, the value 0 removes this restriction:
+there is then no limit.  If you have a large number of physical volumes in
+a volume group with metadata in lvm2 format, for tool performance reasons,
+you should consider some use of \fB--pvmetadatacopies 0\fP as described in
+\fBpvcreate(8)\fP, and/or use \fB--vgmetadatacopies\fP.
+.TP
+.BR \-\-vgmetadatacopies " " \fINumberOfCopies|unmanaged|all\fP
+Sets the desired number of metadata copies in the volume group.  If set to
+a non-zero value, LVM will automatically manage the 'metadataignore'
+flags on the physical volumes (see \fBpvcreate\fP or \fBpvchange\fP --metadataignore\fP) in order
+to achieve \fINumberOfCopies\fP copies of metadata.  If set to \fIunmanaged\fP,
+LVM will not automatically manage the 'metadataignore' flags.  If set to
+\fIall\fP, LVM will first clear all of the 'metadataignore' flags on all
+metadata areas in the volume group, then set the value to \fIunmanaged\fP.
+The \fBvgmetadatacopies\fP option is useful for volume groups containing
+large numbers of physical volumes with metadata as it may be used to
+minimize metadata read and write overhead.
+The default value is \fIunmanaged\fP.
+.TP
+.BR \-s ", " \-\-physicalextentsize " " \fIPhysicalExtentSize\fR[\fBbBsSkKmMgGtTpPeE\fR]
+Sets the physical extent size on physical volumes of this volume group.
+A size suffix (k for kilobytes up to t for terabytes) is optional, megabytes
+is the default if no suffix is present.  
+The default is 4 MB and it must be at least 1 KB and a power of 2.
+
+Once this value has been set, it is difficult to change it without recreating
+the volume group which would involve backing up and restoring data on any 
+logical volumes.  However, if no extents need moving for the new
+value to apply, it can be altered using vgchange \-s.
+
+If the volume group metadata uses lvm1 format, extents can vary in size from
+8KB to 16GB and there is a limit of 65534 extents in each logical volume.  The
+default of 4 MB leads to a maximum logical volume size of around 256GB.  
+
+If the volume group metadata uses lvm2 format those restrictions do not apply,
+but having a large number of extents will slow down the tools but have no
+impact on I/O performance to the logical volume.  The smallest PE is 1KB.
+
+The 2.4 kernel has a limitation of 2TB per block device.
+.SH PHYSICAL DEVICE OPTIONS
+The following options are available for initializing physical devices in the
+volume group.  These options are further described in the pvcreate man page.
+.TP
+.BR \-f ", " \-\-force
+.TP
+.BR \-y ", " \-\-yes
+.TP
+.BR \-Z ", " \-\-zero " y|n"
+.TP
+.BR \-\-labelsector " sector"
+.TP
+.BR \-\-metadatasize " size"
+.TP
+.BR \-\-pvmetadatacopies " copies"
+.TP
+.BR \-\-dataalignment " alignment"
+.TP
+.BR \-\-dataalignmentoffset " alignment_offset"
+.SH EXAMPLES
+To create a volume group named
+.B test_vg 
+using physical volumes
+.BR /dev/sdk1 ", and " /dev/sdl1
+with default physical extent size of 4MB:
+.nf
+
+\      vgcreate test_vg /dev/sdk1 /dev/sdl1
+
+.fi
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvdisplay (8),
+.BR pvcreate (8),
+.BR vgdisplay (8),
+.BR vgextend (8),
+.BR vgreduce (8),
+.BR lvcreate (8),
+.BR lvdisplay (8),
+.BR lvextend (8),
+.BR lvreduce (8)
diff --git a/man/vgdisplay.8.in b/man/vgdisplay.8.in
new file mode 100644 (file)
index 0000000..96dfe8c
--- /dev/null
@@ -0,0 +1,93 @@
+.TH VGDISPLAY 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgdisplay \- display attributes of volume groups
+.SH SYNOPSIS
+.B vgdisplay
+.RB [ \-A | \-\-activevolumegroups ]
+.RB [ \-c | \-\-colon | \-s | \-\-short | \-v|\-\-verbose ]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-ignorelockingfailure ]
+.RB [ \-\-nosuffix ]
+.RB [ \-P | \-\-partial ]
+.RB [\-\-units hHbBsSkKmMgGtTpPeE]
+.RB [ \-\-version ]
+.RI [VolumeGroupName [VolumeGroupName...]]
+.br
+
+.br
+.B vgdisplay \-\-columns | \-C
+.RB [ \-\-aligned ] [ \-d|\-\-debug ] [ \-h|\-?|\-\-help ]
+.RB [ \-\-ignorelockingfailure ] [ \-\-noheadings ] [ \-\-nosuffix ]
+.RB [ \-o|\-\-options [+]Field[,Field] ]
+.RB [ \-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]] ]
+.RB [ \-P|\-\-partial ]
+.RB [ \-\-separator Separator ]
+.RB [ \-\-unbuffered ]
+.RB [ \-\-units hHbBsSkKmMgGtTpPeE ]
+.RB [ \-v|\-\-verbose ]
+.RB [ \-\-version ]
+.RI [VolumeGroupName [VolumeGroupName...]]
+.SH DESCRIPTION
+.B vgdisplay
+allows you to see the attributes of
+.I VolumeGroupName
+(or all volume groups if none is given) with it's physical and logical
+volumes and their sizes etc.
+.P
+\fBvgs\fP (8) is an alternative that provides the same information 
+in the style of \fBps\fP (1).
+.SH OPTIONS
+See \fBlvm\fP for common options and \fBvgs\fP for options given with
+\fB\-\-columns\fP.
+.TP
+.BR \-A ", " \-\-activevolumegroups
+Only select the active volume groups.
+.TP
+.BR \-c ", " \-\-colon
+Generate colon separated output for easier parsing in scripts or programs.
+N.B. \fBvgs\fP (8) provides considerably more control over the output.
+.nf
+
+The values are:
+
+1  volume group name
+2  volume group access
+3  volume group status
+4  internal volume group number
+5  maximum number of logical volumes
+6  current number of logical volumes
+7  open count of all logical volumes in this volume group
+8  maximum logical volume size
+9  maximum number of physical volumes
+10 current number of physical volumes
+11 actual number of physical volumes
+12 size of volume group in kilobytes
+13 physical extent size
+14 total number of physical extents for this volume group
+15 allocated number of physical extents for this volume group
+16 free number of physical extents for this volume group
+17 uuid of volume group
+
+.fi
+.TP
+.BR \-s ", " \-\-short
+Give a short listing showing the existence of volume groups.
+.TP
+.BR \-v ", " \-\-verbose
+Display verbose information containing long listings of physical
+and logical volumes.  If given twice, also display verbose runtime
+information of vgdisplay's activities.
+.TP
+.BR \-\-version
+Display version and exit successfully.
+.TP
+.BR \-\-columns | \-C
+Display output in columns, the equivalent of \fBvgs\fP.  Options listed
+are the same as options given in \fPvgs (8)\fP.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgs (8),
+.BR pvcreate (8),
+.BR vgcreate (8),
+.BR lvcreate (8)
diff --git a/man/vgexport.8.in b/man/vgexport.8.in
new file mode 100644 (file)
index 0000000..27ce577
--- /dev/null
@@ -0,0 +1,27 @@
+.TH VGEXPORT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgexport \- make volume groups unknown to the system
+.SH SYNOPSIS
+.B vgexport
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help] 
+[\-v|\-\-verbose]
+VolumeGroupName [VolumeGroupName...]
+.SH DESCRIPTION
+vgexport allows you to make the inactive 
+.IR VolumeGroupName (s)
+unknown to the system.
+You can then move all the Physical Volumes in that Volume Group to
+a different system for later
+.BR vgimport (8).
+Most LVM2 tools ignore exported Volume Groups.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+Export all inactive Volume Groups.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvscan (8),
+.BR vgimport (8),
+.BR vgscan (8)
diff --git a/man/vgextend.8.in b/man/vgextend.8.in
new file mode 100644 (file)
index 0000000..4ae2f08
--- /dev/null
@@ -0,0 +1,62 @@
+.TH VGEXTEND 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgextend \- add physical volumes to a volume group
+.SH SYNOPSIS
+.B vgextend
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help] 
+[\-\-restoremissing]
+[\-f|\-\-force]
+[\-t|\-\-test]
+[\-v|\-\-verbose]
+[ \fIPHYSICAL DEVICE OPTIONS\fP ]
+VolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]
+.SH DESCRIPTION
+vgextend allows you to add one or more initialized physical volumes ( see
+.B pvcreate(8)
+) to an existing volume group to extend it in size. Moreover, it allows you to
+re-add a physical volume that has gone missing previously, due to a transient
+device failure, without re-initialising it. Use vgextend \-\-restoremissing to
+that effect.
+.sp
+If \fIPhysicalDevicePath\fP was not previously configured for LVM with
+\fBpvcreate (8)\fP, the device will be initialized with the same
+default values used with \fBpvcreate\fP.  If non-default
+\fPpvcreate\fP values are are desired, they may be given on the
+commandline with the same options as \fPpvcreate\fP.  See
+\fBPHYSICAL DEVICE OPTIONS\fP for available options.  Note
+that the restore-related options such as --restorefile, --uuid,
+and --physicalvolumesize are not available.  If a restore operation
+is needed, use \fBpvcreate (8)\fP and \fBvgcfgrestore (8)\fP.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH PHYSICAL DEVICE OPTIONS
+The following options are available for initializing physical devices in the
+volume group.  These options are further described in the pvcreate man page.
+.TP
+.BR \-f ", " \-\-force
+.TP
+.BR \-y ", " \-\-yes
+.TP
+.BR \-Z ", " \-\-zero " y|n"
+.TP
+.BR \-\-labelsector " sector"
+.TP
+.BR \-\-metadatasize " size"
+.TP
+.RB [ \-\-metadataignore y|n ]
+.TP
+.BR \-\-pvmetadatacopies " copies"
+.TP
+.BR \-\-dataalignment " alignment"
+.TP
+.BR \-\-dataalignmentoffset " alignment_offset"
+.SH Examples
+"vgextend vg00 /dev/sda4 /dev/sdn1" tries to extend the existing volume
+group "vg00" by the new physical volumes (see
+.B pvcreate(8)
+) "/dev/sdn1" and /dev/sda4".
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgreduce (8),
+.BR pvcreate (8)
diff --git a/man/vgimport.8.in b/man/vgimport.8.in
new file mode 100644 (file)
index 0000000..ede6394
--- /dev/null
@@ -0,0 +1,25 @@
+.TH VGIMPORT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgimport \- make exported volume groups known to the system
+.SH SYNOPSIS
+.B vgimport
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help] 
+[\-v|\-\-verbose]
+VolumeGroupName [VolumeGroupName...]
+.SH DESCRIPTION
+.B vgimport
+allows you to make a Volume Group that was previously exported using
+.BR vgexport (8)
+known to the system again, perhaps after moving its Physical Volumes
+from a different machine.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+Import all exported Volume Groups.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvscan (8),
+.BR vgexport (8),
+.BR vgscan (8)
diff --git a/man/vgimportclone.8.in b/man/vgimportclone.8.in
new file mode 100644 (file)
index 0000000..7b84df4
--- /dev/null
@@ -0,0 +1,57 @@
+.TH VGIMPORTCLONE 8 "LVM TOOLS #VERSION#" "Red Hat, Inc." \" -*- nroff -*-
+.SH NAME
+vgimportclone \- import and rename duplicated volume group (e.g. a hardware snapshot)
+.SH SYNOPSIS
+.B vgimportclone
+[\-n|\-\-basevgname VolumeGroupName]
+[\-i|\-\-import]
+PhysicalVolume [PhysicalVolume...]
+.SH DESCRIPTION
+.B vgimportclone
+is used to import a duplicated VG (e.g. hardware snapshot).  Duplicate VG(s)
+and PV(s) are not able to be used until they are made to coexist with
+the origin VG(s) and PV(s).   
+.B vgimportclone 
+renames the VG associated with the specified PV(s) and changes the
+associated VG and PV UUIDs.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-n|\-\-basevgname VolumeGroupName
+By default the snapshot VG will be renamed to the original name plus a
+numeric suffix to avoid duplicate naming (e.g. 'test_vg' would be renamed
+to 'test_vg1').  This option will override the base VG name that is
+used for all VG renames.  If a VG already exists with the specified name
+a numeric suffix will be added (like the previous example) to make it unique.
+.TP
+.I \-i|\-\-import
+Import exported Volume Groups.  Otherwise VGs that have been exported
+will not be changed (nor will their associated PVs).
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBLVM_BINARY\fP
+The LVM2 binary to use.
+Defaults to "lvm".
+.SH EXAMPLES
+The origin VG
+.B vg00
+has origin PVs
+.BR /dev/sda " and " /dev/sdb
+and the respective snapshot PVs are
+.BR /dev/sdc " and " /dev/sdd "."
+To rename the VG
+associated with
+.BR /dev/sdc " and " /dev/sdd
+from
+.B vg00
+to
+.B vg00_snap
+(and to change associated VG and PV UUIDs) do:
+.nf
+
+\      vgimportclone --basevgname vg00_snap /dev/sdc /dev/sdd
+
+.fi
+.SH SEE ALSO
+.BR lvm (8)
+
diff --git a/man/vgmerge.8.in b/man/vgmerge.8.in
new file mode 100644 (file)
index 0000000..bf1738b
--- /dev/null
@@ -0,0 +1,29 @@
+.TH VGMERGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgmerge \- merge two volume groups
+.SH SYNOPSIS
+.B vgmerge
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help] [\-l|\-\-list]
+[\-t|\-\-test] [\-v|\-\-verbose] DestinationVolumeGroupName
+SourceVolumeGroupName
+.SH DESCRIPTION
+vgmerge merges two existing volume groups. The inactive SourceVolumeGroupName
+will be merged into the DestinationVolumeGroupName if physical extent sizes
+are equal and physical and logical volume summaries of both volume groups
+fit into DestinationVolumeGroupName's limits.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.I \-l, \-\-list
+Display merged DestinationVolumeGroupName like "vgdisplay -v".
+.TP 
+.I \-t, \-\-test
+Do a test run WITHOUT making any real changes.
+.SH Examples
+"vgmerge -v databases my_vg" merges the inactive volume group named "my_vg" 
+into the active or inactive volume group named "databases" giving verbose
+runtime information.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgextend (8),
+.BR vgreduce (8)
diff --git a/man/vgmknodes.8.in b/man/vgmknodes.8.in
new file mode 100644 (file)
index 0000000..49f6a9d
--- /dev/null
@@ -0,0 +1,26 @@
+.TH VGMKNODES 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgmknodes \- recreate volume group directory and logical volume special files
+.SH SYNOPSIS
+.B vgmknodes
+.RB [ \-d | \-\-debug]
+.RB [ \-h | \-\-help]
+.RB [ \-\-refresh]
+.RB [ \-v | \-\-verbose]
+[[VolumeGroupName | LogicalVolumePath]...]
+.SH DESCRIPTION
+Checks the LVM2 special files in /dev that are needed for active 
+logical volumes and creates any missing ones and removes unused ones.
+.SH OPTIONS
+.TP
+.BR \-\-refresh
+If any logical volume in the volume group is active, reload its metadata.
+This is not necessary in normal operation, but may be useful
+if something has gone wrong or if you're doing clustering
+manually without a clustered lock manager.
+.TP
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgscan (8),
+.BR dmsetup (8)
diff --git a/man/vgreduce.8.in b/man/vgreduce.8.in
new file mode 100644 (file)
index 0000000..f4f5d83
--- /dev/null
@@ -0,0 +1,39 @@
+.TH VGREDUCE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgreduce \- reduce a volume group
+.SH SYNOPSIS
+.B vgreduce
+[\-a|\-\-all] [\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-removemissing]
+[\-t|\-\-test]
+[\-v|\-\-verbose] VolumeGroupName
+[PhysicalVolumePath...]
+.SH DESCRIPTION
+vgreduce allows you to remove one or more unused physical volumes
+from a volume group.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+Removes all empty physical volumes if none are given on command line.
+.TP
+.I \-\-removemissing
+Removes all missing physical volumes from the volume group, if there are no
+logical volumes allocated on those. This resumes normal operation of the volume
+group (new logical volumes may again be created, changed and so on).
+
+If this is not possible (there are logical volumes referencing the missing
+physical volumes) and you cannot or do not want to remove them manually, you
+can run this option with --force to have vgreduce remove any partial LVs.
+
+Any logical volumes and dependent snapshots that were partly on the 
+missing disks get removed completely. This includes those parts 
+that lie on disks that are still present.
+
+If your logical volumes spanned several disks including the ones that are
+lost, you might want to try to salvage data first by activating your
+logical volumes with --partial as described in \fBlvm (8)\fP.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgextend (8)
diff --git a/man/vgremove.8.in b/man/vgremove.8.in
new file mode 100644 (file)
index 0000000..0ec6510
--- /dev/null
@@ -0,0 +1,35 @@
+.TH VGREMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgremove \- remove a volume group
+.SH SYNOPSIS
+.B vgremove
+[\-d|\-\-debug] [\-f|\-\-force] [\-h|\-?|\-\-help]
+[\-\-noudevsync] [\-t|\-\-test] [\-v|\-\-verbose]
+VolumeGroupName [VolumeGroupName...]
+.SH DESCRIPTION
+vgremove allows you to remove one or more volume groups.
+If one or more physical volumes in the volume group are lost,
+consider \fBvgreduce --removemissing\fP to make the volume group
+metadata consistent again.
+.sp
+If there are logical volumes that exist in the volume group,
+a prompt will be given to confirm removal.  You can override
+the prompt with \fB-f\fP.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-f ", " \-\-force
+Force the removal of any logical volumes on the volume group
+without confirmation.
+.TP
+.BR \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background.  You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvremove (8),
+.BR vgcreate (8),
+.BR vgreduce (8)
diff --git a/man/vgrename.8.in b/man/vgrename.8.in
new file mode 100644 (file)
index 0000000..3fabbbb
--- /dev/null
@@ -0,0 +1,49 @@
+.TH VGRENAME 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgrename \- rename a volume group
+.SH SYNOPSIS
+.B vgrename
+[\-A|\-\-autobackup y|n]
+[\-d|\-\-debug]
+[\-h|\-?|\-\-help]
+[\-t|\-\-test]
+[\-v|\-\-verbose]
+.IR OldVolumeGroup { Path | Name | UUID }
+.IR NewVolumeGroup { Path | Name }
+.SH DESCRIPTION
+vgrename renames an existing (see
+.B vgcreate(8)
+) volume group from
+.IR OldVolumeGroup { Name | Path | UUID }
+to
+.IR NewVolumeGroup { Name | Path }.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH Examples
+"vgrename /dev/vg02 /dev/my_volume_group" renames existing
+volume group "vg02" to "my_volume_group".
+.TP
+"vgrename vg02 my_volume_group" does the same.
+.TP
+"vgrename Zvlifi-Ep3t-e0Ng-U42h-o0ye-KHu1-nl7Ns4 VolGroup00_tmp"
+changes the name of the Volume Group with UUID
+Zvlifi-Ep3t-e0Ng-U42h-o0ye-KHu1-nl7Ns4 to 
+"VolGroup00_tmp".
+
+All the Volume Groups visible to a system need to have different
+names.  Otherwise many LVM2 commands will refuse to run or give
+warning messages.
+
+This situation could arise when disks are moved between machines.  If
+a disk is connected and it contains a Volume Group with the same name
+as the Volume Group containing your root filesystem the machine might
+not even boot correctly.  However, the two Volume Groups should have
+different UUIDs (unless the disk was cloned) so you can rename
+one of the conflicting Volume Groups with
+\fBvgrename\fP.
+.TP
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgchange (8),
+.BR vgcreate (8),
+.BR lvrename (8)
diff --git a/man/vgs.8.in b/man/vgs.8.in
new file mode 100644 (file)
index 0000000..a1a97df
--- /dev/null
@@ -0,0 +1,100 @@
+.TH VGS 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgs \- report information about volume groups
+.SH SYNOPSIS
+.B vgs
+[\-a|\-\-all]
+[\-\-aligned] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure] [\-\-nameprefixes] [\-\-noheadings] [\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial] [\-\-rows]
+[\-\-separator Separator] [\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-\-unquoted]
+[\-v|\-\-verbose] 
+[\-\-version] [VolumeGroupName [VolumeGroupName...]]
+.SH DESCRIPTION
+vgs produces formatted output about volume groups.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-all
+List all volume groups.  Equivalent to not specifying any volume groups.
+.TP
+.I \-\-aligned
+Use with \-\-separator to align the output columns.
+.TP
+.I \-\-nameprefixes
+Add an "LVM2_" prefix plus the field name to the output.  Useful
+with --noheadings to produce a list of field=value pairs that can
+be used to set environment variables (for example, in \fBudev (7)\fP rules).
+.TP
+.I \-\-noheadings
+Suppress the headings line that is normally the first line of output.
+Useful if grepping the output.
+.TP
+.I \-\-nosuffix
+Suppress the suffix on output sizes.  Use with \-\-units (except h and H)
+if processing the output.
+.TP
+.I \-o, \-\-options
+Comma-separated ordered list of columns.  Precede the list with '+' to append
+to the default selection of columns.
+.IP
+Use \fb-o vg_all\fP to select all volume group columns.
+.IP
+Use \fb-o help\fP to view the full list of columns available.
+.IP
+Column names include: vg_fmt, vg_uuid, vg_name, vg_attr, vg_size, vg_free,
+vg_sysid, vg_extent_size, vg_extent_count, vg_free_count, max_lv, max_pv,
+pv_count, lv_count, snap_count, vg_seqno, vg_tags, vg_mda_count, vg_mda_free,
+and vg_mda_size, vg_mda_used_count.
+.IP
+Any "vg_" prefixes are optional.  Columns mentioned in either \fBpvs (8)\fP 
+or \fBlvs (8)\fP can also be chosen, but columns cannot be taken from both
+at the same time.  
+.IP
+The vg_attr bits are:
+.RS
+.IP 1 3
+Permissions: (w)riteable, (r)ead-only
+.IP 2 3
+Resi(z)eable
+.IP 3 3
+E(x)ported
+.IP 4 3
+(p)artial: one or more physical volumes belonging to the volume group
+are missing from the system
+.IP 5 3
+Allocation policy: (c)ontiguous, c(l)ing, (n)ormal, (a)nywhere, (i)nherited
+.IP 6 3
+(c)lustered
+.RE
+.TP
+.I \-O, \-\-sort
+Comma-separated ordered list of columns to sort by.  Replaces the default
+selection. Precede any column with - for a reverse sort on that column.
+.TP
+.I \-\-rows
+Output columns as rows.
+.TP
+.I \-\-separator Separator
+String to use to separate each column.  Useful if grepping the output.
+.TP
+.I \-\-unbuffered
+Produce output immediately without sorting or aligning the columns properly.
+.TP
+.I \-\-units hHbBsSkKmMgGtTpPeE
+All sizes are output in these units: (h)uman-readable, (b)ytes, (s)ectors,
+(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes.  
+Capitalise to use multiples of 1000 (S.I.) instead of 1024.  Can also specify 
+custom units e.g. \-\-units 3M
+.TP
+.I \-\-unquoted
+When used with --nameprefixes, output values in the field=value pairs are not quoted.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgdisplay (8),
+.BR pvs (8),
+.BR lvs (8)
diff --git a/man/vgscan.8.in b/man/vgscan.8.in
new file mode 100644 (file)
index 0000000..cc46269
--- /dev/null
@@ -0,0 +1,28 @@
+.TH VGSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgscan \- scan all disks for volume groups and rebuild caches
+.SH SYNOPSIS
+.B vgscan
+[\-d|\-\-debug] [\-h|\-?|\-\-help] 
+[\-\-ignorelockingfailure]
+[\-\-mknodes]
+[\-P|\-\-partial]
+[\-v|\-\-verbose]
+.SH DESCRIPTION
+vgscan scans all SCSI, (E)IDE disks, multiple devices and a bunch
+of other disk devices in the system looking for LVM physical volumes
+and volume groups.  Define a filter in \fBlvm.conf\fP(5) to restrict 
+the scan to avoid a CD ROM, for example.
+.LP
+In LVM2, vgscans take place automatically; but you might still need to
+run one explicitly after changing hardware.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-mknodes
+Also checks the LVM special files in /dev that are needed for active 
+logical volumes and creates any missing ones and removes unused ones.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgchange (8)
diff --git a/man/vgsplit.8.in b/man/vgsplit.8.in
new file mode 100644 (file)
index 0000000..0c6ba22
--- /dev/null
@@ -0,0 +1,75 @@
+.TH VGSPLIT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgsplit \- split a volume group into two
+.SH SYNOPSIS
+.B vgsplit
+.RB [ \-\-alloc
+.IR AllocationPolicy ]
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-c | \-\-clustered " {" y | n }]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-l | \-\-maxlogicalvolumes
+.IR MaxLogicalVolumes ]
+.RB [ -M | \-\-metadatatype
+.IR type ]
+.RB [ -p | \-\-maxphysicalvolumes
+.IR MaxPhysicalVolumes ]
+.RB [ \-\-[vg]metadatacopies ]
+.IR NumberOfCopies|unmanaged|all ]
+.RB [ \-n | \-\-name 
+.IR LogicalVolumeName ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+SourceVolumeGroupName DestinationVolumeGroupName
+[ PhysicalVolumePath ...]
+.SH DESCRIPTION
+.B vgsplit 
+moves one or more physical volumes from
+.I SourceVolumeGroupName
+into
+.I DestinationVolumeGroupName\fP.  The physical volumes moved can be
+specified either explicitly via \fIPhysicalVolumePath\fP, or implicitly by
+\fB-n\fP \fILogicalVolumeName\fP, in which case only physical volumes
+underlying the specified logical volume will be moved.
+
+If
+.I DestinationVolumeGroupName
+does not exist, a new volume group will be created.  The default attributes
+for the new volume group can be specified with \fB\-\-alloc\fR,
+\fB\-\-clustered\fR, \fB\-\-maxlogicalvolumes\fR, \fB\-\-metadatatype\fR,
+\fB\-\-maxphysicalvolumes\fR and \fB\-\-[vg]metadatacopies\fR,
+(see \fBvgcreate(8)\fR for a description of these options).  If any
+of these options are not given, default attribute(s) are taken from
+.I SourceVolumeGroupName\fP.  If a non-LVM2 metadata type (e.g. lvm1) is
+being used, you should use the -M option to specify the metadata type
+directly.
+
+If
+.I DestinationVolumeGroupName
+does exist, it will be checked for compatibility with
+.I SourceVolumeGroupName
+before the physical volumes are moved. Specifying any of the above default
+volume group attributes with an existing destination volume group is an error,
+and no split will occur.
+
+Logical volumes cannot be split between volume groups. \fBVgsplit(8)\fP only
+moves complete physical volumes: To move part of a physical volume, use
+\fBpvmove(8)\fP.  Each existing logical volume must be entirely on the physical
+volumes forming either the source or the destination volume group.  For this
+reason, \fBvgsplit(8)\fP may fail with an error if a split would result in a
+logical volume being split across volume groups.
+
+A \fBvgsplit\fP into an existing volume group retains the existing volume group's
+value of \fPvgmetadatacopies\fP (see \fBvgcreate\fP and \fBlvm.conf\fP for further
+explanation of \fPvgmetadatacopies\fP).  To change the value of
+\fBvgmetadatacopies\fP, use \fBvgchange\fP.
+
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgextend (8),
+.BR vgreduce (8),
+.BR vgmerge (8)
diff --git a/packaging/device-mapper.changes b/packaging/device-mapper.changes
new file mode 100644 (file)
index 0000000..2ebfe64
--- /dev/null
@@ -0,0 +1,14 @@
+* Wed Jan 05 2011 Paolo Capriotti <paolo.capriotti@collabora.co.uk> - 1.02.60
+- Update to latest upstream version (fixes BMC#12710)
+- Build from LVM2 source.
+- Remove obsolete patch.
+
+* Thu Dec 25 2008 Arjan van de Ven <arjan@linux.intel.com> 1.02.28
+- Fix Source: to be a resolvable URL
+
+* Tue Dec 09 2008 Yi Yang <yi.y.yang@intel.com> 1.02.28
+- Upgrade to 1.02.28
+- Fixed corrupt primary.xml.gz introduced by libdevmapper.pc
+
+* Sun Nov 02 2008 Anas Nashif <anas.nashif@intel.com> 1.02.24
+- fixed file list
diff --git a/packaging/device-mapper.spec b/packaging/device-mapper.spec
new file mode 100644 (file)
index 0000000..46f95e9
--- /dev/null
@@ -0,0 +1,81 @@
+%define device_mapper_version 1.02.28
+%define lvm2_version 2.02.79
+
+# Do not reset Release to 1 unless both lvm2 and device-mapper 
+# versions are increased together.
+
+Name: device-mapper
+Summary: Device mapper utility
+Version: %{device_mapper_version}
+Release: 1
+License: GPLv2
+Source0: ftp://sources.redhat.com/pub/lvm2/LVM2.%{lvm2_version}.tgz
+Group: System/Base
+URL: http://sources.redhat.com/dm
+Requires: device-mapper-libs = %{device_mapper_version}-%{release}
+
+%description -n device-mapper
+This package contains the supporting userspace utility, dmsetup,
+for the kernel device-mapper.
+
+%package -n device-mapper-devel
+Summary: Development libraries and headers for device-mapper
+Version: %{device_mapper_version}
+Release: %{release}
+License: LGPLv2.1
+Group: Development/Libraries
+Requires: device-mapper = %{device_mapper_version}-%{release}
+Requires: device-mapper-libs = %{device_mapper_version}-%{release}
+
+%description -n device-mapper-devel
+This package contains files needed to develop applications that use
+the device-mapper libraries.
+
+%package -n device-mapper-libs
+Summary: Device-mapper shared library
+Version: %{device_mapper_version}
+Release: %{release}
+License: LGPLv2.1
+Group: System/Libraries
+Obsoletes: device-mapper < 1.02.17-6
+
+%description -n device-mapper-libs
+This package contains the device-mapper shared library, libdevmapper.
+
+%prep
+%setup -q -n LVM2.%{lvm2_version}
+
+%build
+%define _exec_prefix ""
+%configure --with-user= --with-group= --with-device-uid=0 --with-device-gid=6 --with-device-mode=0660 --enable-pkgconfig
+%define _exec_prefix /
+make device-mapper
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install_device-mapper DESTDIR=$RPM_BUILD_ROOT usrlibdir=$RPM_BUILD_ROOT/usr/%{_lib}
+sed -i 's/ (.*)//g' $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post -n device-mapper-libs -p /sbin/ldconfig
+
+%postun -n device-mapper-libs -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING COPYING.LIB INSTALL README VERSION_DM WHATS_NEW_DM
+%attr(755,root,root) %{_sbindir}/dmsetup
+%{_mandir}/man8/dmsetup.8.gz
+
+%files -n device-mapper-devel
+%defattr(-,root,root,-)
+%attr(755,root,root) /%{_libdir}/libdevmapper.so
+%{_includedir}/libdevmapper.h
+%{_libdir}/pkgconfig/*.pc
+
+%files -n device-mapper-libs
+%attr(755,root,root) /%{_libdir}/libdevmapper.so.*
+
+
diff --git a/po/Makefile.in b/po/Makefile.in
new file mode 100644 (file)
index 0000000..a4f57a6
--- /dev/null
@@ -0,0 +1,65 @@
+#
+# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+LANGS=de
+
+TARGETS=$(LANGS:%=lvm2_%.mo) $(LANGS:%=dm_%.mo)
+
+DM_POSOURCES = $(top_srcdir)/dmsetup/*.pot $(top_srcdir)/libdm/*.pot \
+              $(top_srcdir)/libdm/*/*.pot
+
+LVM_POSOURCES = $(top_srcdir)/tools/*.pot $(top_srcdir)/lib/*/*.pot
+
+include $(top_builddir)/make.tmpl
+
+lvm2.po: Makefile $(LVM_POSOURCES)
+       @echo Compiling string table
+       @xgettext -C -F --keyword=print_log --keyword=log_debug \
+               --keyword=log_info --keyword=_ --keyword=N_ \
+               --keyword=log_notice --keyword=log_warn --keyword=log_err \
+               --keyword=log_fatal --keyword=log_debug --keyword=log_error \
+               --keyword=log_print --keyword=log_verbose \
+               --keyword=log_very_verbose -d - \
+               $(LVM_POSOURCES) > $@
+
+device-mapper.po: Makefile $(DM_POSOURCES)
+       @echo Compiling string table
+       @xgettext -C -F --keyword=dm_log --keyword=log_debug \
+               --keyword=log_info --keyword=_ --keyword=N_ \
+               --keyword=log_notice --keyword=log_warn --keyword=log_err \
+               --keyword=log_fatal --keyword=log_debug --keyword=log_error \
+               --keyword=log_print --keyword=log_verbose \
+               --keyword=log_very_verbose -d - \
+               $(DM_POSOURCES) > $@
+
+pofile: lvm2.po device-mapper.po
+
+# FIXME
+install: $(TARGETS)
+       @echo Installing translation files in $(localedir)
+       @( \
+               for lang in $(LANGS); do \
+                       $(INSTALL_DATA) -D $$lang.mo \
+                           $(localedir)/$$lang/LC_MESSAGES/lvm2.mo;\
+         done; \
+       )
+       @( \
+               for lang in $(LANGS); do \
+                       $(INSTALL_DATA) -D $$lang.mo \
+                           $(localedir)/$$lang/LC_MESSAGES/device-mapper.mo;\
+         done; \
+       )
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..d1bbbbe
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,10 @@
+# Dummy test file
+msgid ""
+msgstr ""
+"PO-Revision-Date: 2004-02-13 20:35+0000\n"
+"Last-Translator: Nobody <nobody@nowhere>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
diff --git a/po/lvm2.po b/po/lvm2.po
new file mode 100644 (file)
index 0000000..2b236ae
--- /dev/null
@@ -0,0 +1,7630 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-04-27 21:46+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activate/activate.c:44
+msgid "LVM1 proc global snprintf failed"
+msgstr ""
+
+#: activate/activate.c:63
+msgid "module string allocation failed"
+msgstr ""
+
+#: activate/activate.c:74 activate/activate.c:91 activate/activate.c:109
+#: activate/activate.c:364 activate/activate.c:417 activate/activate.c:438
+#: activate/activate.c:445 activate/activate.c:492 activate/activate.c:495
+#: activate/activate.c:514 activate/activate.c:520 activate/activate.c:523
+#: activate/activate.c:536 activate/activate.c:548 activate/activate.c:561
+#: activate/activate.c:564 activate/activate.c:576 activate/activate.c:579
+#: activate/activate.c:591 activate/activate.c:594 activate/activate.c:606
+#: activate/activate.c:609 activate/activate.c:764 activate/activate.c:768
+#: activate/activate.c:776 activate/activate.c:785 activate/activate.c:791
+#: activate/activate.c:836 activate/activate.c:848 activate/activate.c:882
+#: activate/activate.c:894 activate/activate.c:953 activate/activate.c:967
+#: activate/activate.c:996 activate/dev_manager.c:104
+#: activate/dev_manager.c:130 activate/dev_manager.c:139
+#: activate/dev_manager.c:142 activate/dev_manager.c:168
+#: activate/dev_manager.c:176 activate/dev_manager.c:250
+#: activate/dev_manager.c:258 activate/dev_manager.c:261
+#: activate/dev_manager.c:339 activate/dev_manager.c:347
+#: activate/dev_manager.c:350 activate/dev_manager.c:379
+#: activate/dev_manager.c:434 activate/dev_manager.c:439
+#: activate/dev_manager.c:452 activate/dev_manager.c:489
+#: activate/dev_manager.c:492 activate/dev_manager.c:500
+#: activate/dev_manager.c:523 activate/dev_manager.c:535
+#: activate/dev_manager.c:611 activate/dev_manager.c:628
+#: activate/dev_manager.c:631 activate/dev_manager.c:654
+#: activate/dev_manager.c:658 activate/dev_manager.c:661
+#: activate/dev_manager.c:664 activate/dev_manager.c:682
+#: activate/dev_manager.c:689 activate/dev_manager.c:698
+#: activate/dev_manager.c:737 activate/dev_manager.c:757
+#: activate/dev_manager.c:760 activate/dev_manager.c:780
+#: activate/dev_manager.c:783 activate/dev_manager.c:788
+#: activate/dev_manager.c:842 activate/dev_manager.c:851
+#: activate/dev_manager.c:854 activate/dev_manager.c:860
+#: activate/dev_manager.c:866 activate/dev_manager.c:869
+#: activate/dev_manager.c:871 activate/dev_manager.c:877
+#: activate/dev_manager.c:891 activate/dev_manager.c:894
+#: activate/dev_manager.c:920 activate/dev_manager.c:929
+#: activate/dev_manager.c:996 activate/dev_manager.c:1010
+#: activate/dev_manager.c:1018 activate/dev_manager.c:1025
+#: activate/dev_manager.c:1030 activate/dev_manager.c:1038
+#: activate/dev_manager.c:1044 activate/dev_manager.c:1048
+#: activate/dev_manager.c:1052 activate/dev_manager.c:1075
+#: activate/dev_manager.c:1138 activate/fs.c:179 activate/fs.c:229
+#: activate/fs.c:236 activate/fs.c:243 activate/fs.c:246 activate/fs.c:320
+#: archiver.c:68 archiver.c:75 archiver.c:87 archiver.c:163 archiver.c:236
+#: archiver.c:286 archiver.c:303 archiver.c:345 archiver.c:350
+#: cache/lvmcache.c:486 cache/lvmcache.c:490 cache/lvmcache.c:704
+#: cache/lvmcache.c:724 cache/lvmcache.c:750 cache/lvmcache.c:810
+#: commands/toolcontext.c:276 commands/toolcontext.c:295
+#: commands/toolcontext.c:302 commands/toolcontext.c:379
+#: commands/toolcontext.c:394 commands/toolcontext.c:418
+#: commands/toolcontext.c:469 commands/toolcontext.c:685
+#: commands/toolcontext.c:781 config/config.c:148 config/config.c:161
+#: config/config.c:176 config/config.c:194 config/config.c:215
+#: config/config.c:235 config/config.c:282 config/config.c:285
+#: config/config.c:467 config/config.c:485 config/config.c:490
+#: config/config.c:500 config/config.c:514 config/config.c:530
+#: config/config.c:586 config/config.c:777 datastruct/btree.c:90
+#: datastruct/str_list.c:24 datastruct/str_list.c:38 datastruct/str_list.c:47
+#: datastruct/str_list.c:77 device/dev-cache.c:240 device/dev-cache.c:253
+#: device/dev-cache.c:298 device/dev-cache.c:302 device/dev-cache.c:373
+#: device/dev-cache.c:404 device/dev-cache.c:443 device/dev-cache.c:511
+#: device/dev-cache.c:547 device/dev-cache.c:552 device/dev-cache.c:567
+#: device/dev-io.c:174 device/dev-io.c:204 device/dev-io.c:358
+#: device/dev-io.c:556 device/dev-io.c:606 device/dev-io.c:624
+#: device/dev-io.c:643 device/dev-io.c:671 device/dev-md.c:41
+#: device/dev-md.c:49 device/dev-md.c:66 device/device.c:61 device/device.c:66
+#: device/device.c:90 display/display.c:243 display/display.c:274
+#: display/display.c:333 display/display.c:379 display/display.c:605
+#: display/display.c:641 error/errseg.c:101 filters/filter-composite.c:54
+#: filters/filter-persistent.c:46 filters/filter-persistent.c:110
+#: filters/filter-persistent.c:114 filters/filter-persistent.c:117
+#: filters/filter-persistent.c:197 filters/filter-persistent.c:299
+#: filters/filter-persistent.c:305 filters/filter-persistent.c:316
+#: filters/filter-regex.c:74 filters/filter-regex.c:101
+#: filters/filter-regex.c:119 filters/filter-regex.c:142
+#: filters/filter-regex.c:196 filters/filter-regex.c:201
+#: filters/filter-regex.c:206 filters/filter-regex.c:209
+#: filters/filter-sysfs.c:288 filters/filter.c:278 format1/disk-rep.c:221
+#: format1/disk-rep.c:233 format1/disk-rep.c:238 format1/disk-rep.c:257
+#: format1/disk-rep.c:260 format1/disk-rep.c:291 format1/disk-rep.c:294
+#: format1/disk-rep.c:313 format1/disk-rep.c:316 format1/disk-rep.c:334
+#: format1/disk-rep.c:351 format1/disk-rep.c:361 format1/disk-rep.c:421
+#: format1/disk-rep.c:428 format1/disk-rep.c:522 format1/disk-rep.c:547
+#: format1/disk-rep.c:563 format1/disk-rep.c:591 format1/disk-rep.c:609
+#: format1/disk-rep.c:646 format1/disk-rep.c:711 format1/disk-rep.c:718
+#: format1/disk-rep.c:734 format1/format1.c:134 format1/format1.c:137
+#: format1/format1.c:149 format1/format1.c:154 format1/format1.c:157
+#: format1/format1.c:160 format1/format1.c:163 format1/format1.c:166
+#: format1/format1.c:171 format1/format1.c:186 format1/format1.c:195
+#: format1/format1.c:198 format1/format1.c:213 format1/format1.c:227
+#: format1/format1.c:245 format1/format1.c:256 format1/format1.c:271
+#: format1/format1.c:297 format1/format1.c:302 format1/format1.c:307
+#: format1/format1.c:312 format1/format1.c:348 format1/format1.c:394
+#: format1/format1.c:410 format1/format1.c:415 format1/format1.c:421
+#: format1/format1.c:431 format1/format1.c:477 format1/format1.c:498
+#: format1/format1.c:507 format1/format1.c:551 format1/import-export.c:63
+#: format1/import-export.c:118 format1/import-export.c:151
+#: format1/import-export.c:168 format1/import-export.c:185
+#: format1/import-export.c:193 format1/import-export.c:228
+#: format1/import-export.c:233 format1/import-export.c:238
+#: format1/import-export.c:316 format1/import-export.c:448
+#: format1/import-export.c:453 format1/import-export.c:474
+#: format1/import-export.c:481 format1/import-export.c:503
+#: format1/import-export.c:524 format1/import-export.c:529
+#: format1/import-export.c:538 format1/import-export.c:548
+#: format1/import-export.c:558 format1/import-export.c:563
+#: format1/import-export.c:666 format1/import-export.c:714
+#: format1/import-extents.c:63 format1/import-extents.c:68
+#: format1/import-extents.c:71 format1/import-extents.c:122
+#: format1/import-extents.c:193 format1/import-extents.c:220
+#: format1/import-extents.c:235 format1/import-extents.c:284
+#: format1/import-extents.c:314 format1/import-extents.c:338
+#: format1/import-extents.c:354 format1/import-extents.c:369
+#: format1/layout.c:126 format1/lvm1-label.c:75 format1/vg_number.c:37
+#: format1/vg_number.c:42 format_pool/disk_rep.c:49 format_pool/disk_rep.c:102
+#: format_pool/disk_rep.c:256 format_pool/disk_rep.c:358
+#: format_pool/disk_rep.c:368 format_pool/disk_rep.c:373
+#: format_pool/format_pool.c:132 format_pool/format_pool.c:137
+#: format_pool/format_pool.c:142 format_pool/format_pool.c:152
+#: format_pool/format_pool.c:161 format_pool/format_pool.c:166
+#: format_pool/format_pool.c:186 format_pool/format_pool.c:195
+#: format_pool/format_pool.c:201 format_pool/format_pool.c:231
+#: format_pool/format_pool.c:236 format_pool/format_pool.c:246
+#: format_pool/format_pool.c:251 format_pool/import_export.c:93
+#: format_pool/import_export.c:180 format_pool/import_export.c:218
+#: format_pool/import_export.c:232 format_pool/import_export.c:256
+#: format_pool/import_export.c:276 format_pool/import_export.c:304
+#: format_pool/import_export.c:309 format_text/archive.c:117
+#: format_text/archive.c:138 format_text/archive.c:165
+#: format_text/archive.c:258 format_text/archive.c:274
+#: format_text/archive.c:350 format_text/archive.c:370
+#: format_text/archiver.c:82 format_text/archiver.c:89
+#: format_text/archiver.c:101 format_text/archiver.c:189
+#: format_text/archiver.c:267 format_text/archiver.c:317
+#: format_text/archiver.c:334 format_text/archiver.c:376
+#: format_text/archiver.c:381 format_text/export.c:138
+#: format_text/export.c:198 format_text/export.c:206 format_text/export.c:293
+#: format_text/export.c:294 format_text/export.c:295 format_text/export.c:296
+#: format_text/export.c:298 format_text/export.c:299 format_text/export.c:300
+#: format_text/export.c:303 format_text/export.c:313 format_text/export.c:317
+#: format_text/export.c:319 format_text/export.c:322 format_text/export.c:325
+#: format_text/export.c:329 format_text/export.c:332 format_text/export.c:336
+#: format_text/export.c:340 format_text/export.c:343 format_text/export.c:344
+#: format_text/export.c:348 format_text/export.c:349 format_text/export.c:373
+#: format_text/export.c:380 format_text/export.c:384 format_text/export.c:385
+#: format_text/export.c:389 format_text/export.c:393 format_text/export.c:395
+#: format_text/export.c:398 format_text/export.c:401 format_text/export.c:404
+#: format_text/export.c:408 format_text/export.c:411 format_text/export.c:415
+#: format_text/export.c:419 format_text/export.c:422 format_text/export.c:427
+#: format_text/export.c:431 format_text/export.c:440 format_text/export.c:443
+#: format_text/export.c:446 format_text/export.c:450 format_text/export.c:451
+#: format_text/export.c:455 format_text/export.c:458 format_text/export.c:463
+#: format_text/export.c:468 format_text/export.c:479 format_text/export.c:481
+#: format_text/export.c:488 format_text/export.c:492 format_text/export.c:497
+#: format_text/export.c:508 format_text/export.c:518 format_text/export.c:519
+#: format_text/export.c:524 format_text/export.c:528 format_text/export.c:531
+#: format_text/export.c:534 format_text/export.c:538 format_text/export.c:541
+#: format_text/export.c:545 format_text/export.c:549 format_text/export.c:551
+#: format_text/export.c:553 format_text/export.c:554 format_text/export.c:555
+#: format_text/export.c:560 format_text/export.c:566 format_text/export.c:581
+#: format_text/export.c:591 format_text/export.c:600 format_text/export.c:606
+#: format_text/export.c:624 format_text/export.c:627 format_text/export.c:634
+#: format_text/export.c:637 format_text/export.c:640 format_text/export.c:652
+#: format_text/export.c:657 format_text/export.c:660 format_text/export.c:665
+#: format_text/export.c:667 format_text/export.c:669 format_text/export.c:671
+#: format_text/export.c:673 format_text/export.c:677 format_text/export.c:680
+#: format_text/export.c:702 format_text/export.c:729 format_text/export.c:747
+#: format_text/flags.c:94 format_text/flags.c:138
+#: format_text/format-text.c:158 format_text/format-text.c:161
+#: format_text/format-text.c:195 format_text/format-text.c:199
+#: format_text/format-text.c:238 format_text/format-text.c:295
+#: format_text/format-text.c:346 format_text/format-text.c:378
+#: format_text/format-text.c:420 format_text/format-text.c:425
+#: format_text/format-text.c:433 format_text/format-text.c:451
+#: format_text/format-text.c:456 format_text/format-text.c:481
+#: format_text/format-text.c:494 format_text/format-text.c:542
+#: format_text/format-text.c:547 format_text/format-text.c:587
+#: format_text/format-text.c:601 format_text/format-text.c:619
+#: format_text/format-text.c:650 format_text/format-text.c:700
+#: format_text/format-text.c:757 format_text/format-text.c:762
+#: format_text/format-text.c:785 format_text/format-text.c:799
+#: format_text/format-text.c:1059 format_text/format-text.c:1064
+#: format_text/format-text.c:1072 format_text/format-text.c:1082
+#: format_text/format-text.c:1103 format_text/format-text.c:1107
+#: format_text/format-text.c:1113 format_text/format-text.c:1125
+#: format_text/format-text.c:1309 format_text/format-text.c:1365
+#: format_text/format-text.c:1370 format_text/format-text.c:1380
+#: format_text/format-text.c:1382 format_text/format-text.c:1390
+#: format_text/format-text.c:1430 format_text/format-text.c:1436
+#: format_text/format-text.c:1621 format_text/format-text.c:1627
+#: format_text/format-text.c:1666 format_text/format-text.c:1711
+#: format_text/format-text.c:1730 format_text/format-text.c:1746
+#: format_text/format-text.c:1751 format_text/format-text.c:1765
+#: format_text/format-text.c:1777 format_text/format-text.c:1783
+#: format_text/format-text.c:1813 format_text/format-text.c:1818
+#: format_text/format-text.c:1823 format_text/format-text.c:1832
+#: format_text/format-text.c:1935 format_text/import.c:47
+#: format_text/import.c:52 format_text/import.c:63 format_text/import.c:98
+#: format_text/import.c:115 format_text/import_vsn1.c:123
+#: format_text/import_vsn1.c:134 format_text/import_vsn1.c:167
+#: format_text/import_vsn1.c:237 format_text/import_vsn1.c:303
+#: format_text/import_vsn1.c:309 format_text/import_vsn1.c:322
+#: format_text/import_vsn1.c:387 format_text/import_vsn1.c:429
+#: format_text/import_vsn1.c:457 format_text/import_vsn1.c:465
+#: format_text/import_vsn1.c:482 format_text/import_vsn1.c:489
+#: format_text/import_vsn1.c:518 format_text/import_vsn1.c:576
+#: format_text/import_vsn1.c:629 format_text/import_vsn1.c:654
+#: format_text/import_vsn1.c:664 format_text/import_vsn1.c:667
+#: format_text/import_vsn1.c:735 format_text/import_vsn1.c:846
+#: format_text/tags.c:28 format_text/tags.c:35 format_text/tags.c:42
+#: format_text/tags.c:48 format_text/tags.c:67 format_text/text_label.c:210
+#: format_text/text_label.c:246 label/label.c:90 label/label.c:207
+#: label/label.c:258 label/label.c:274 label/label.c:284 label/label.c:291
+#: label/label.c:321 label/label.c:329 label/label.c:341 label/label.c:360
+#: label/label.c:364 label/label.c:370 locking/cluster_locking.c:85
+#: locking/cluster_locking.c:420 locking/cluster_locking.c:432
+#: locking/cluster_locking.c:436 locking/external_locking.c:77 lvchange.c:57
+#: lvchange.c:99 lvchange.c:116 lvchange.c:122 lvchange.c:136 lvchange.c:143
+#: lvchange.c:150 lvchange.c:268 lvchange.c:282 lvchange.c:353 lvchange.c:361
+#: lvchange.c:395 lvchange.c:472 lvchange.c:479 lvchange.c:526 lvchange.c:534
+#: lvconvert.c:96 lvconvert.c:147 lvconvert.c:211 lvconvert.c:222
+#: lvconvert.c:273 lvconvert.c:285 lvconvert.c:298 lvconvert.c:332
+#: lvconvert.c:354 lvconvert.c:369 lvconvert.c:378 lvconvert.c:397
+#: lvconvert.c:404 lvconvert.c:470 lvconvert.c:481 lvconvert.c:544
+#: lvconvert.c:585 lvcreate.c:133 lvcreate.c:349 lvcreate.c:373 lvcreate.c:399
+#: lvcreate.c:529 lvcreate.c:661 lvcreate.c:698 lvcreate.c:728 lvcreate.c:755
+#: lvcreate.c:763 lvcreate.c:769 lvcreate.c:776 lvcreate.c:868
+#: lvmcmdline.c:830 lvmcmdline.c:836 lvmcmdline.c:839 lvmcmdline.c:842
+#: lvmcmdline.c:846 lvmcmdline.c:853 lvmcmdline.c:885 lvmcmdline.c:896
+#: lvmcmdline.c:906 lvmcmdline.c:936 lvmcmdline.c:1022 lvremove.c:99
+#: lvrename.c:85 lvrename.c:164 lvrename.c:175 lvrename.c:182 lvrename.c:188
+#: lvresize.c:466 lvresize.c:522 lvresize.c:529 lvresize.c:536 lvresize.c:547
+#: lvresize.c:554 lvresize.c:560 lvresize.c:579 lvresize.c:593 lvresize.c:618
+#: metadata/lv_manip.c:85 metadata/lv_manip.c:91 metadata/lv_manip.c:192
+#: metadata/lv_manip.c:227 metadata/lv_manip.c:258 metadata/lv_manip.c:316
+#: metadata/lv_manip.c:325 metadata/lv_manip.c:340 metadata/lv_manip.c:349
+#: metadata/lv_manip.c:379 metadata/lv_manip.c:580 metadata/lv_manip.c:588
+#: metadata/lv_manip.c:623 metadata/lv_manip.c:735 metadata/lv_manip.c:738
+#: metadata/lv_manip.c:748 metadata/lv_manip.c:846 metadata/lv_manip.c:874
+#: metadata/lv_manip.c:1048 metadata/lv_manip.c:1095 metadata/lv_manip.c:1100
+#: metadata/lv_manip.c:1130 metadata/lv_manip.c:1221 metadata/lv_manip.c:1228
+#: metadata/lv_manip.c:1265 metadata/lv_manip.c:1277 metadata/lv_manip.c:1306
+#: metadata/lv_manip.c:1316 metadata/lv_manip.c:1364 metadata/lv_manip.c:1429
+#: metadata/lv_manip.c:1436 metadata/lv_manip.c:1548 metadata/lv_manip.c:1619
+#: metadata/merge.c:253 metadata/merge.c:292 metadata/merge.c:297
+#: metadata/metadata.c:119 metadata/metadata.c:154 metadata/metadata.c:182
+#: metadata/metadata.c:252 metadata/metadata.c:276 metadata/metadata.c:284
+#: metadata/metadata.c:322 metadata/metadata.c:372 metadata/metadata.c:378
+#: metadata/metadata.c:384 metadata/metadata.c:395 metadata/metadata.c:401
+#: metadata/metadata.c:413 metadata/metadata.c:419 metadata/metadata.c:431
+#: metadata/metadata.c:439 metadata/metadata.c:446 metadata/metadata.c:453
+#: metadata/metadata.c:460 metadata/metadata.c:473 metadata/metadata.c:481
+#: metadata/metadata.c:490 metadata/metadata.c:549 metadata/metadata.c:564
+#: metadata/metadata.c:754 metadata/metadata.c:779 metadata/metadata.c:815
+#: metadata/metadata.c:846 metadata/metadata.c:874 metadata/metadata.c:880
+#: metadata/metadata.c:887 metadata/metadata.c:898 metadata/metadata.c:903
+#: metadata/metadata.c:925 metadata/metadata.c:947 metadata/metadata.c:964
+#: metadata/metadata.c:1063 metadata/metadata.c:1068 metadata/metadata.c:1079
+#: metadata/metadata.c:1137 metadata/metadata.c:1142 metadata/metadata.c:1168
+#: metadata/metadata.c:1183 metadata/metadata.c:1191 metadata/metadata.c:1246
+#: metadata/metadata.c:1250 metadata/metadata.c:1399 metadata/metadata.c:1433
+#: metadata/metadata.c:1490 metadata/metadata.c:1494 metadata/metadata.c:1527
+#: metadata/mirror.c:106 metadata/mirror.c:109 metadata/mirror.c:112
+#: metadata/mirror.c:205 metadata/mirror.c:484 metadata/mirror.c:526
+#: metadata/mirror.c:560 metadata/mirror.c:599 metadata/mirror.c:608
+#: metadata/mirror.c:736 metadata/mirror.c:757 metadata/mirror.c:762
+#: metadata/mirror.c:836 metadata/pv_manip.c:54 metadata/pv_manip.c:73
+#: metadata/pv_manip.c:94 metadata/pv_manip.c:131 metadata/pv_manip.c:156
+#: metadata/pv_manip.c:197 metadata/pv_manip.c:332 metadata/pv_map.c:44
+#: metadata/pv_map.c:92 metadata/pv_map.c:112 metadata/pv_map.c:122
+#: metadata/pv_map.c:149 metadata/pv_map.c:159 metadata/snapshot_manip.c:70
+#: metadata/snapshot_manip.c:77 mirror/mirrored.c:144 mirror/mirrored.c:149
+#: mirror/mirrored.c:151 mirror/mirrored.c:304 mirror/mirrored.c:328
+#: mirror/mirrored.c:331 mirror/mirrored.c:501 mirror/mirrored.c:552
+#: misc/lvm-file.c:291 misc/timestamp.c:44 pvchange.c:191 pvmove.c:102
+#: pvmove.c:107 pvmove.c:192 pvmove.c:220 pvmove.c:227 pvmove.c:292
+#: pvmove.c:299 pvmove.c:308 pvmove.c:337 pvmove.c:349 pvmove.c:356
+#: pvmove.c:363 pvmove.c:371 pvmove.c:383 pvmove.c:524 pvresize.c:165
+#: pvscan.c:55 report/report.c:187 report/report.c:513 report/report.c:543
+#: report/report.c:699 reporter.c:289 snapshot/snapshot.c:74
+#: snapshot/snapshot.c:83 snapshot/snapshot.c:84 snapshot/snapshot.c:85
+#: snapshot/snapshot.c:169 striped/striped.c:89 striped/striped.c:169
+#: striped/striped.c:172 striped/striped.c:216 toollib.c:912 toollib.c:962
+#: toollib.c:1020 toollib.c:1060 toollib.c:1085 toollib.c:1194 toollib.c:1332
+#: toollib.c:1337 toollib.c:1350 toollib.c:1357 uuid/uuid.c:90 uuid/uuid.c:94
+#: vgcfgbackup.c:69 vgcfgbackup.c:78 vgcfgbackup.c:85 vgchange.c:420
+#: vgmerge.c:193 vgreduce.c:29 vgreduce.c:96 vgreduce.c:102 vgreduce.c:124
+#: vgreduce.c:130 vgreduce.c:148 vgreduce.c:196 vgreduce.c:217 vgreduce.c:241
+#: vgreduce.c:307 vgreduce.c:353 zero/zero.c:99
+msgid "<backtrace>"
+msgstr ""
+
+#: activate/activate.c:81
+msgid "snap_seg module string allocation failed"
+msgstr ""
+
+#: activate/activate.c:245
+msgid "Activation enabled. Device-mapper kernel driver will be used."
+msgstr ""
+
+#: activate/activate.c:248
+msgid ""
+"WARNING: Activation disabled. No device-mapper interaction will be attempted."
+msgstr ""
+
+#: activate/activate.c:281
+msgid "Ignoring invalid string in config file activation/volume_list"
+msgstr ""
+
+#: activate/activate.c:287
+msgid "Ignoring empty string in config file activation/volume_list"
+msgstr ""
+
+#: activate/activate.c:296
+msgid "Ignoring empty tag in config file activation/volume_list"
+msgstr ""
+
+#: activate/activate.c:326
+#, c-format
+msgid "dm_snprintf error from %s/%s"
+msgstr ""
+
+#: activate/activate.c:350
+msgid "Getting driver version"
+msgstr ""
+
+#: activate/activate.c:362
+#, c-format
+msgid "Getting target version for %s"
+msgstr ""
+
+#: activate/activate.c:367
+#, c-format
+msgid "Failed to get %s target version"
+msgstr ""
+
+#: activate/activate.c:411
+#, c-format
+msgid "target_present module name too long: %s"
+msgstr ""
+
+#: activate/activate.c:440
+#, c-format
+msgid "Getting device info for %s"
+msgstr ""
+
+#: activate/activate.c:771
+#, c-format
+msgid "Skipping: Suspending '%s'."
+msgstr ""
+
+#: activate/activate.c:831
+#, c-format
+msgid "Skipping: Resuming '%s'."
+msgstr ""
+
+#: activate/activate.c:877
+#, c-format
+msgid "Skipping: Deactivating '%s'."
+msgstr ""
+
+#: activate/activate.c:888
+#, c-format
+msgid "LV %s/%s in use: not deactivating"
+msgstr ""
+
+#: activate/activate.c:917 activate/activate.c:942
+#, c-format
+msgid "Not activating %s/%s due to config file settings"
+msgstr ""
+
+#: activate/activate.c:948
+#, c-format
+msgid "Skipping: Activating '%s'."
+msgstr ""
+
+#: activate/dev_manager.c:75
+#, c-format
+msgid "_build_dlid: pool allocation failed for %zu %s %s."
+msgstr ""
+
+#: activate/dev_manager.c:136 activate/dev_manager.c:255
+#: activate/dev_manager.c:344
+msgid "Failed to disable open_count"
+msgstr ""
+
+#: activate/dev_manager.c:163
+msgid "Failed to allocate dm_task struct to check dev status"
+msgstr ""
+
+#: activate/dev_manager.c:171
+msgid "Failed to get state of mapped device"
+msgstr ""
+
+#: activate/dev_manager.c:229 activate/dev_manager.c:528
+#, c-format
+msgid "dlid build failed for %s"
+msgstr ""
+
+#: activate/dev_manager.c:360 activate/dev_manager.c:384
+#, c-format
+msgid "Number of segments in active LV %s does not match metadata"
+msgstr ""
+
+#: activate/dev_manager.c:394
+#, c-format
+msgid "LV percent: %f"
+msgstr ""
+
+#: activate/dev_manager.c:497
+#, c-format
+msgid "Getting device status percentage for %s"
+msgstr ""
+
+#: activate/dev_manager.c:532
+#, c-format
+msgid "Getting device mirror status percentage for %s"
+msgstr ""
+
+#: activate/dev_manager.c:633
+#, c-format
+msgid "Getting device info for %s [%s]"
+msgstr ""
+
+#: activate/dev_manager.c:635
+#, c-format
+msgid "Failed to get info for %s [%s]."
+msgstr ""
+
+#: activate/dev_manager.c:640
+#, c-format
+msgid "Failed to add device (%u:%u) to dtree"
+msgstr ""
+
+#: activate/dev_manager.c:677
+#, c-format
+msgid "Partial dtree creation failed for %s."
+msgstr ""
+
+#: activate/dev_manager.c:741
+#, c-format
+msgid "Internal error: Unassigned area found in LV %s."
+msgstr ""
+
+#: activate/dev_manager.c:775
+#, c-format
+msgid "Couldn't find snapshot for '%s'."
+msgstr ""
+
+#: activate/dev_manager.c:800
+#, c-format
+msgid "_emit_target: Internal error: Can't handle segment type %s"
+msgstr ""
+
+#: activate/dev_manager.c:828
+#, c-format
+msgid "Checking kernel supports %s segment type for %s%s%s"
+msgstr ""
+
+#: activate/dev_manager.c:834
+#, c-format
+msgid "Can't expand LV %s: %s target support missing from kernel?"
+msgstr ""
+
+#: activate/dev_manager.c:847
+msgid "Clustered snapshots are not yet supported"
+msgstr ""
+
+#: activate/dev_manager.c:902
+#, c-format
+msgid "_add_new_lv_to_dtree: pool alloc failed for %s %s."
+msgstr ""
+
+#: activate/dev_manager.c:961
+#, c-format
+msgid "_create_lv_symlinks: Couldn't split up old device name %s"
+msgstr ""
+
+#: activate/dev_manager.c:987
+#, c-format
+msgid "_clean_tree: Couldn't split up device name %s."
+msgstr ""
+
+#: activate/dev_manager.c:1013 activate/dev_manager.c:1133
+msgid "Lost dependency tree root node"
+msgstr ""
+
+#: activate/dev_manager.c:1055
+#, c-format
+msgid "Failed to create symlinks for %s."
+msgstr ""
+
+#: activate/dev_manager.c:1060
+#, c-format
+msgid "_tree_action: Action %u not supported."
+msgstr ""
+
+#: activate/dev_manager.c:1119
+msgid "partial dtree creation failed"
+msgstr ""
+
+#: activate/dev_manager.c:1124
+#, c-format
+msgid "Failed to add device %s (%u:%u) to dtree"
+msgstr ""
+
+#: activate/fs.c:35 activate/fs.c:58
+msgid "Couldn't construct name of volume group directory."
+msgstr ""
+
+#: activate/fs.c:43
+#, c-format
+msgid "Creating directory %s"
+msgstr ""
+
+#: activate/fs.c:45 activate/fs.c:80 activate/fs.c:100 activate/fs.c:153
+#: activate/fs.c:166 activate/fs.c:173 activate/fs.c:208
+#: commands/toolcontext.c:342 commands/toolcontext.c:820 config/config.c:209
+#: config/config.c:247 config/config.c:262 config/config.c:328
+#: config/config.c:428 config/config.c:452 device/dev-cache.c:208
+#: device/dev-cache.c:212 device/dev-cache.c:394 device/dev-cache.c:417
+#: device/dev-cache.c:424 device/dev-cache.c:681 device/dev-cache.c:683
+#: device/dev-io.c:131 device/dev-io.c:231 device/dev-io.c:249
+#: device/dev-io.c:254 device/dev-io.c:256 device/dev-io.c:262
+#: device/dev-io.c:396 device/dev-io.c:398 device/dev-io.c:481
+#: filters/filter-persistent.c:203 filters/filter-persistent.c:207
+#: filters/filter-persistent.c:230 filters/filter-persistent.c:243
+#: filters/filter-sysfs.c:42 filters/filter-sysfs.c:58
+#: filters/filter-sysfs.c:156 filters/filter-sysfs.c:163
+#: filters/filter-sysfs.c:182 filters/filter-sysfs.c:225 filters/filter.c:164
+#: filters/filter.c:221 filters/filter.c:232 filters/filter.c:240
+#: filters/filter.c:253 format_text/archive.c:214 format_text/archive.c:223
+#: format_text/archive.c:253 format_text/archive.c:260
+#: format_text/archive.c:265 format_text/format-text.c:873
+#: format_text/format-text.c:875 format_text/format-text.c:884
+#: format_text/format-text.c:889 format_text/format-text.c:891
+#: format_text/format-text.c:896 format_text/format-text.c:921
+#: format_text/format-text.c:983 format_text/format-text.c:988
+#: format_text/format-text.c:1013 format_text/format-text.c:1040
+#: locking/file_locking.c:61 locking/file_locking.c:69
+#: locking/file_locking.c:72 locking/file_locking.c:105
+#: locking/file_locking.c:167 locking/file_locking.c:171
+#: locking/file_locking.c:187 locking/file_locking.c:296
+#: locking/file_locking.c:301 locking/locking.c:45 locking/locking.c:50
+#: locking/locking.c:66 locking/locking.c:221 log/log.c:69 lvmcmdline.c:1092
+#: lvmcmdline.c:1130 misc/lvm-exec.c:42 misc/lvm-file.c:47 misc/lvm-file.c:70
+#: misc/lvm-file.c:97 misc/lvm-file.c:107 misc/lvm-file.c:157
+#: misc/lvm-file.c:170 misc/lvm-file.c:199 misc/lvm-file.c:208
+#: misc/lvm-file.c:236 misc/lvm-file.c:241 misc/lvm-file.c:244
+#: misc/lvm-file.c:289 misc/lvm-file.c:297 misc/timestamp.c:47 mm/memlock.c:97
+#: mm/memlock.c:105 mm/memlock.c:116 uuid/uuid.c:83 uuid/uuid.c:88
+#, c-format
+msgid "%s: %s failed: %s"
+msgstr ""
+
+#: activate/fs.c:64
+#, c-format
+msgid "Removing directory %s"
+msgstr ""
+
+#: activate/fs.c:91
+#, c-format
+msgid "Couldn't create path for %s"
+msgstr ""
+
+#: activate/fs.c:98 activate/fs.c:151 activate/fs.c:164
+#, c-format
+msgid "Removing %s"
+msgstr ""
+
+#: activate/fs.c:114
+#, c-format
+msgid "Couldn't create path for volume group dir %s"
+msgstr ""
+
+#: activate/fs.c:121
+#, c-format
+msgid "Couldn't create source pathname for logical volume link %s"
+msgstr ""
+
+#: activate/fs.c:128
+#, c-format
+msgid "Couldn't create destination pathname for logical volume link for %s"
+msgstr ""
+
+#: activate/fs.c:135
+#, c-format
+msgid "Couldn't create pathname for LVM1 group file for %s"
+msgstr ""
+
+#: activate/fs.c:146
+#, c-format
+msgid "Non-LVM1 character device found at %s"
+msgstr ""
+
+#: activate/fs.c:159
+#, c-format
+msgid "Symbolic link %s not created: file exists"
+msgstr ""
+
+#: activate/fs.c:171
+#, c-format
+msgid "Linking %s -> %s"
+msgstr ""
+
+#: activate/fs.c:195
+msgid "Couldn't determine link pathname."
+msgstr ""
+
+#: activate/fs.c:202
+#, c-format
+msgid "%s not symbolic link - not removing"
+msgstr ""
+
+#: activate/fs.c:206
+#, c-format
+msgid "Removing link %s"
+msgstr ""
+
+#: activate/fs.c:282
+msgid "No space to stack fs operation"
+msgstr ""
+
+#: archiver.c:40 format_text/archiver.c:53
+msgid "Couldn't copy archive directory name."
+msgstr ""
+
+#: archiver.c:102 format_text/archiver.c:116
+msgid "Test mode: Skipping archiving of volume group."
+msgstr ""
+
+#: archiver.c:109
+#, c-format
+msgid "Archiving volume group \"%s\" metadata."
+msgstr ""
+
+#: archiver.c:111 format_text/archiver.c:131
+#, c-format
+msgid "Volume group \"%s\" metadata archive failed."
+msgstr ""
+
+#: archiver.c:138 format_text/archiver.c:164
+msgid "Couldn't copy backup directory name."
+msgstr ""
+
+#: archiver.c:169 format_text/archiver.c:195
+msgid "Failed to generate volume group metadata backup filename."
+msgstr ""
+
+#: archiver.c:180 format_text/archiver.c:206
+msgid "WARNING: This metadata update is NOT backed up"
+msgstr ""
+
+#: archiver.c:185 format_text/archiver.c:211
+msgid "Test mode: Skipping volume group backup."
+msgstr ""
+
+#: archiver.c:193 format_text/archiver.c:224
+#, c-format
+msgid "Backup of volume group %s metadata failed."
+msgstr ""
+
+#: archiver.c:207 format_text/archiver.c:238
+msgid "Failed to generate backup filename (for removal)."
+msgstr ""
+
+#: archiver.c:230 format_text/archiver.c:261
+msgid "Couldn't create text format object."
+msgstr ""
+
+#: archiver.c:259 format_text/archiver.c:290
+msgid "Failed to allocate format instance"
+msgstr ""
+
+#: archiver.c:267 format_text/archiver.c:298
+#, c-format
+msgid "PV %s missing from cache"
+msgstr ""
+
+#: archiver.c:272
+#, c-format
+msgid "PV %s is a different format (%s)"
+msgstr ""
+
+#: archiver.c:279 format_text/archiver.c:310
+#, c-format
+msgid "Format-specific setup for %s failed"
+msgstr ""
+
+#: archiver.c:316 format_text/archiver.c:347
+msgid "Failed to generate backup filename (for restore)."
+msgstr ""
+
+#: archiver.c:333
+#, c-format
+msgid "Creating volume group backup \"%s\""
+msgstr ""
+
+#: archiver.c:338 format_text/archiver.c:369
+msgid "Couldn't create backup object."
+msgstr ""
+
+#: cache/lvmcache.c:56 cache/lvmcache.c:235 cache/lvmcache.c:740
+msgid "Internal cache initialisation failed"
+msgstr ""
+
+#: cache/lvmcache.c:61
+#, c-format
+msgid "Cache locking failure for %s"
+msgstr ""
+
+#: cache/lvmcache.c:127
+msgid "device_list element allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:245 toollib.c:638
+msgid "dev_iter creation failed"
+msgstr ""
+
+#: cache/lvmcache.c:278
+msgid "vgids list allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:285 cache/lvmcache.c:308 cache/lvmcache.c:334
+#: toollib.c:271 toollib.c:306 toollib.c:314 toollib.c:326 toollib.c:405
+#: toollib.c:547 toollib.c:561 toollib.c:698
+msgid "strlist allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:301
+msgid "vgnames list allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:324
+msgid "pvids list allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:395
+#, c-format
+msgid "vg hash re-insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:440
+#, c-format
+msgid "_lvmcache_update: pvid insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:456
+#, c-format
+msgid "lvmcache: %s: clearing VGID"
+msgstr ""
+
+#: cache/lvmcache.c:463
+#, c-format
+msgid "_lvmcache_update: vgid hash insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:468
+#, c-format
+msgid "lvmcache: %s: setting %s VGID to %s"
+msgstr ""
+
+#: cache/lvmcache.c:502
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: Existing %s takes precedence over exported %s"
+msgstr ""
+
+#: cache/lvmcache.c:508
+#, c-format
+msgid "WARNING: Duplicate VG name %s: %s takes precedence over exported %s"
+msgstr ""
+
+#: cache/lvmcache.c:516
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: Existing %s (created here) takes precedence "
+"over %s"
+msgstr ""
+
+#: cache/lvmcache.c:521
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: %s (with creation_host) takes precedence over "
+"%s"
+msgstr ""
+
+#: cache/lvmcache.c:529
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: %s (created here) takes precedence over %s"
+msgstr ""
+
+#: cache/lvmcache.c:547
+#, c-format
+msgid "cache_update: vg hash insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:619
+msgid "lvmcache_update_vgname: list alloc failed"
+msgstr ""
+
+#: cache/lvmcache.c:625
+#, c-format
+msgid "cache vgname alloc failed for %s"
+msgstr ""
+
+#: cache/lvmcache.c:652
+#, c-format
+msgid "lvmcache: %s: now %s%s%s%s%s"
+msgstr ""
+
+#: cache/lvmcache.c:668
+#, c-format
+msgid "lvmcache: %s: VG %s %s exported"
+msgstr ""
+
+#: cache/lvmcache.c:685
+#, c-format
+msgid "cache creation host alloc failed for %s"
+msgstr ""
+
+#: cache/lvmcache.c:690
+#, c-format
+msgid "lvmcache: %s: VG %s: Set creation host to %s."
+msgstr ""
+
+#: cache/lvmcache.c:754
+msgid "lvmcache_info allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:769
+#, c-format
+msgid "Ignoring duplicate PV %s on %s - using md %s"
+msgstr ""
+
+#: cache/lvmcache.c:776
+#, c-format
+msgid "Ignoring duplicate PV %s on %s - using dm %s"
+msgstr ""
+
+#: cache/lvmcache.c:783
+#, c-format
+msgid "Duplicate PV %s on %s - using md %s"
+msgstr ""
+
+#: cache/lvmcache.c:789
+#, c-format
+msgid "Duplicate PV %s on %s - using dm %s"
+msgstr ""
+
+#: cache/lvmcache.c:798
+#, c-format
+msgid "Found duplicate PV %s: using %s not %s"
+msgstr ""
+
+#: cache/lvmcache.c:872
+msgid "Wiping internal VG cache"
+msgstr ""
+
+#: commands/toolcontext.c:70
+msgid "LVM_SYSTEM_DIR environment variable is too long."
+msgstr ""
+
+#: commands/toolcontext.c:146
+#, c-format
+msgid "Logging initialised at %s"
+msgstr ""
+
+#: commands/toolcontext.c:165
+#, c-format
+msgid "Set umask to %04o"
+msgstr ""
+
+#: commands/toolcontext.c:171 commands/toolcontext.c:182
+msgid "Device directory given in config file too long"
+msgstr ""
+
+#: commands/toolcontext.c:187
+#, c-format
+msgid "Warning: proc dir %s not found - some checks will be bypassed"
+msgstr ""
+
+#: commands/toolcontext.c:207 lvmcmdline.c:723
+msgid "Invalid units specification"
+msgstr ""
+
+#: commands/toolcontext.c:216
+#, c-format
+msgid "Setting host tag: %s"
+msgstr ""
+
+#: commands/toolcontext.c:219
+#, c-format
+msgid "_set_tag: str_list_add %s failed"
+msgstr ""
+
+#: commands/toolcontext.c:243
+#, c-format
+msgid "Invalid hostname string for tag %s"
+msgstr ""
+
+#: commands/toolcontext.c:254
+msgid "host_filter not supported yet"
+msgstr ""
+
+#: commands/toolcontext.c:289
+#, c-format
+msgid "Invalid tag in config file: %s"
+msgstr ""
+
+#: commands/toolcontext.c:322
+msgid "LVM_SYSTEM_DIR or tag was too long"
+msgstr ""
+
+#: commands/toolcontext.c:327
+msgid "config_tree_list allocation failed"
+msgstr ""
+
+#: commands/toolcontext.c:332
+msgid "config_tree allocation failed"
+msgstr ""
+
+#: commands/toolcontext.c:347
+#, c-format
+msgid "Loading config file: %s"
+msgstr ""
+
+#: commands/toolcontext.c:349
+#, c-format
+msgid "Failed to load config file %s"
+msgstr ""
+
+#: commands/toolcontext.c:372 commands/toolcontext.c:410
+msgid "Failed to create config tree"
+msgstr ""
+
+#: commands/toolcontext.c:473
+msgid "Failed to add /dev to internal device cache"
+msgstr ""
+
+#: commands/toolcontext.c:477
+msgid "device/scan not in config file: Defaulting to /dev"
+msgstr ""
+
+#: commands/toolcontext.c:484
+msgid "Invalid string in config file: devices/scan"
+msgstr ""
+
+#: commands/toolcontext.c:490 format_text/format-text.c:1980
+#, c-format
+msgid "Failed to add %s to internal device cache"
+msgstr ""
+
+#: commands/toolcontext.c:501
+msgid "Invalid string in config file: devices/loopfiles"
+msgstr ""
+
+#: commands/toolcontext.c:507
+#, c-format
+msgid "Failed to add loopfile %s to internal device cache"
+msgstr ""
+
+#: commands/toolcontext.c:546
+msgid "devices/filter not found in config file: no regex filter installed"
+msgstr ""
+
+#: commands/toolcontext.c:550
+msgid "Failed to create regex device filter"
+msgstr ""
+
+#: commands/toolcontext.c:557
+msgid "Failed to create lvm type filter"
+msgstr ""
+
+#: commands/toolcontext.c:602 commands/toolcontext.c:610
+msgid "Persistent cache filename too long."
+msgstr ""
+
+#: commands/toolcontext.c:615
+msgid "Failed to create persistent device filter"
+msgstr ""
+
+#: commands/toolcontext.c:634
+#, c-format
+msgid "Failed to load existing device cache from %s"
+msgstr ""
+
+#: commands/toolcontext.c:679
+msgid "Invalid string in config file: global/format_libraries"
+msgstr ""
+
+#: commands/toolcontext.c:690
+#, c-format
+msgid "Shared library %s does not contain format functions"
+msgstr ""
+
+#: commands/toolcontext.c:722
+#, c-format
+msgid "_init_formats: Default format (%s) not found"
+msgstr ""
+
+#: commands/toolcontext.c:775
+msgid "Invalid string in config file: global/segment_libraries"
+msgstr ""
+
+#: commands/toolcontext.c:786
+#, c-format
+msgid "Shared library %s does not contain segment type functions"
+msgstr ""
+
+#: commands/toolcontext.c:801
+#, c-format
+msgid "Duplicate segment type %s: unloading shared library %s"
+msgstr ""
+
+#: commands/toolcontext.c:825
+msgid "_init_hostname: dm_pool_strdup failed"
+msgstr ""
+
+#: commands/toolcontext.c:830
+msgid "_init_hostname: dm_pool_strdup kernel_vsn failed"
+msgstr ""
+
+#: commands/toolcontext.c:844
+msgid "WARNING: Metadata changes will NOT be backed up"
+msgstr ""
+
+#: commands/toolcontext.c:864
+#, c-format
+msgid "Couldn't create default archive path '%s/%s'."
+msgstr ""
+
+#: commands/toolcontext.c:873 commands/toolcontext.c:893
+msgid "backup_init failed."
+msgstr ""
+
+#: commands/toolcontext.c:885
+#, c-format
+msgid "Couldn't create default backup path '%s/%s'."
+msgstr ""
+
+#: commands/toolcontext.c:911
+msgid "setlocale failed"
+msgstr ""
+
+#: commands/toolcontext.c:920
+msgid "Failed to allocate command context"
+msgstr ""
+
+#: commands/toolcontext.c:940
+msgid ""
+"Failed to create LVM2 system dir for metadata backups, config files and "
+"internal cache."
+msgstr ""
+
+#: commands/toolcontext.c:942
+msgid ""
+"Set environment variable LVM_SYSTEM_DIR to alternative location or empty "
+"string."
+msgstr ""
+
+#: commands/toolcontext.c:948
+msgid "Library memory pool creation failed"
+msgstr ""
+
+#: commands/toolcontext.c:979
+msgid "Command memory pool creation failed"
+msgstr ""
+
+#: commands/toolcontext.c:1042
+msgid "Reloading config files"
+msgstr ""
+
+#: config/config.c:111
+msgid "Failed to allocate config pool."
+msgstr ""
+
+#: config/config.c:116
+msgid "Failed to allocate config tree."
+msgstr ""
+
+#: config/config.c:165
+msgid "Failed to allocate config tree parser."
+msgstr ""
+
+#: config/config.c:228
+#, c-format
+msgid "%s: Checksum error"
+msgstr ""
+
+#: config/config.c:268
+#, c-format
+msgid "%s is not a regular file"
+msgstr ""
+
+#: config/config.c:276
+#, c-format
+msgid "%s is empty"
+msgstr ""
+
+#: config/config.c:324
+#, c-format
+msgid "Config file %s has disappeared!"
+msgstr ""
+
+#: config/config.c:329
+msgid "Failed to reload configuration files"
+msgstr ""
+
+#: config/config.c:334
+#, c-format
+msgid "Configuration file %s is not a regular file"
+msgstr ""
+
+#: config/config.c:344
+#, c-format
+msgid "Detected config file change to %s"
+msgstr ""
+
+#: config/config.c:368
+#, c-format
+msgid "_write_value: Unknown value type: %d"
+msgstr ""
+
+#: config/config.c:432
+#, c-format
+msgid "Dumping configuration to %s"
+msgstr ""
+
+#: config/config.c:435 config/config.c:441
+#, c-format
+msgid "Failure while writing to %s"
+msgstr ""
+
+#: config/config.c:445
+#, c-format
+msgid "Configuration node %s not found"
+msgstr ""
+
+#: config/config.c:494 config/config.c:497 config/config.c:510
+#: config/config.c:512 config/config.c:527 config/config.c:541
+#: config/config.c:543 config/config.c:572 config/config.c:578
+#: config/config.c:590
+#, c-format
+msgid "Parse error at byte %td (line %d): unexpected token"
+msgstr ""
+
+#: config/config.c:594
+#, c-format
+msgid "Parse error at byte %td (line %d): expected a value"
+msgstr ""
+
+#: config/config.c:810
+#, c-format
+msgid "WARNING: Ignoring duplicate config node: %s (seeking %s)"
+msgstr ""
+
+#: config/config.c:858
+#, c-format
+msgid "Setting %s to %s"
+msgstr ""
+
+#: config/config.c:863
+#, c-format
+msgid "%s not found in config: defaulting to %s"
+msgstr ""
+
+#: config/config.c:881
+#, c-format
+msgid "Setting %s to %ld"
+msgstr ""
+
+#: config/config.c:885
+#, c-format
+msgid "%s not found in config: defaulting to %ld"
+msgstr ""
+
+#: config/config.c:903
+#, c-format
+msgid "Setting %s to %f"
+msgstr ""
+
+#: config/config.c:907
+#, c-format
+msgid "%s not found in config: defaulting to %f"
+msgstr ""
+
+#: device/dev-cache.c:64 device/dev-cache.c:81 device/dev-cache.c:118
+msgid "struct device allocation failed"
+msgstr ""
+
+#: device/dev-cache.c:68 device/dev-cache.c:85
+msgid "struct str_list allocation failed"
+msgstr ""
+
+#: device/dev-cache.c:73 device/dev-cache.c:90 device/dev-cache.c:95
+msgid "filename strdup failed"
+msgstr ""
+
+#: device/dev-cache.c:142
+#, c-format
+msgid "%s: New preferred name"
+msgstr ""
+
+#: device/dev-cache.c:247
+#, c-format
+msgid "%s: Already in device cache"
+msgstr ""
+
+#: device/dev-cache.c:260
+#, c-format
+msgid "%s: Aliased to %s in device cache%s"
+msgstr ""
+
+#: device/dev-cache.c:264
+#, c-format
+msgid "%s: Added to device cache"
+msgstr ""
+
+#: device/dev-cache.c:307
+msgid "Couldn't insert device into binary tree."
+msgstr ""
+
+#: device/dev-cache.c:314
+msgid "Couldn't add alias to dev cache."
+msgstr ""
+
+#: device/dev-cache.c:319
+msgid "Couldn't add name to hash in dev cache."
+msgstr ""
+
+#: device/dev-cache.c:399
+#, c-format
+msgid "%s: Not a regular file"
+msgstr ""
+
+#: device/dev-cache.c:429
+#, c-format
+msgid "%s: Symbolic link to directory"
+msgstr ""
+
+#: device/dev-cache.c:438
+#, c-format
+msgid "%s: Not a block device"
+msgstr ""
+
+#: device/dev-cache.c:496
+msgid ""
+"devices/preferred_names not found in config file: using built-in preferences"
+msgstr ""
+
+#: device/dev-cache.c:503
+msgid "preferred_names patterns must be enclosed in quotes"
+msgstr ""
+
+#: device/dev-cache.c:514
+msgid "Failed to allocate preferred device name pattern list."
+msgstr ""
+
+#: device/dev-cache.c:521
+msgid "Failed to allocate a preferred device name pattern."
+msgstr ""
+
+#: device/dev-cache.c:529
+msgid "Preferred device name pattern matcher creation failed."
+msgstr ""
+
+#: device/dev-cache.c:559
+msgid "Couldn't create binary tree for dev-cache."
+msgstr ""
+
+#: device/dev-cache.c:579
+#, c-format
+msgid "Device '%s' has been left open."
+msgstr ""
+
+#: device/dev-cache.c:617 device/dev-cache.c:643
+#, c-format
+msgid "Ignoring %s: %s"
+msgstr ""
+
+#: device/dev-cache.c:623
+#, c-format
+msgid "Ignoring %s: Not a directory"
+msgstr ""
+
+#: device/dev-cache.c:628
+msgid "dir_list allocation failed"
+msgstr ""
+
+#: device/dev-cache.c:649
+#, c-format
+msgid "Ignoring %s: Not a regular file"
+msgstr ""
+
+#: device/dev-cache.c:654
+msgid "dir_list allocation failed for file"
+msgstr ""
+
+#: device/dev-cache.c:686 device/dev-cache.c:690
+#, c-format
+msgid "Path %s no longer valid for device(%d,%d)"
+msgstr ""
+
+#: device/dev-cache.c:707
+#, c-format
+msgid "Aborting - please provide new pathname for what used to be %s"
+msgstr ""
+
+#: device/dev-cache.c:747
+msgid "dev_iter allocation failed"
+msgstr ""
+
+#: device/dev-io.c:67
+#, c-format
+msgid "Attempt to read an unopened device (%s)."
+msgstr ""
+
+#: device/dev-io.c:79
+#, c-format
+msgid "Read size too large: %lu"
+msgstr ""
+
+#: device/dev-io.c:84
+#, c-format
+msgid "%s: lseek %lu failed: %s"
+msgstr ""
+
+#: device/dev-io.c:98
+#, c-format
+msgid "%s: %s failed after %lu of %lu at %lu: %s"
+msgstr ""
+
+#: device/dev-io.c:134
+#, c-format
+msgid "%s: block size is %u bytes"
+msgstr ""
+
+#: device/dev-io.c:191
+msgid "Bounce buffer alloca failed"
+msgstr ""
+
+#: device/dev-io.c:238 device/dev-io.c:264
+#, c-format
+msgid "%s: size is %lu sectors"
+msgstr ""
+
+#: device/dev-io.c:343
+#, c-format
+msgid "WARNING: %s already opened read-only"
+msgstr ""
+
+#: device/dev-io.c:352
+#, c-format
+msgid "WARNING: dev_open(%s) called while suspended"
+msgstr ""
+
+#: device/dev-io.c:364
+#, c-format
+msgid "%s: stat failed: Has device name changed?"
+msgstr ""
+
+#: device/dev-io.c:390
+#, c-format
+msgid "%s: Not using O_DIRECT"
+msgstr ""
+
+#: device/dev-io.c:422
+#, c-format
+msgid "%s: fstat failed: Has device name changed?"
+msgstr ""
+
+#: device/dev-io.c:437
+#, c-format
+msgid "Opened %s %s%s%s"
+msgstr ""
+
+#: device/dev-io.c:486
+#, c-format
+msgid "Closed %s"
+msgstr ""
+
+#: device/dev-io.c:501
+#, c-format
+msgid "Attempt to close device '%s' which is not open."
+msgstr ""
+
+#: device/dev-io.c:515
+#, c-format
+msgid "%s: Immediate close attempt while still referenced"
+msgstr ""
+
+#: device/dev-io.c:576
+#, c-format
+msgid "Read from %s failed"
+msgstr ""
+
+#: device/dev-io.c:588
+#, c-format
+msgid "Circular read from %s failed"
+msgstr ""
+
+#: device/dev-io.c:648
+#, c-format
+msgid "Wiping %s at %lu length %zu"
+msgstr ""
+
+#: device/dev-io.c:651
+#, c-format
+msgid "Wiping %s at sector %lu length %zu sectors"
+msgstr ""
+
+#: display/display.c:145
+#, c-format
+msgid "Unrecognised allocation policy %s"
+msgstr ""
+
+#: display/display.c:172
+msgid "no memory for size display buffer"
+msgstr ""
+
+#: display/display.c:247
+#, c-format
+msgid "%s:%s:%lu:-1:%u:%u:-1:%u:%u:%u:%u:%s"
+msgstr ""
+
+#: display/display.c:278
+#, c-format
+msgid "--- %sPhysical volume ---"
+msgstr ""
+
+#: display/display.c:279
+#, c-format
+msgid "PV Name               %s"
+msgstr ""
+
+#: display/display.c:280
+#, c-format
+msgid "VG Name               %s%s"
+msgstr ""
+
+#: display/display.c:290
+#, c-format
+msgid "PV Size               %s / not usable %s"
+msgstr ""
+
+#: display/display.c:296
+#, c-format
+msgid "PV Size               %s"
+msgstr ""
+
+#: display/display.c:304
+#, c-format
+msgid "Allocatable           yes %s"
+msgstr ""
+
+#: display/display.c:307
+msgid "Allocatable           NO"
+msgstr ""
+
+#: display/display.c:312
+#, c-format
+msgid "PE Size (KByte)       %u"
+msgstr ""
+
+#: display/display.c:313 display/display.c:592
+#, c-format
+msgid "Total PE              %u"
+msgstr ""
+
+#: display/display.c:314
+#, c-format
+msgid "Free PE               %u"
+msgstr ""
+
+#: display/display.c:315
+#, c-format
+msgid "Allocated PE          %u"
+msgstr ""
+
+#: display/display.c:316 display/display.c:339
+#, c-format
+msgid "PV UUID               %s"
+msgstr ""
+
+#: display/display.c:317 display/display.c:345 display/display.c:476
+#: display/display.c:527 display/display.c:610 format_text/archive.c:315
+#: lvmcmdline.c:769 mirror/mirrored.c:73 striped/striped.c:49
+msgid " "
+msgstr ""
+
+#: display/display.c:337
+#, c-format
+msgid "PV Name               %s     "
+msgstr ""
+
+#: display/display.c:340
+#, c-format
+msgid "PV Status             %sallocatable"
+msgstr ""
+
+#: display/display.c:342
+#, c-format
+msgid "Total PE / Free PE    %u / %u"
+msgstr ""
+
+#: display/display.c:355
+#, c-format
+msgid "%s%s/%s:%s:%d:%d:-1:%d:%lu:%d:-1:%d:%d:%d:%d"
+msgstr ""
+
+#: display/display.c:385
+msgid "--- Logical volume ---"
+msgstr ""
+
+#: display/display.c:387
+#, c-format
+msgid "LV Name                %s%s/%s"
+msgstr ""
+
+#: display/display.c:389
+#, c-format
+msgid "VG Name                %s"
+msgstr ""
+
+#: display/display.c:391
+#, c-format
+msgid "LV UUID                %s"
+msgstr ""
+
+#: display/display.c:393
+#, c-format
+msgid "LV Write Access        %s"
+msgstr ""
+
+#: display/display.c:397
+msgid "LV snapshot status     source of"
+msgstr ""
+
+#: display/display.c:406
+#, c-format
+msgid "                       %s%s/%s [%s]"
+msgstr ""
+
+#: display/display.c:419
+#, c-format
+msgid "LV snapshot status     %s destination for %s%s/%s"
+msgstr ""
+
+#: display/display.c:426
+msgid "LV Status              suspended"
+msgstr ""
+
+#: display/display.c:428
+#, c-format
+msgid "LV Status              %savailable"
+msgstr ""
+
+#: display/display.c:436
+#, c-format
+msgid "# open                 %u"
+msgstr ""
+
+#: display/display.c:438
+#, c-format
+msgid "LV Size                %s"
+msgstr ""
+
+#: display/display.c:442
+#, c-format
+msgid "Current LE             %u"
+msgstr ""
+
+#: display/display.c:446
+#, c-format
+msgid "COW-table size         %s"
+msgstr ""
+
+#: display/display.c:448
+#, c-format
+msgid "COW-table LE           %u"
+msgstr ""
+
+#: display/display.c:451
+#, c-format
+msgid "Allocated to snapshot  %.2f%% "
+msgstr ""
+
+#: display/display.c:453
+#, c-format
+msgid "Snapshot chunk size    %s"
+msgstr ""
+
+#: display/display.c:457
+#, c-format
+msgid "Segments               %u"
+msgstr ""
+
+#: display/display.c:463
+#, c-format
+msgid "Allocation             %s"
+msgstr ""
+
+#: display/display.c:464
+#, c-format
+msgid "Read ahead sectors     %u"
+msgstr ""
+
+#: display/display.c:468
+#, c-format
+msgid "Persistent major       %d"
+msgstr ""
+
+#: display/display.c:469
+#, c-format
+msgid "Persistent minor       %d"
+msgstr ""
+
+#: display/display.c:473
+#, c-format
+msgid "Block device           %d:%d"
+msgstr ""
+
+#: display/display.c:486
+#, c-format
+msgid "%sPhysical volume\t%s"
+msgstr ""
+
+#: display/display.c:492
+#, c-format
+msgid "%sPhysical extents\t%d to %d"
+msgstr ""
+
+#: display/display.c:497
+#, c-format
+msgid "%sLogical volume\t%s"
+msgstr ""
+
+#: display/display.c:502
+#, c-format
+msgid "%sLogical extents\t%d to %d"
+msgstr ""
+
+#: display/display.c:507
+#, c-format
+msgid "%sUnassigned area"
+msgstr ""
+
+#: display/display.c:515
+msgid "--- Segments ---"
+msgstr ""
+
+#: display/display.c:518
+#, c-format
+msgid "Logical extent %u to %u:"
+msgstr ""
+
+#: display/display.c:521
+#, c-format
+msgid "  Type\t\t%s"
+msgstr ""
+
+#: display/display.c:547
+msgid "--- Volume group ---"
+msgstr ""
+
+#: display/display.c:548
+#, c-format
+msgid "VG Name               %s"
+msgstr ""
+
+#: display/display.c:549
+#, c-format
+msgid "System ID             %s"
+msgstr ""
+
+#: display/display.c:550
+#, c-format
+msgid "Format                %s"
+msgstr ""
+
+#: display/display.c:552
+#, c-format
+msgid "Metadata Areas        %d"
+msgstr ""
+
+#: display/display.c:554
+#, c-format
+msgid "Metadata Sequence No  %d"
+msgstr ""
+
+#: display/display.c:557
+#, c-format
+msgid "VG Access             %s%s%s%s"
+msgstr ""
+
+#: display/display.c:562
+#, c-format
+msgid "VG Status             %s%sresizable"
+msgstr ""
+
+#: display/display.c:569
+msgid "Clustered             yes"
+msgstr ""
+
+#: display/display.c:570
+#, c-format
+msgid "Shared                %s"
+msgstr ""
+
+#: display/display.c:573
+#, c-format
+msgid "MAX LV                %u"
+msgstr ""
+
+#: display/display.c:574
+#, c-format
+msgid "Cur LV                %u"
+msgstr ""
+
+#: display/display.c:575
+#, c-format
+msgid "Open LV               %u"
+msgstr ""
+
+#: display/display.c:581
+#, c-format
+msgid "Max PV                %u"
+msgstr ""
+
+#: display/display.c:582
+#, c-format
+msgid "Cur PV                %u"
+msgstr ""
+
+#: display/display.c:583
+#, c-format
+msgid "Act PV                %u"
+msgstr ""
+
+#: display/display.c:585
+#, c-format
+msgid "VG Size               %s"
+msgstr ""
+
+#: display/display.c:589
+#, c-format
+msgid "PE Size               %s"
+msgstr ""
+
+#: display/display.c:594
+#, c-format
+msgid "Alloc PE / Size       %u / %s"
+msgstr ""
+
+#: display/display.c:600
+#, c-format
+msgid "Free  PE / Size       %u / %s"
+msgstr ""
+
+#: display/display.c:609
+#, c-format
+msgid "VG UUID               %s"
+msgstr ""
+
+#: display/display.c:645
+#, c-format
+msgid "%s:%s:%d:-1:%u:%u:%u:-1:%u:%u:%u:%lu:%u:%u:%u:%u:%s"
+msgstr ""
+
+#: display/display.c:669
+#, c-format
+msgid "\"%s\" %-9s [%-9s used / %s free]"
+msgstr ""
+
+#: display/display.c:686 display/display.c:695 pvscan.c:34
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: error/errseg.c:73
+msgid "error module string list allocation failed"
+msgstr ""
+
+#: error/errseg.c:109 mirror/mirrored.c:562 snapshot/snapshot.c:179
+#: striped/striped.c:227 zero/zero.c:109
+#, c-format
+msgid "Initialised segtype: %s"
+msgstr ""
+
+#: filters/filter-composite.c:31
+#, c-format
+msgid "Using %s"
+msgstr ""
+
+#: filters/filter-composite.c:59
+msgid "composite filters allocation failed"
+msgstr ""
+
+#: filters/filter-composite.c:67
+msgid "compsoite filters allocation failed"
+msgstr ""
+
+#: filters/filter-md.c:31
+#, c-format
+msgid "%s: Skipping md component device"
+msgstr ""
+
+#: filters/filter-md.c:36
+#, c-format
+msgid "%s: Skipping: error in md component detection"
+msgstr ""
+
+#: filters/filter-md.c:54
+msgid "md filter allocation failed"
+msgstr ""
+
+#: filters/filter-persistent.c:57
+msgid "Wiping cache of LVM-capable devices"
+msgstr ""
+
+#: filters/filter-persistent.c:73
+#, c-format
+msgid "Couldn't find %s array in '%s'"
+msgstr ""
+
+#: filters/filter-persistent.c:84
+msgid "Devices array contains a value which is not a string ... ignoring"
+msgstr ""
+
+#: filters/filter-persistent.c:90
+#, c-format
+msgid "Couldn't add '%s' to filter ... ignoring"
+msgstr ""
+
+#: filters/filter-persistent.c:108
+#, c-format
+msgid "%s: stat failed: %s"
+msgstr ""
+
+#: filters/filter-persistent.c:132
+#, c-format
+msgid "Loaded persistent filter cache from %s"
+msgstr ""
+
+#: filters/filter-persistent.c:183
+#, c-format
+msgid "Internal persistent device cache empty - not writing to %s"
+msgstr ""
+
+#: filters/filter-persistent.c:188
+#, c-format
+msgid "Device cache incomplete - not writing to %s"
+msgstr ""
+
+#: filters/filter-persistent.c:193
+#, c-format
+msgid "Dumping persistent device cache to %s"
+msgstr ""
+
+#: filters/filter-persistent.c:248 format_text/format-text.c:902
+#: format_text/format-text.c:928 format_text/format-text.c:965
+#: misc/lvm-file.c:91
+#, c-format
+msgid "%s: rename to %s failed: %s"
+msgstr ""
+
+#: filters/filter-persistent.c:276
+#, c-format
+msgid "%s: Skipping (cached)"
+msgstr ""
+
+#: filters/filter-persistent.c:311
+msgid "Couldn't create hash table for persistent filter."
+msgstr ""
+
+#: filters/filter-regex.c:44
+msgid "pattern must begin with 'a' or 'r'"
+msgstr ""
+
+#: filters/filter-regex.c:83
+msgid "invalid separator at end of regex"
+msgstr ""
+
+#: filters/filter-regex.c:108
+msgid "filter patterns must be enclosed in quotes"
+msgstr ""
+
+#: filters/filter-regex.c:133
+msgid "invalid filter pattern"
+msgstr ""
+
+#: filters/filter-regex.c:174
+#, c-format
+msgid "%s: Skipping (regex)"
+msgstr ""
+
+#: filters/filter-sysfs.c:31
+msgid "No proc filesystem found: skipping sysfs filter"
+msgstr ""
+
+#: filters/filter-sysfs.c:37
+msgid "Failed to create /proc/mounts string"
+msgstr ""
+
+#: filters/filter-sysfs.c:137
+#, c-format
+msgid "Empty sysfs device file: %s"
+msgstr ""
+
+#: filters/filter-sysfs.c:142
+msgid "sysfs device file not correct format"
+msgstr ""
+
+#: filters/filter-sysfs.c:192
+#, c-format
+msgid "sysfs path name too long: %s in %s"
+msgstr ""
+
+#: filters/filter-sysfs.c:255
+#, c-format
+msgid "%s: Skipping (sysfs)"
+msgstr ""
+
+#: filters/filter-sysfs.c:278
+msgid "sysfs pool creation failed"
+msgstr ""
+
+#: filters/filter-sysfs.c:283
+msgid "sysfs dev_set creation failed"
+msgstr ""
+
+#: filters/filter.c:90
+#, c-format
+msgid "%s: Skipping: Unrecognised LVM device type %lu"
+msgstr ""
+
+#: filters/filter.c:98
+#, c-format
+msgid "%s: Skipping: Suspended dm device"
+msgstr ""
+
+#: filters/filter.c:104
+#, c-format
+msgid "%s: Skipping: open failed"
+msgstr ""
+
+#: filters/filter.c:110
+#, c-format
+msgid "%s: Skipping: dev_get_size failed"
+msgstr ""
+
+#: filters/filter.c:115
+#, c-format
+msgid "%s: Skipping: Too small to hold a PV"
+msgstr ""
+
+#: filters/filter.c:120
+#, c-format
+msgid "%s: Skipping: Partition table signature found"
+msgstr ""
+
+#: filters/filter.c:147
+msgid "No proc filesystem found: using all block device types"
+msgstr ""
+
+#: filters/filter.c:159
+msgid "Failed to create /proc/devices string"
+msgstr ""
+
+#: filters/filter.c:218
+msgid "Expecting string in devices/types in config file"
+msgstr ""
+
+#: filters/filter.c:228
+#, c-format
+msgid "Max partition count missing for %s in devices/types in config file"
+msgstr ""
+
+#: filters/filter.c:236
+#, c-format
+msgid "Zero partition count invalid for %s in devices/types in config file"
+msgstr ""
+
+#: filters/filter.c:269
+msgid "LVM type filter allocation failed"
+msgstr ""
+
+#: format1/disk-rep.c:190
+#, c-format
+msgid "%s does not have a valid LVM1 PV identifier"
+msgstr ""
+
+#: format1/disk-rep.c:196
+#, c-format
+msgid "format1: Unknown metadata version %d found on %s"
+msgstr ""
+
+#: format1/disk-rep.c:210 format_pool/disk_rep.c:43
+#, c-format
+msgid "Failed to read PV data from %s"
+msgstr ""
+
+#: format1/disk-rep.c:367
+#, c-format
+msgid "%s is not a member of any format1 VG"
+msgstr ""
+
+#: format1/disk-rep.c:374
+#, c-format
+msgid "Failed to read VG data from PV (%s)"
+msgstr ""
+
+#: format1/disk-rep.c:380
+#, c-format
+msgid "%s is not a member of the VG %s"
+msgstr ""
+
+#: format1/disk-rep.c:390
+#, c-format
+msgid "Failed to read PV uuid list from %s"
+msgstr ""
+
+#: format1/disk-rep.c:395
+#, c-format
+msgid "Failed to read LV's from %s"
+msgstr ""
+
+#: format1/disk-rep.c:400
+#, c-format
+msgid "Failed to read extents from %s"
+msgstr ""
+
+#: format1/disk-rep.c:404
+#, c-format
+msgid "Found %s in %sVG %s"
+msgstr ""
+
+#: format1/disk-rep.c:443 format_pool/disk_rep.c:67
+#, c-format
+msgid "Ignoring duplicate PV %s on %s"
+msgstr ""
+
+#: format1/disk-rep.c:448 format_pool/disk_rep.c:72
+#, c-format
+msgid "Duplicate PV %s - using md %s"
+msgstr ""
+
+#: format1/disk-rep.c:494
+msgid "read_pvs_in_vg: dev_iter_create failed"
+msgstr ""
+
+#: format1/disk-rep.c:517
+#, c-format
+msgid "Writing %s VG metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:537
+#, c-format
+msgid "Too many uuids to fit on %s"
+msgstr ""
+
+#: format1/disk-rep.c:542
+#, c-format
+msgid "Writing %s uuidlist to %s at %lu len %d"
+msgstr ""
+
+#: format1/disk-rep.c:557
+#, c-format
+msgid "Writing %s LV %s metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:578
+#, c-format
+msgid "Couldn't zero lv area on device '%s'"
+msgstr ""
+
+#: format1/disk-rep.c:586
+#, c-format
+msgid "lv_number %d too large"
+msgstr ""
+
+#: format1/disk-rep.c:603
+#, c-format
+msgid "Writing %s extents metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:623
+msgid "Invalid PV structure size."
+msgstr ""
+
+#: format1/disk-rep.c:632
+msgid "Couldn't allocate temporary PV buffer."
+msgstr ""
+
+#: format1/disk-rep.c:639
+#, c-format
+msgid "Writing %s PV metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:662
+#, c-format
+msgid "Failed to write PV structure onto %s"
+msgstr ""
+
+#: format1/disk-rep.c:681
+#, c-format
+msgid "Failed to write VG data to %s"
+msgstr ""
+
+#: format1/disk-rep.c:686
+#, c-format
+msgid "Failed to write PV uuid list to %s"
+msgstr ""
+
+#: format1/disk-rep.c:691
+#, c-format
+msgid "Failed to write LV's to %s"
+msgstr ""
+
+#: format1/disk-rep.c:696
+#, c-format
+msgid "Failed to write extents to %s"
+msgstr ""
+
+#: format1/disk-rep.c:736
+#, c-format
+msgid "Successfully wrote data to %s"
+msgstr ""
+
+#: format1/format1.c:72
+#, c-format
+msgid "VG data differs between PVs %s and %s"
+msgstr ""
+
+#: format1/format1.c:74 format1/format1.c:89
+#, c-format
+msgid "VG data on %s: %s %s %u %u  %u %u %u %u %u %u %u %u %u %u %u %u %u"
+msgstr ""
+
+#: format1/format1.c:115
+#, c-format
+msgid "%d PV(s) found for VG %s: expected %d"
+msgstr ""
+
+#: format1/format1.c:294 format_pool/format_pool.c:228
+#, c-format
+msgid "Reading physical volume data %s from disk"
+msgstr ""
+
+#: format1/format1.c:335
+#, c-format
+msgid "Physical volumes cannot be bigger than %s"
+msgstr ""
+
+#: format1/format1.c:355
+msgid "Metadata would overwrite physical extents"
+msgstr ""
+
+#: format1/format1.c:370
+#, c-format
+msgid "logical volumes cannot contain more than %d extents."
+msgstr ""
+
+#: format1/format1.c:375
+#, c-format
+msgid "logical volumes cannot be larger than %s"
+msgstr ""
+
+#: format1/format1.c:451
+#, c-format
+msgid "Extent size must be between %s and %s"
+msgstr ""
+
+#: format1/format1.c:459
+#, c-format
+msgid "Extent size must be multiple of %s"
+msgstr ""
+
+#: format1/format1.c:466 format_text/format-text.c:79
+msgid "Extent size must be power of 2"
+msgstr ""
+
+#: format1/format1.c:563
+msgid "Couldn't create lvm1 label handler."
+msgstr ""
+
+#: format1/format1.c:568
+msgid "Couldn't register lvm1 label handler."
+msgstr ""
+
+#: format1/format1.c:572 format_pool/format_pool.c:354
+#: format_text/format-text.c:1994
+#, c-format
+msgid "Initialised format: %s"
+msgstr ""
+
+#: format1/import-export.c:75
+#, c-format
+msgid "System ID %s on %s differs from %s for volume group"
+msgstr ""
+
+#: format1/import-export.c:98 format_text/import_vsn1.c:220
+#: metadata/metadata.c:569 metadata/metadata.c:1542 pvresize.c:121
+#: vgreduce.c:395 vgremove.c:62
+#, c-format
+msgid "%s: Couldn't get size."
+msgstr ""
+
+#: format1/import-export.c:101 format_text/import_vsn1.c:223
+#, c-format
+msgid "Fixing up missing format1 size (%s) for PV %s"
+msgstr ""
+
+#: format1/import-export.c:108 format_text/import_vsn1.c:230
+#, c-format
+msgid "WARNING: Physical Volume %s is too large for underlying device"
+msgstr ""
+
+#: format1/import-export.c:130
+msgid "Generated system_id too long"
+msgstr ""
+
+#: format1/import-export.c:174
+#, c-format
+msgid "Volume group name %s too long to export"
+msgstr ""
+
+#: format1/import-export.c:412
+#, c-format
+msgid "Segment type %s in LV %s: unsupported by format1"
+msgstr ""
+
+#: format1/import-export.c:418
+#, c-format
+msgid "Non-PV stripe found in LV %s: unsupported by format1"
+msgstr ""
+
+#: format1/import-export.c:610
+msgid "Logical volume number out of bounds."
+msgstr ""
+
+#: format1/import-export.c:617
+#, c-format
+msgid "Couldn't find logical volume '%s'."
+msgstr ""
+
+#: format1/import-export.c:637
+#, c-format
+msgid "Couldn't find origin logical volume for snapshot '%s'."
+msgstr ""
+
+#: format1/import-export.c:650
+msgid "Couldn't add snapshot."
+msgstr ""
+
+#: format1/import-extents.c:53
+msgid "Unable to create hash table for holding extent maps."
+msgstr ""
+
+#: format1/import-extents.c:92
+#, c-format
+msgid "Physical volume (%s) contains an unknown logical volume (%s)."
+msgstr ""
+
+#: format1/import-extents.c:137
+#, c-format
+msgid "Invalid LV in extent map (PV %s, PE %u, LV %u, LE %u)"
+msgstr ""
+
+#: format1/import-extents.c:149
+msgid "logical extent number out of bounds"
+msgstr ""
+
+#: format1/import-extents.c:155
+#, c-format
+msgid "logical extent (%u) already mapped."
+msgstr ""
+
+#: format1/import-extents.c:175
+#, c-format
+msgid "Logical volume (%s) contains an incomplete mapping table."
+msgstr ""
+
+#: format1/import-extents.c:229
+msgid "Failed to allocate linear segment."
+msgstr ""
+
+#: format1/import-extents.c:276
+#, c-format
+msgid ""
+"Number of stripes (%u) incompatible with logical extent count (%u) for %s"
+msgstr ""
+
+#: format1/import-extents.c:303
+msgid "Failed to allocate striped segment."
+msgstr ""
+
+#: format1/import-extents.c:359
+msgid "Couldn't allocate logical volume maps."
+msgstr ""
+
+#: format1/import-extents.c:364
+msgid "Couldn't fill logical volume maps."
+msgstr ""
+
+#: format1/import-extents.c:374
+msgid "Couldn't build extent segments."
+msgstr ""
+
+#: format1/layout.c:79
+#, c-format
+msgid "MaxLogicalVolumes of %d exceeds format limit of %d for VG '%s'"
+msgstr ""
+
+#: format1/layout.c:86
+#, c-format
+msgid "MaxPhysicalVolumes of %d exceeds format limit of %d for VG '%s'"
+msgstr ""
+
+#: format1/layout.c:105
+msgid "Insufficient space for metadata and PE's."
+msgstr ""
+
+#: format1/layout.c:141
+#, c-format
+msgid "Too few extents on %s.  Try smaller extent size."
+msgstr ""
+
+#: format1/layout.c:162
+#, c-format
+msgid "Metadata extent limit (%u) exceeded for %s - %u required"
+msgstr ""
+
+#: format1/lvm1-label.c:29
+#, c-format
+msgid "The '%s' operation is not supported for the lvm1 labeller."
+msgstr ""
+
+#: format1/lvm1-label.c:120 format_pool/pool_label.c:99
+#: format_text/text_label.c:285
+msgid "Couldn't allocate labeller object."
+msgstr ""
+
+#: format_pool/disk_rep.c:94 format_pool/disk_rep.c:98
+#, c-format
+msgid "Calculated uuid %s for %s"
+msgstr ""
+
+#: format_pool/disk_rep.c:274
+#, c-format
+msgid "Unable to allocate %d 32-bit uints"
+msgstr ""
+
+#: format_pool/disk_rep.c:341
+#, c-format
+msgid "No devices for vg %s found in cache"
+msgstr ""
+
+#: format_pool/disk_rep.c:363
+msgid "Unable to allocate pool list structure"
+msgstr ""
+
+#: format_pool/format_pool.c:44
+#, c-format
+msgid "Unable to allocate %d subpool structures"
+msgstr ""
+
+#: format_pool/format_pool.c:64
+#, c-format
+msgid "Unable to allocate %d pool_device structures"
+msgstr ""
+
+#: format_pool/format_pool.c:87
+#, c-format
+msgid "Missing subpool %d in pool %s"
+msgstr ""
+
+#: format_pool/format_pool.c:92
+#, c-format
+msgid "Missing device %u for subpool %d in pool %s"
+msgstr ""
+
+#: format_pool/format_pool.c:113
+msgid "Unable to allocate volume group structure"
+msgstr ""
+
+#: format_pool/format_pool.c:279
+msgid "Unable to allocate format instance structure for pool format"
+msgstr ""
+
+#: format_pool/format_pool.c:289
+msgid "Unable to allocate metadata area structure for pool format"
+msgstr ""
+
+#: format_pool/format_pool.c:332
+msgid "Unable to allocate format type structure for pool format"
+msgstr ""
+
+#: format_pool/format_pool.c:345
+msgid "Couldn't create pool label handler."
+msgstr ""
+
+#: format_pool/format_pool.c:350
+msgid "Couldn't register pool label handler."
+msgstr ""
+
+#: format_pool/import_export.c:64
+msgid "Unable to allocate lv list structure"
+msgstr ""
+
+#: format_pool/import_export.c:69
+msgid "Unable to allocate logical volume structure"
+msgstr ""
+
+#: format_pool/import_export.c:98
+#, c-format
+msgid "Calculated lv uuid for lv %s: %s"
+msgstr ""
+
+#: format_pool/import_export.c:133
+msgid "Unable to allocate pv list structure"
+msgstr ""
+
+#: format_pool/import_export.c:137
+msgid "Unable to allocate pv structure"
+msgstr ""
+
+#: format_pool/import_export.c:165
+msgid "Unable to duplicate vg_name string"
+msgstr ""
+
+#: format_pool/import_export.c:195
+#, c-format
+msgid "Found sptype %X and converted it to %s"
+msgstr ""
+
+#: format_pool/import_export.c:210
+msgid "Stripe size must be a power of 2"
+msgstr ""
+
+#: format_pool/import_export.c:226
+msgid "Unable to allocate striped lv_segment structure"
+msgstr ""
+
+#: format_pool/import_export.c:267
+msgid "Unable to allocate linear lv_segment structure"
+msgstr ""
+
+#: format_pool/pool_label.c:28
+#, c-format
+msgid "The '%s' operation is not supported for the pool labeller."
+msgstr ""
+
+#: format_text/archive.c:146
+#, c-format
+msgid "Couldn't scan the archive directory (%s)."
+msgstr ""
+
+#: format_text/archive.c:173
+msgid "Couldn't create new archive file."
+msgstr ""
+
+#: format_text/archive.c:221
+#, c-format
+msgid "Expiring archive %s"
+msgstr ""
+
+#: format_text/archive.c:246
+msgid "Couldn't create temporary archive name."
+msgstr ""
+
+#: format_text/archive.c:251
+msgid "Couldn't create FILE object for archive."
+msgstr ""
+
+#: format_text/archive.c:288
+msgid "Archive file name too long."
+msgstr ""
+
+#: format_text/archive.c:299
+#, c-format
+msgid "Archive rename failed for %s"
+msgstr ""
+
+#: format_text/archive.c:316
+#, c-format
+msgid "File:\t\t%s"
+msgstr ""
+
+#: format_text/archive.c:321
+msgid "Couldn't create text instance object."
+msgstr ""
+
+#: format_text/archive.c:331
+msgid "Unable to read archive file."
+msgstr ""
+
+#: format_text/archive.c:336
+#, c-format
+msgid "VG name:    \t%s"
+msgstr ""
+
+#: format_text/archive.c:337
+#, c-format
+msgid "Description:\t%s"
+msgstr ""
+
+#: format_text/archive.c:338
+#, c-format
+msgid "Backup Time:\t%s"
+msgstr ""
+
+#: format_text/archive.c:355
+#, c-format
+msgid "No archives found in %s."
+msgstr ""
+
+#: format_text/archiver.c:43 format_text/archiver.c:155
+msgid "archive_params alloc failed"
+msgstr ""
+
+#: format_text/archiver.c:128
+#, c-format
+msgid "Archiving volume group \"%s\" metadata (seqno %u)."
+msgstr ""
+
+#: format_text/archiver.c:303
+#, c-format
+msgid "PV %s is a different format (seqno %s)"
+msgstr ""
+
+#: format_text/archiver.c:364
+#, c-format
+msgid "Creating volume group backup \"%s\" (seqno %u)."
+msgstr ""
+
+#: format_text/archiver.c:402
+msgid "Failed to generate backup filename."
+msgstr ""
+
+#: format_text/export.c:80
+#, c-format
+msgid "uname failed: %s"
+msgstr ""
+
+#: format_text/export.c:101
+msgid "Internal error tracking indentation"
+msgstr ""
+
+#: format_text/export.c:120
+#, c-format
+msgid "Doubling metadata output buffer to %u"
+msgstr ""
+
+#: format_text/export.c:124
+msgid "Buffer reallocation failed."
+msgstr ""
+
+#: format_text/export.c:737
+msgid "text_export buffer allocation failed"
+msgstr ""
+
+#: format_text/flags.c:79
+msgid "Unknown flag set requested."
+msgstr ""
+
+#: format_text/flags.c:125
+msgid "Metadata inconsistency: Not all flags successfully exported."
+msgstr ""
+
+#: format_text/flags.c:147
+msgid "Status value is not a string."
+msgstr ""
+
+#: format_text/flags.c:158
+#, c-format
+msgid "Unknown status flag '%s'."
+msgstr ""
+
+#: format_text/format-text.c:152
+#, c-format
+msgid "Found text metadata area, offset=%lu, size=%lu"
+msgstr ""
+
+#: format_text/format-text.c:207
+#, c-format
+msgid ""
+"Found LVM2 metadata record at offset=%lu, size=%lu, offset2=%lu size2=%lu"
+msgstr ""
+
+#: format_text/format-text.c:259
+#, c-format
+msgid "Random lvid creation failed for %s/%s."
+msgstr ""
+
+#: format_text/format-text.c:290
+msgid "struct mda_header allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:302
+msgid "Incorrect metadata area header checksum"
+msgstr ""
+
+#: format_text/format-text.c:309
+msgid "Wrong magic number in metadata area header"
+msgstr ""
+
+#: format_text/format-text.c:314
+#, c-format
+msgid "Incompatible metadata area header version: %d"
+msgstr ""
+
+#: format_text/format-text.c:320
+#, c-format
+msgid "Incorrect start sector in metadata area header: %lu"
+msgstr ""
+
+#: format_text/format-text.c:461
+#, c-format
+msgid "VG %s not found on %s"
+msgstr ""
+
+#: format_text/format-text.c:469 format_text/format-text.c:574
+#, c-format
+msgid "VG %s metadata too large for circular buffer"
+msgstr ""
+
+#: format_text/format-text.c:484
+#, c-format
+msgid "Read %s %smetadata (%u) from %s at %lu size %lu"
+msgstr ""
+
+#: format_text/format-text.c:557
+#, c-format
+msgid "VG %s metadata writing failed"
+msgstr ""
+
+#: format_text/format-text.c:579
+#, c-format
+msgid "Writing %s metadata to %s at %lu len %lu"
+msgstr ""
+
+#: format_text/format-text.c:592
+#, c-format
+msgid "Writing metadata to %s at %lu len %u"
+msgstr ""
+
+#: format_text/format-text.c:681
+#, c-format
+msgid "%sCommitting %s metadata (%u) to %s header at %lu"
+msgstr ""
+
+#: format_text/format-text.c:685
+#, c-format
+msgid "Wiping pre-committed %s metadata from %s header at %lu"
+msgstr ""
+
+#: format_text/format-text.c:691 format_text/format-text.c:777
+msgid "Failed to write metadata area header"
+msgstr ""
+
+#: format_text/format-text.c:810
+#, c-format
+msgid "'%s' does not contain volume group '%s'."
+msgstr ""
+
+#: format_text/format-text.c:814
+#, c-format
+msgid "Read volume group %s from %s"
+msgstr ""
+
+#: format_text/format-text.c:863
+msgid "Text format failed to determine directory."
+msgstr ""
+
+#: format_text/format-text.c:868
+msgid "Couldn't create temporary text file name."
+msgstr ""
+
+#: format_text/format-text.c:879
+#, c-format
+msgid "Writing %s metadata to %s"
+msgstr ""
+
+#: format_text/format-text.c:882
+#, c-format
+msgid "Failed to write metadata to %s."
+msgstr ""
+
+#: format_text/format-text.c:901 format_text/format-text.c:926
+#: format_text/format-text.c:960
+#, c-format
+msgid "Renaming %s to %s"
+msgstr ""
+
+#: format_text/format-text.c:917
+#, c-format
+msgid "Test mode: Skipping committing %s metadata (%u)"
+msgstr ""
+
+#: format_text/format-text.c:920
+#, c-format
+msgid "Unlinking %s"
+msgstr ""
+
+#: format_text/format-text.c:925
+#, c-format
+msgid "Committing %s metadata (%u)"
+msgstr ""
+
+#: format_text/format-text.c:962
+msgid "Test mode: Skipping rename"
+msgstr ""
+
+#: format_text/format-text.c:1025 format_text/format-text.c:1723
+#, c-format
+msgid "Name too long %s/%s"
+msgstr ""
+
+#: format_text/format-text.c:1089
+#, c-format
+msgid "%s: metadata too large for circular buffer"
+msgstr ""
+
+#: format_text/format-text.c:1118
+#, c-format
+msgid "%s: Found metadata at %lu size %lu for %s (%s)"
+msgstr ""
+
+#: format_text/format-text.c:1186
+#, c-format
+msgid "Physical extents end beyond end of device %s!"
+msgstr ""
+
+#: format_text/format-text.c:1207
+#, c-format
+msgid "Warning: metadata area fills disk leaving no space for data on %s."
+msgstr ""
+
+#: format_text/format-text.c:1238 format_text/format-text.c:1283
+msgid "Failed to wipe new metadata area"
+msgstr ""
+
+#: format_text/format-text.c:1329
+#, c-format
+msgid "Creating metadata area on %s at sector %lu size %lu sectors"
+msgstr ""
+
+#: format_text/format-text.c:1410
+msgid "_add_raw allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:1470
+#, c-format
+msgid "Must be exactly one data area (found %d) on PV %s"
+msgstr ""
+
+#: format_text/format-text.c:1485 format_text/format-text.c:1489
+msgid "metadata_area allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:1650
+#, c-format
+msgid "PV %s too large for extent size %s."
+msgstr ""
+
+#: format_text/format-text.c:1693
+msgid "Couldn't allocate format instance object."
+msgstr ""
+
+#: format_text/format-text.c:1699
+msgid "Couldn't allocate text_fid_context."
+msgstr ""
+
+#: format_text/format-text.c:1807
+#, c-format
+msgid "%s: Volume group filename may not end in .tmp"
+msgstr ""
+
+#: format_text/format-text.c:1841
+msgid "Couldn't allocate text format context object."
+msgstr ""
+
+#: format_text/format-text.c:1863
+msgid "_add_dir allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:1866
+#, c-format
+msgid "Adding text format metadata dir: %s"
+msgstr ""
+
+#: format_text/format-text.c:1883
+msgid "Empty metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1888
+msgid "Missing start_sector in metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1895
+msgid "Missing size in metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1902
+msgid "Missing uuid in metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1908
+#, c-format
+msgid "Invalid uuid in metadata disk_area section of config file: %s"
+msgstr ""
+
+#: format_text/format-text.c:1917 format_text/import_vsn1.c:155
+msgid "Couldn't find device."
+msgstr ""
+
+#: format_text/format-text.c:1919 format_text/import_vsn1.c:157
+#, c-format
+msgid "Couldn't find device with uuid '%s'."
+msgstr ""
+
+#: format_text/format-text.c:1948
+msgid "Failed to allocate dir_list"
+msgstr ""
+
+#: format_text/format-text.c:1960
+msgid "Couldn't create text label handler."
+msgstr ""
+
+#: format_text/format-text.c:1966
+msgid "Couldn't register text label handler."
+msgstr ""
+
+#: format_text/format-text.c:1974
+msgid "Invalid string in config file: metadata/dirs"
+msgstr ""
+
+#: format_text/import.c:103
+msgid "Couldn't read volume group metadata."
+msgstr ""
+
+#: format_text/import_vsn1.c:46
+#, c-format
+msgid "Can't process text format file - %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:94
+msgid "Couldn't find uuid."
+msgstr ""
+
+#: format_text/import_vsn1.c:100
+msgid "uuid must be a string."
+msgstr ""
+
+#: format_text/import_vsn1.c:105
+msgid "Invalid uuid."
+msgstr ""
+
+#: format_text/import_vsn1.c:139
+msgid "Empty pv section."
+msgstr ""
+
+#: format_text/import_vsn1.c:144
+msgid "Couldn't read uuid for volume group."
+msgstr ""
+
+#: format_text/import_vsn1.c:174
+msgid "Couldn't find status flags for physical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:179
+msgid "Couldn't read status flags for physical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:187
+msgid "Couldn't read extent size for volume group."
+msgstr ""
+
+#: format_text/import_vsn1.c:192
+msgid "Couldn't find extent count (pe_count) for physical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:203
+#, c-format
+msgid "Couldn't read tags for physical volume %s in %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:275
+msgid "Empty segment section."
+msgstr ""
+
+#: format_text/import_vsn1.c:280
+#, c-format
+msgid "Couldn't read 'start_extent' for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:286
+#, c-format
+msgid "Couldn't read 'extent_count' for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:296
+msgid "Segment type must be a string."
+msgstr ""
+
+#: format_text/import_vsn1.c:316
+msgid "Segment allocation failed"
+msgstr ""
+
+#: format_text/import_vsn1.c:329
+#, c-format
+msgid "Couldn't read tags for a segment of %s/%s."
+msgstr ""
+
+#: format_text/import_vsn1.c:358
+#, c-format
+msgid "Zero areas not allowed for segment '%s'"
+msgstr ""
+
+#: format_text/import_vsn1.c:394
+#, c-format
+msgid "Couldn't find volume '%s' for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:407
+#, c-format
+msgid "Incorrect number of areas in area array for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:437
+msgid "Only one segment permitted for snapshot"
+msgstr ""
+
+#: format_text/import_vsn1.c:443
+msgid "Couldn't read segment count for logical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:448
+msgid "segment_count and actual number of segments disagree."
+msgstr ""
+
+#: format_text/import_vsn1.c:494 format_text/import_vsn1.c:562
+msgid "Empty logical volume section."
+msgstr ""
+
+#: format_text/import_vsn1.c:499
+msgid "Couldn't find status flags for logical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:504
+msgid "Couldn't read status flags for logical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:512 format_text/import_vsn1.c:729
+msgid "allocation_policy must be a string."
+msgstr ""
+
+#: format_text/import_vsn1.c:535
+#, c-format
+msgid "Couldn't read tags for logical volume %s/%s."
+msgstr ""
+
+#: format_text/import_vsn1.c:555
+#, c-format
+msgid "Lost logical volume reference %s"
+msgstr ""
+
+#: format_text/import_vsn1.c:568
+#, c-format
+msgid "Couldn't read uuid for logical volume %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:595
+#, c-format
+msgid "Couldn't read minor number for logical volume %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:603
+#, c-format
+msgid "Couldn't read major number for logical volume %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:620
+#, c-format
+msgid "Couldn't find section '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:649 format_text/import_vsn1.c:841
+msgid "Couldn't find volume group in file."
+msgstr ""
+
+#: format_text/import_vsn1.c:673
+msgid "system_id must be a string"
+msgstr ""
+
+#: format_text/import_vsn1.c:680 format_text/import_vsn1.c:851
+#, c-format
+msgid "Couldn't read uuid for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:685
+#, c-format
+msgid "Couldn't read 'seqno' for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:691 format_text/import_vsn1.c:856
+#, c-format
+msgid "Couldn't find status flags for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:697 format_text/import_vsn1.c:862
+#, c-format
+msgid "Couldn't read status flags for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:703
+#, c-format
+msgid "Couldn't read extent size for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:714
+#, c-format
+msgid "Couldn't read 'max_lv' for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:720
+#, c-format
+msgid "Couldn't read 'max_pv' for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:745
+msgid "Couldn't create hash table."
+msgstr ""
+
+#: format_text/import_vsn1.c:752
+#, c-format
+msgid "Couldn't find all physical volumes for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:763
+#, c-format
+msgid "Couldn't read tags for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:769
+#, c-format
+msgid "Couldn't read all logical volume names for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:776
+#, c-format
+msgid "Couldn't read all logical volumes for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:782
+#, c-format
+msgid "Failed to fixup mirror pointers after import for volume group %s."
+msgstr ""
+
+#: format_text/tags.c:62
+msgid "Found a tag that is not a string"
+msgstr ""
+
+#: format_text/text_label.c:98 format_text/text_label.c:103
+msgid "struct data_area_list allocation failed"
+msgstr ""
+
+#: format_text/text_label.c:138 format_text/text_label.c:149
+msgid "struct mda_list allocation failed"
+msgstr ""
+
+#: format_text/text_label.c:143 format_text/text_label.c:154
+msgid "struct mda_context allocation failed"
+msgstr ""
+
+#: label/label.c:49
+msgid "Couldn't allocate memory for labeller list object."
+msgstr ""
+
+#: label/label.c:123 label/label.c:218
+#, c-format
+msgid "%s: Failed to read label area"
+msgstr ""
+
+#: label/label.c:135 label/label.c:164
+#, c-format
+msgid "Ignoring additional label on %s at sector %lu"
+msgstr ""
+
+#: label/label.c:140
+#, c-format
+msgid "%s: Label for sector %lu found at sector %lu - ignoring"
+msgstr ""
+
+#: label/label.c:150
+#, c-format
+msgid "Label checksum incorrect on %s - ignoring"
+msgstr ""
+
+#: label/label.c:161
+#, c-format
+msgid "%s: %s label detected"
+msgstr ""
+
+#: label/label.c:185
+#, c-format
+msgid "%s: No label detected"
+msgstr ""
+
+#: label/label.c:204
+#, c-format
+msgid "Scanning for labels to wipe from %s"
+msgstr ""
+
+#: label/label.c:244
+#, c-format
+msgid "%s: Wiping label at sector %lu"
+msgstr ""
+
+#: label/label.c:248
+#, c-format
+msgid "Failed to remove label from %s at sector %lu"
+msgstr ""
+
+#: label/label.c:304
+msgid "Label handler does not support label writes"
+msgstr ""
+
+#: label/label.c:309
+#, c-format
+msgid "Label sector %lu beyond range (%ld)"
+msgstr ""
+
+#: label/label.c:333
+#, c-format
+msgid "%s: Writing label to sector %lu"
+msgstr ""
+
+#: label/label.c:336
+#, c-format
+msgid "Failed to write label to %s"
+msgstr ""
+
+#: label/label.c:386
+msgid "label allocaction failed"
+msgstr ""
+
+#: locking/cluster_locking.c:69
+#, c-format
+msgid "Local socket creation failed: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:82
+#, c-format
+msgid "connect() failed on local socket: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:109
+#, c-format
+msgid "Error writing data to clvmd: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:118
+#, c-format
+msgid "Error reading data from clvmd: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:123
+msgid "EOF reading CLVMD"
+msgstr ""
+
+#: locking/cluster_locking.c:156
+#, c-format
+msgid "cluster request failed: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:346
+#, c-format
+msgid "clvmd not running on node %s"
+msgstr ""
+
+#: locking/cluster_locking.c:351
+#, c-format
+msgid "Error locking on node %s: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:402 locking/file_locking.c:266
+#: locking/locking.c:265 locking/no_locking.c:71
+#, c-format
+msgid "Unrecognised lock scope: %d"
+msgstr ""
+
+#: locking/cluster_locking.c:408
+#, c-format
+msgid "Locking %s at 0x%x"
+msgstr ""
+
+#: locking/external_locking.c:64
+msgid "External locking already initialised"
+msgstr ""
+
+#: locking/external_locking.c:86
+#, c-format
+msgid "Shared library %s does not contain locking functions"
+msgstr ""
+
+#: locking/external_locking.c:93
+#, c-format
+msgid "Loaded external locking library %s"
+msgstr ""
+
+#: locking/file_locking.c:59
+#, c-format
+msgid "Unlocking %s"
+msgstr ""
+
+#: locking/file_locking.c:111
+msgid "CTRL-c detected: giving up waiting for lock"
+msgstr ""
+
+#: locking/file_locking.c:149
+#, c-format
+msgid "Unrecognised lock type: %d"
+msgstr ""
+
+#: locking/file_locking.c:163
+#, c-format
+msgid "Locking %s %c%c"
+msgstr ""
+
+#: locking/file_locking.c:237
+#, c-format
+msgid "Unlocking LV %s"
+msgstr ""
+
+#: locking/file_locking.c:242
+#, c-format
+msgid "Locking LV %s (NL)"
+msgstr ""
+
+#: locking/file_locking.c:247
+#, c-format
+msgid "Locking LV %s (R)"
+msgstr ""
+
+#: locking/file_locking.c:252
+#, c-format
+msgid "Locking LV %s (W)"
+msgstr ""
+
+#: locking/file_locking.c:257
+#, c-format
+msgid "Locking LV %s (EX)"
+msgstr ""
+
+#: locking/locking.c:133
+msgid ""
+"WARNING: Locking disabled. Be careful! This could corrupt your metadata."
+msgstr ""
+
+#: locking/locking.c:138
+msgid "File-based locking selected."
+msgstr ""
+
+#: locking/locking.c:146
+msgid "External locking selected."
+msgstr ""
+
+#: locking/locking.c:156
+msgid "Falling back to internal clustered locking."
+msgstr ""
+
+#: locking/locking.c:160
+msgid "Cluster locking selected."
+msgstr ""
+
+#: locking/locking.c:167
+msgid "Unknown locking type requested."
+msgstr ""
+
+#: locking/locking.c:174
+msgid "WARNING: Falling back to local file-based locking."
+msgstr ""
+
+#: locking/locking.c:175
+msgid "Volume Groups with the clustered attribute will be inaccessible."
+msgstr ""
+
+#: locking/locking.c:185
+msgid "Locking disabled - only read operations permitted."
+msgstr ""
+
+#: locking/locking.c:212
+#, c-format
+msgid "LVM1 proc VG pathname too long for %s"
+msgstr ""
+
+#: locking/locking.c:217
+#, c-format
+msgid "%s exists: Is the original LVM driver using this volume group?"
+msgstr ""
+
+#: locking/locking.c:302 lvresize.c:573
+#, c-format
+msgid "Failed to suspend %s"
+msgstr ""
+
+#: locking/locking.c:323
+#, c-format
+msgid "Failed to activate %s"
+msgstr ""
+
+#: log/log.c:145
+msgid "Test mode: Metadata will NOT be updated."
+msgstr ""
+
+#: lvchange.c:27
+#, c-format
+msgid "Logical volume \"%s\" is already writable"
+msgstr ""
+
+#: lvchange.c:33
+#, c-format
+msgid "Logical volume \"%s\" is already read only"
+msgstr ""
+
+#: lvchange.c:40
+#, c-format
+msgid "Cannot change permissions of mirror \"%s\" while active."
+msgstr ""
+
+#: lvchange.c:47
+#, c-format
+msgid "Setting logical volume \"%s\" read/write"
+msgstr ""
+
+#: lvchange.c:51
+#, c-format
+msgid "Setting logical volume \"%s\" read-only"
+msgstr ""
+
+#: lvchange.c:55 lvchange.c:314 lvchange.c:350 lvchange.c:393 lvchange.c:470
+#: lvchange.c:524 lvconvert.c:401
+#, c-format
+msgid "Updating logical volume \"%s\" on disk(s)"
+msgstr ""
+
+#: lvchange.c:64 lvchange.c:402 lvconvert.c:409 metadata/mirror.c:227
+#, c-format
+msgid "Failed to lock %s"
+msgstr ""
+
+#: lvchange.c:74 lvchange.c:412
+#, c-format
+msgid "Updating permissions for \"%s\" in kernel"
+msgstr ""
+
+#: lvchange.c:76 lvchange.c:414 lvconvert.c:422 lvresize.c:585
+#: metadata/mirror.c:240
+#, c-format
+msgid "Problem reactivating %s"
+msgstr ""
+
+#: lvchange.c:89
+#, c-format
+msgid "Logical volume, %s, is not active"
+msgstr ""
+
+#: lvchange.c:113
+#, c-format
+msgid "Deactivating logical volume \"%s\" locally"
+msgstr ""
+
+#: lvchange.c:120
+#, c-format
+msgid "Deactivating logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:127
+#, c-format
+msgid "Locking failed: ignoring clustered logical volume %s"
+msgstr ""
+
+#: lvchange.c:133
+#, c-format
+msgid "Activating logical volume \"%s\" exclusively"
+msgstr ""
+
+#: lvchange.c:140
+#, c-format
+msgid "Activating logical volume \"%s\" locally"
+msgstr ""
+
+#: lvchange.c:147
+#, c-format
+msgid "Activating logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:157
+#, c-format
+msgid "Spawning background pvmove process for %s"
+msgstr ""
+
+#: lvchange.c:168
+#, c-format
+msgid "Refreshing logical volume \"%s\" (if active)"
+msgstr ""
+
+#: lvchange.c:183
+#, c-format
+msgid "Unable to resync %s because it is not mirrored."
+msgstr ""
+
+#: lvchange.c:189
+#, c-format
+msgid "Unable to resync pvmove volume %s"
+msgstr ""
+
+#: lvchange.c:194
+#, c-format
+msgid "Unable to resync locked volume %s"
+msgstr ""
+
+#: lvchange.c:200
+#, c-format
+msgid "Can't resync open logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:210
+#, c-format
+msgid "Logical volume \"%s\" not resynced"
+msgstr ""
+
+#: lvchange.c:220
+#, c-format
+msgid "Can't get exclusive access to clustered volume %s"
+msgstr ""
+
+#: lvchange.c:226
+#, c-format
+msgid "Unable to deactivate %s for resync"
+msgstr ""
+
+#: lvchange.c:232
+#, c-format
+msgid "Starting resync of %s%s%s mirror \"%s\""
+msgstr ""
+
+#: lvchange.c:246
+#, c-format
+msgid "Failed to reactivate %s to resynchronize mirror"
+msgstr ""
+
+#: lvchange.c:262
+msgid "Failed to write intermediate VG metadata."
+msgstr ""
+
+#: lvchange.c:276
+msgid "Failed to commit intermediate VG metadata."
+msgstr ""
+
+#: lvchange.c:288
+#, c-format
+msgid "Unable to activate %s for mirror log resync"
+msgstr ""
+
+#: lvchange.c:293
+#, c-format
+msgid "Clearing log device %s"
+msgstr ""
+
+#: lvchange.c:295
+#, c-format
+msgid "Unable to reset sync status for %s"
+msgstr ""
+
+#: lvchange.c:297
+msgid "Failed to deactivate log LV after wiping failed"
+msgstr ""
+
+#: lvchange.c:303
+#, c-format
+msgid "Unable to deactivate log LV %s after wiping for resync"
+msgstr ""
+
+#: lvchange.c:316
+msgid "Failed to update metadata on disk."
+msgstr ""
+
+#: lvchange.c:321
+#, c-format
+msgid "Failed to reactivate %s after resync"
+msgstr ""
+
+#: lvchange.c:338
+#, c-format
+msgid "Allocation policy of logical volume \"%s\" is already %s"
+msgstr ""
+
+#: lvchange.c:347
+#, c-format
+msgid "Setting contiguous allocation policy for \"%s\" to %s"
+msgstr ""
+
+#: lvchange.c:383
+#, c-format
+msgid "Read ahead is already %u for \"%s\""
+msgstr ""
+
+#: lvchange.c:390
+#, c-format
+msgid "Setting read ahead to %u for \"%s\""
+msgstr ""
+
+#: lvchange.c:429
+#, c-format
+msgid "Minor number is already not persistent for \"%s\""
+msgstr ""
+
+#: lvchange.c:436
+#, c-format
+msgid "Disabling persistent device number for \"%s\""
+msgstr ""
+
+#: lvchange.c:440
+msgid "Minor number must be specified with -My"
+msgstr ""
+
+#: lvchange.c:444
+msgid "Major number must be specified with -My"
+msgstr ""
+
+#: lvchange.c:453
+#, c-format
+msgid "%s device number not changed."
+msgstr ""
+
+#: lvchange.c:457
+#, c-format
+msgid "Ensuring %s is inactive."
+msgstr ""
+
+#: lvchange.c:459
+#, c-format
+msgid "%s: deactivation failed"
+msgstr ""
+
+#: lvchange.c:465
+#, c-format
+msgid "Setting persistent device number to (%d, %d) for \"%s\""
+msgstr ""
+
+#: lvchange.c:484
+#, c-format
+msgid "Re-activating logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:486
+#, c-format
+msgid "%s: reactivation failed"
+msgstr ""
+
+#: lvchange.c:500 lvcreate.c:680 pvchange.c:49 vgchange.c:440 vgcreate.c:107
+msgid "Failed to get tag"
+msgstr ""
+
+#: lvchange.c:505
+#, c-format
+msgid "Logical volume %s/%s does not support tags"
+msgstr ""
+
+#: lvchange.c:512 lvcreate.c:746
+#, c-format
+msgid "Failed to add tag %s to %s/%s"
+msgstr ""
+
+#: lvchange.c:518
+#, c-format
+msgid "Failed to remove tag %s from %s/%s"
+msgstr ""
+
+#: lvchange.c:551
+#, c-format
+msgid "Only -a permitted with read-only volume group \"%s\""
+msgstr ""
+
+#: lvchange.c:560
+#, c-format
+msgid "Can't change logical volume \"%s\" under snapshot"
+msgstr ""
+
+#: lvchange.c:566
+#, c-format
+msgid "Can't change snapshot logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:572
+#, c-format
+msgid "Unable to change pvmove LV %s"
+msgstr ""
+
+#: lvchange.c:574
+msgid "Use 'pvmove --abort' to abandon a pvmove"
+msgstr ""
+
+#: lvchange.c:579
+#, c-format
+msgid "Unable to change mirror log LV %s directly"
+msgstr ""
+
+#: lvchange.c:584
+#, c-format
+msgid "Unable to change mirror image LV %s directly"
+msgstr ""
+
+#: lvchange.c:590
+#, c-format
+msgid "Unable to change internal LV %s directly"
+msgstr ""
+
+#: lvchange.c:648
+#, c-format
+msgid "Logical volume \"%s\" changed"
+msgstr ""
+
+#: lvchange.c:683
+msgid ""
+"Need 1 or more of -a, -C, -j, -m, -M, -p, -r, --resync, --refresh, --alloc, "
+"--addtag, --deltag or --monitor"
+msgstr ""
+
+#: lvchange.c:694
+msgid "Only -a permitted with --ignorelockingfailure"
+msgstr ""
+
+#: lvchange.c:699
+msgid "Please give logical volume path(s)"
+msgstr ""
+
+#: lvchange.c:705
+msgid "--major and --minor require -My"
+msgstr ""
+
+#: lvchange.c:710
+msgid "Only give one logical volume when specifying minor"
+msgstr ""
+
+#: lvchange.c:715
+msgid "Only one of --alloc and --contiguous permitted"
+msgstr ""
+
+#: lvconvert.c:50 lvcreate.c:69
+msgid "Please specify a logical volume to act as the snapshot origin."
+msgstr ""
+
+#: lvconvert.c:58 lvcreate.c:77
+msgid "The origin name should include the volume group."
+msgstr ""
+
+#: lvconvert.c:69
+msgid "Please provide logical volume path"
+msgstr ""
+
+#: lvconvert.c:79 lvrename.c:38
+#, c-format
+msgid "Please use a single volume group name (\"%s\" or \"%s\")"
+msgstr ""
+
+#: lvconvert.c:88 lvrename.c:52
+msgid "Please provide a valid volume group name"
+msgstr ""
+
+#: lvconvert.c:110
+msgid "Exactly one of --mirrors or --snapshot arguments required."
+msgstr ""
+
+#: lvconvert.c:129
+msgid "--regionsize is only available with mirrors"
+msgstr ""
+
+#: lvconvert.c:134 lvcreate.c:336
+msgid "Negative chunk size is invalid"
+msgstr ""
+
+#: lvconvert.c:140 lvcreate.c:342
+msgid "Chunk size must be a power of 2 in the range 4K to 512K"
+msgstr ""
+
+#: lvconvert.c:144 lvcreate.c:346
+#, c-format
+msgid "Setting chunksize to %d sectors."
+msgstr ""
+
+#: lvconvert.c:156
+msgid "--chunksize is only available with snapshots"
+msgstr ""
+
+#: lvconvert.c:162
+msgid "--zero is only available with snapshots"
+msgstr ""
+
+#: lvconvert.c:174 lvcreate.c:253
+msgid "Negative regionsize is invalid"
+msgstr ""
+
+#: lvconvert.c:184 lvcreate.c:262
+msgid "Negative regionsize in configuration file is invalid"
+msgstr ""
+
+#: lvconvert.c:192 lvcreate.c:276
+#, c-format
+msgid "Region size (%u) must be a multiple of machine memory page size (%d)"
+msgstr ""
+
+#: lvconvert.c:200 lvcreate.c:270
+#, c-format
+msgid "Region size (%u) must be a power of 2"
+msgstr ""
+
+#: lvconvert.c:206 lvcreate.c:283
+msgid "Non-zero region size must be supplied."
+msgstr ""
+
+#: lvconvert.c:216 lvcreate.c:390 metadata/mirror.c:566
+#, c-format
+msgid "%s: Required device-mapper target(s) not detected in your kernel"
+msgstr ""
+
+#: lvconvert.c:249
+#, c-format
+msgid "Logical volume %s only has %u mirrors."
+msgstr ""
+
+#: lvconvert.c:259
+msgid "Mirror log region size cannot be changed on an existing mirror."
+msgstr ""
+
+#: lvconvert.c:266
+#, c-format
+msgid "Logical volume %s is already not mirrored."
+msgstr ""
+
+#: lvconvert.c:277
+#, c-format
+msgid "Logical volume %s has multiple mirror segments."
+msgstr ""
+
+#: lvconvert.c:287 lvconvert.c:320
+msgid "Unable to determine mirror sync status."
+msgstr ""
+
+#: lvconvert.c:311 lvconvert.c:389 lvcreate.c:721
+msgid "Failed to create mirror log."
+msgstr ""
+
+#: lvconvert.c:335
+#, c-format
+msgid "Logical volume %s already has %u mirror(s)."
+msgstr ""
+
+#: lvconvert.c:346
+msgid "Adding mirror images is not supported yet."
+msgstr ""
+
+#: lvconvert.c:363
+msgid "Mirrors of striped volumes are not yet supported."
+msgstr ""
+
+#: lvconvert.c:419 metadata/mirror.c:237
+#, c-format
+msgid "Updating \"%s\" in kernel"
+msgstr ""
+
+#: lvconvert.c:426
+#, c-format
+msgid "Logical volume %s converted."
+msgstr ""
+
+#: lvconvert.c:438 lvcreate.c:608
+#, c-format
+msgid "Couldn't find origin volume '%s'."
+msgstr ""
+
+#: lvconvert.c:443
+#, c-format
+msgid "Unable to create a snapshot of a %s LV."
+msgstr ""
+
+#: lvconvert.c:450 lvcreate.c:799
+#, c-format
+msgid "WARNING: \"%s\" not zeroed"
+msgstr ""
+
+#: lvconvert.c:452
+msgid "Aborting. Failed to wipe snapshot exception store."
+msgstr ""
+
+#: lvconvert.c:458
+#, c-format
+msgid "Couldn't deactivate LV %s."
+msgstr ""
+
+#: lvconvert.c:464 lvcreate.c:812
+msgid "Couldn't create snapshot."
+msgstr ""
+
+#: lvconvert.c:475 lvcreate.c:821
+#, c-format
+msgid "Failed to suspend origin %s"
+msgstr ""
+
+#: lvconvert.c:484 lvcreate.c:830
+#, c-format
+msgid "Problem reactivating origin %s"
+msgstr ""
+
+#: lvconvert.c:488
+#, c-format
+msgid "Logical volume %s converted to snapshot."
+msgstr ""
+
+#: lvconvert.c:499
+#, c-format
+msgid "Cannot convert locked LV %s"
+msgstr ""
+
+#: lvconvert.c:504
+#, c-format
+msgid "Can't convert logical volume \"%s\" under snapshot"
+msgstr ""
+
+#: lvconvert.c:510
+#, c-format
+msgid "Can't convert snapshot logical volume \"%s\""
+msgstr ""
+
+#: lvconvert.c:516
+#, c-format
+msgid "Unable to convert pvmove LV %s"
+msgstr ""
+
+#: lvconvert.c:548 lvrename.c:100 vgrename.c:62
+#, c-format
+msgid "Checking for existing volume group \"%s\""
+msgstr ""
+
+#: lvconvert.c:551 lvcreate.c:863 lvrename.c:103 lvresize.c:613 pvchange.c:59
+#: pvmove.c:59 pvresize.c:69 vgcreate.c:140 vgextend.c:53 vgmerge.c:34
+#: vgmerge.c:65 vgreduce.c:476 vgrename.c:94 vgrename.c:133 vgsplit.c:240
+#: vgsplit.c:277
+#, c-format
+msgid "Can't get lock for %s"
+msgstr ""
+
+#: lvconvert.c:556 lvcreate.c:492 lvrename.c:108 pvmove.c:64 vgdisplay.c:24
+#: vgmerge.c:39 vgmerge.c:72 vgreduce.c:482 vgsplit.c:245
+#, c-format
+msgid "Volume group \"%s\" doesn't exist"
+msgstr ""
+
+#: lvconvert.c:562 lvcreate.c:498 lvrename.c:114 lvresize.c:146 pvchange.c:72
+#: pvdisplay.c:41 pvmove.c:71 pvresize.c:83 reporter.c:76 reporter.c:124
+#: toollib.c:363 toollib.c:383 toollib.c:490 toollib.c:741 vgextend.c:64
+#: vgmerge.c:46 vgmerge.c:78 vgreduce.c:489 vgreduce.c:511 vgrename.c:107
+#: vgsplit.c:252
+#, c-format
+msgid "Skipping clustered volume group %s"
+msgstr ""
+
+#: lvconvert.c:567 lvcreate.c:503 lvrename.c:119 metadata/metadata.c:1377
+#: polldaemon.c:195 pvchange.c:78 pvmove.c:76 pvresize.c:89 toollib.c:163
+#: vgchange.c:534 vgck.c:34 vgconvert.c:54 vgextend.c:69 vgmerge.c:52
+#: vgmerge.c:83 vgreduce.c:541 vgremove.c:35 vgrename.c:113 vgsplit.c:258
+#, c-format
+msgid "Volume group \"%s\" is exported"
+msgstr ""
+
+#: lvconvert.c:572 lvcreate.c:508 lvremove.c:28 lvrename.c:124 pvchange.c:84
+#: pvmove.c:82 pvresize.c:95 vgchange.c:529 vgconvert.c:49 vgexport.c:42
+#: vgextend.c:74 vgmerge.c:58 vgmerge.c:88 vgreduce.c:547 vgrename.c:117
+#: vgsplit.c:270
+#, c-format
+msgid "Volume group \"%s\" is read-only"
+msgstr ""
+
+#: lvconvert.c:577
+#, c-format
+msgid "Logical volume \"%s\" not found in volume group \"%s\""
+msgstr ""
+
+#: lvcreate.c:93 lvresize.c:105
+msgid "Please provide a volume group name"
+msgstr ""
+
+#: lvcreate.c:100
+msgid "Volume group name expected (no slash)"
+msgstr ""
+
+#: lvcreate.c:115
+#, c-format
+msgid "Inconsistent volume group names given: \"%s\" and \"%s\""
+msgstr ""
+
+#: lvcreate.c:138
+#, c-format
+msgid "Logical volume name \"%s\" is invalid"
+msgstr ""
+
+#: lvcreate.c:151 lvresize.c:65
+msgid "Please specify either size or extents (not both)"
+msgstr ""
+
+#: lvcreate.c:157
+msgid "Negative number of extents is invalid"
+msgstr ""
+
+#: lvcreate.c:167
+msgid "Negative size is invalid"
+msgstr ""
+
+#: lvcreate.c:189
+msgid "Negative stripesize is invalid"
+msgstr ""
+
+#: lvcreate.c:194 lvresize.c:192
+#, c-format
+msgid "Stripe size cannot be larger than %s"
+msgstr ""
+
+#: lvcreate.c:202
+msgid "Ignoring stripesize argument with single stripe"
+msgstr ""
+
+#: lvcreate.c:210 lvresize.c:330
+#, c-format
+msgid "Using default stripesize %s"
+msgstr ""
+
+#: lvcreate.c:215
+#, c-format
+msgid "Too few physical volumes on command line for %d-way striping"
+msgstr ""
+
+#: lvcreate.c:221
+#, c-format
+msgid "Number of stripes (%d) must be between %d and %d"
+msgstr ""
+
+#: lvcreate.c:229 lvresize.c:407
+#, c-format
+msgid "Invalid stripe size %s"
+msgstr ""
+
+#: lvcreate.c:246
+#, c-format
+msgid "Too few physical volumes on command line for %d-way mirroring"
+msgstr ""
+
+#: lvcreate.c:309
+msgid "Redundant stripes argument: default is 1"
+msgstr ""
+
+#: lvcreate.c:323
+msgid "Redundant mirrors argument: default is 0"
+msgstr ""
+
+#: lvcreate.c:325 lvresize.c:180
+msgid "Mirrors argument may not be negative"
+msgstr ""
+
+#: lvcreate.c:332
+msgid "-Z is incompatible with snapshots"
+msgstr ""
+
+#: lvcreate.c:354
+msgid "-c is only available with snapshots"
+msgstr ""
+
+#: lvcreate.c:361
+msgid "mirrors and snapshots are currently incompatible"
+msgstr ""
+
+#: lvcreate.c:367
+msgid "mirrors and stripes are currently incompatible"
+msgstr ""
+
+#: lvcreate.c:378
+msgid "--corelog is only available with mirrors"
+msgstr ""
+
+#: lvcreate.c:383
+msgid "--nosync is only available with mirrors"
+msgstr ""
+
+#: lvcreate.c:419
+msgid "Conflicting contiguous and alloc arguments"
+msgstr ""
+
+#: lvcreate.c:448
+msgid "Please specify minor number with --minor when using -My"
+msgstr ""
+
+#: lvcreate.c:453
+msgid "Please specify major number with --major when using -My"
+msgstr ""
+
+#: lvcreate.c:459
+msgid "--major and --minor incompatible with -Mn"
+msgstr ""
+
+#: lvcreate.c:489 pvmove.c:305 toollib.c:481 vgreduce.c:474
+#, c-format
+msgid "Finding volume group \"%s\""
+msgstr ""
+
+#: lvcreate.c:513 lvrename.c:129
+#, c-format
+msgid "Logical volume \"%s\" already exists in volume group \"%s\""
+msgstr ""
+
+#: lvcreate.c:519
+msgid "Metadata does not support mirroring."
+msgstr ""
+
+#: lvcreate.c:536
+#, c-format
+msgid "Reducing requested stripe size %s to maximum, physical extent size %s"
+msgstr ""
+
+#: lvcreate.c:547
+#, c-format
+msgid "Stripe size may not exceed %s"
+msgstr ""
+
+#: lvcreate.c:559 lvresize.c:237
+#, c-format
+msgid "Rounding up size to full physical extent %s"
+msgstr ""
+
+#: lvcreate.c:564
+#, c-format
+msgid "Volume too large (%s) for extent size %s. Upper limit is %s."
+msgstr ""
+
+#: lvcreate.c:583
+#, c-format
+msgid "Please express size as %%VG or %%FREE."
+msgstr ""
+
+#: lvcreate.c:590
+#, c-format
+msgid "Rounding size (%d extents) up to stripe boundary size (%d extents)"
+msgstr ""
+
+#: lvcreate.c:598
+msgid "Can't create snapshot without using device-mapper kernel driver"
+msgstr ""
+
+#: lvcreate.c:604
+msgid "Clustered snapshots are not yet supported."
+msgstr ""
+
+#: lvcreate.c:613
+msgid "Snapshots of snapshots are not supported yet."
+msgstr ""
+
+#: lvcreate.c:618
+msgid "Snapshots of locked devices are not supported yet"
+msgstr ""
+
+#: lvcreate.c:625
+msgid "Snapshots and mirrors may not yet be mixed."
+msgstr ""
+
+#: lvcreate.c:634
+msgid "Unable to create new logical volume with no extents"
+msgstr ""
+
+#: lvcreate.c:640
+#, c-format
+msgid "Insufficient free extents (%u) in volume group %s: %u required"
+msgstr ""
+
+#: lvcreate.c:646
+#, c-format
+msgid "Number of stripes (%u) must not exceed number of physical volumes (%d)"
+msgstr ""
+
+#: lvcreate.c:653
+msgid "Can't create mirror without using device-mapper kernel driver."
+msgstr ""
+
+#: lvcreate.c:672
+msgid "Failed to generate LV name."
+msgstr ""
+
+#: lvcreate.c:685 vgchange.c:445
+#, c-format
+msgid "Volume group %s does not support tags"
+msgstr ""
+
+#: lvcreate.c:709
+msgid ""
+"WARNING: New mirror won't be synchronised. Don't read what you didn't write!"
+msgstr ""
+
+#: lvcreate.c:733
+msgid "Setting read ahead sectors"
+msgstr ""
+
+#: lvcreate.c:741
+#, c-format
+msgid "Setting device number to (%d, %d)"
+msgstr ""
+
+#: lvcreate.c:782
+msgid ""
+"Aborting. Failed to activate snapshot exception store. Remove new LV and "
+"retry."
+msgstr ""
+
+#: lvcreate.c:787
+msgid "Failed to activate new LV."
+msgstr ""
+
+#: lvcreate.c:794
+msgid ""
+"Aborting. Failed to wipe snapshot exception store. Remove new LV and retry."
+msgstr ""
+
+#: lvcreate.c:837
+#, c-format
+msgid "Logical volume \"%s\" created"
+msgstr ""
+
+#: lvdisplay.c:39 lvdisplay.c:48 pvdisplay.c:89 pvdisplay.c:99 vgdisplay.c:67
+#: vgdisplay.c:76
+msgid "Incompatible options selected"
+msgstr ""
+
+#: lvdisplay.c:53
+msgid "Options -v and -c are incompatible"
+msgstr ""
+
+#: lvmchange.c:21
+msgid "With LVM2 and the device mapper, this program is obsolete."
+msgstr ""
+
+#: lvmcmdline.c:289
+msgid "Minor number outside range 0-255"
+msgstr ""
+
+#: lvmcmdline.c:304
+msgid "Major number outside range 0-255"
+msgstr ""
+
+#: lvmcmdline.c:402
+msgid "Couldn't allocate memory."
+msgstr ""
+
+#: lvmcmdline.c:451
+msgid "Out of memory."
+msgstr ""
+
+#: lvmcmdline.c:504
+#, c-format
+msgid ""
+"%s: %s\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lvmcmdline.c:598
+msgid "Unrecognised option."
+msgstr ""
+
+#: lvmcmdline.c:604
+#, c-format
+msgid "Option%s%c%s%s may not be repeated"
+msgstr ""
+
+#: lvmcmdline.c:613
+msgid "Option requires argument."
+msgstr ""
+
+#: lvmcmdline.c:620
+#, c-format
+msgid "Invalid argument %s"
+msgstr ""
+
+#: lvmcmdline.c:639
+#, c-format
+msgid "%s and %s are synonyms.  Please only supply one."
+msgstr ""
+
+#: lvmcmdline.c:667
+#, c-format
+msgid "LVM version:     %s"
+msgstr ""
+
+#: lvmcmdline.c:669
+#, c-format
+msgid "Library version: %s"
+msgstr ""
+
+#: lvmcmdline.c:671
+#, c-format
+msgid "Driver version:  %s"
+msgstr ""
+
+#: lvmcmdline.c:706
+msgid "Partial mode. Incomplete volume groups will be activated read-only."
+msgstr ""
+
+#: lvmcmdline.c:729
+msgid "--trustcache is incompatible with --all"
+msgstr ""
+
+#: lvmcmdline.c:733
+msgid ""
+"WARNING: Cache file of PVs will be trusted.  New devices holding PVs may get "
+"ignored."
+msgstr ""
+
+#: lvmcmdline.c:767
+msgid "Available lvm commands:"
+msgstr ""
+
+#: lvmcmdline.c:768
+msgid "Use 'lvm help <command>' for more information"
+msgstr ""
+
+#: lvmcmdline.c:774
+#, c-format
+msgid "%-16.16s%s"
+msgstr ""
+
+#: lvmcmdline.c:794
+msgid "Failed to set overridden configuration entries."
+msgstr ""
+
+#: lvmcmdline.c:858
+msgid "Couldn't copy command line."
+msgstr ""
+
+#: lvmcmdline.c:871
+#, c-format
+msgid "Parsing: %s"
+msgstr ""
+
+#: lvmcmdline.c:877
+msgid "Error during parsing of command line."
+msgstr ""
+
+#: lvmcmdline.c:890
+msgid "Updated config file invalid. Aborting."
+msgstr ""
+
+#: lvmcmdline.c:899
+#, c-format
+msgid "Processing: %s"
+msgstr ""
+
+#: lvmcmdline.c:902
+msgid "O_DIRECT will be used"
+msgstr ""
+
+#: lvmcmdline.c:915
+#, c-format
+msgid "Locking type %d initialisation failed."
+msgstr ""
+
+#: lvmcmdline.c:927
+msgid "Test mode: Wiping internal cache"
+msgstr ""
+
+#: lvmcmdline.c:951
+#, c-format
+msgid "Completed: %s"
+msgstr ""
+
+#: lvmcmdline.c:1073
+#, c-format
+msgid "Line too long (max 255) beginning: %s"
+msgstr ""
+
+#: lvmcmdline.c:1080
+#, c-format
+msgid "Too many arguments: %s"
+msgstr ""
+
+#: lvmcmdline.c:1125
+msgid "Failed to create LVM1 tool pathname"
+msgstr ""
+
+#: lvmcmdline.c:1173
+msgid "Falling back to LVM1 tools, but no command specified."
+msgstr ""
+
+#: lvmcmdline.c:1189
+msgid "Please supply an LVM command."
+msgstr ""
+
+#: lvmcmdline.c:1203
+msgid "No such command.  Try 'help'."
+msgstr ""
+
+#: lvmdiskscan.c:38 lvmdiskscan.c:108
+msgid "dev_iter_create failed"
+msgstr ""
+
+#: lvmdiskscan.c:66
+#, c-format
+msgid "%-*s [%15s] %s"
+msgstr ""
+
+#: lvmdiskscan.c:83 lvmdiskscan.c:117
+#, c-format
+msgid "Couldn't get size of \"%s\""
+msgstr ""
+
+#: lvmdiskscan.c:88
+#, c-format
+msgid "dev_close on \"%s\" failed"
+msgstr ""
+
+#: lvmdiskscan.c:103
+msgid "WARNING: only considering LVM devices"
+msgstr ""
+
+#: lvmdiskscan.c:137
+#, c-format
+msgid "%d disk%s"
+msgstr ""
+
+#: lvmdiskscan.c:139
+#, c-format
+msgid "%d partition%s"
+msgstr ""
+
+#: lvmdiskscan.c:142
+#, c-format
+msgid "%d LVM physical volume whole disk%s"
+msgstr ""
+
+#: lvmdiskscan.c:144
+#, c-format
+msgid "%d LVM physical volume%s"
+msgstr ""
+
+#: lvremove.c:33
+#, c-format
+msgid "Can't remove logical volume \"%s\" under snapshot"
+msgstr ""
+
+#: lvremove.c:39
+#, c-format
+msgid "Can't remove logical volume %s used by a mirror"
+msgstr ""
+
+#: lvremove.c:45
+#, c-format
+msgid "Can't remove logical volume %s used as mirror log"
+msgstr ""
+
+#: lvremove.c:51
+#, c-format
+msgid "Can't remove locked LV %s"
+msgstr ""
+
+#: lvremove.c:59
+#, c-format
+msgid "Can't remove open logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:68
+#, c-format
+msgid "Logical volume \"%s\" not removed"
+msgstr ""
+
+#: lvremove.c:82
+#, c-format
+msgid "Can't get exclusive access to volume \"%s\""
+msgstr ""
+
+#: lvremove.c:90
+#, c-format
+msgid "Unable to deactivate logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:97
+#, c-format
+msgid "Removing snapshot %s"
+msgstr ""
+
+#: lvremove.c:104
+#, c-format
+msgid "Releasing logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:106
+#, c-format
+msgid "Error releasing logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:122
+#, c-format
+msgid "Failed to refresh %s without snapshot."
+msgstr ""
+
+#: lvremove.c:124
+#, c-format
+msgid "Failed to resume %s."
+msgstr ""
+
+#: lvremove.c:127
+#, c-format
+msgid "Logical volume \"%s\" successfully removed"
+msgstr ""
+
+#: lvremove.c:134
+msgid "Please enter one or more logical volume paths"
+msgstr ""
+
+#: lvrename.c:47
+msgid "Old and new logical volume names required"
+msgstr ""
+
+#: lvrename.c:59
+#, c-format
+msgid "Logical volume names must have the same volume group (\"%s\" or \"%s\")"
+msgstr ""
+
+#: lvrename.c:74
+#, c-format
+msgid "New logical volume path exceeds maximum length of %zu!"
+msgstr ""
+
+#: lvrename.c:80
+msgid "New logical volume name may not be blank"
+msgstr ""
+
+#: lvrename.c:90
+#, c-format
+msgid "New logical volume name \"%s\" is invalid"
+msgstr ""
+
+#: lvrename.c:96
+msgid "Old and new logical volume names must differ"
+msgstr ""
+
+#: lvrename.c:135
+#, c-format
+msgid "Existing logical volume \"%s\" not found in volume group \"%s\""
+msgstr ""
+
+#: lvrename.c:143
+#, c-format
+msgid "Cannot rename locked LV %s"
+msgstr ""
+
+#: lvrename.c:150 lvrename.c:158
+#, c-format
+msgid "Mirrored LV, \"%s\" cannot be renamed: %s"
+msgstr ""
+
+#: lvrename.c:169
+msgid "Failed to allocate space for new name"
+msgstr ""
+
+#: lvrename.c:173 vgmerge.c:223 vgrename.c:165
+msgid "Writing out updated volume group"
+msgstr ""
+
+#: lvrename.c:197
+#, c-format
+msgid "Renamed \"%s\" to \"%s\" in volume group \"%s\""
+msgstr ""
+
+#: lvresize.c:83
+msgid "Negative argument not permitted - use lvreduce"
+msgstr ""
+
+#: lvresize.c:88
+msgid "Positive sign not permitted - use lvextend"
+msgstr ""
+
+#: lvresize.c:96
+msgid "Please provide the logical volume name"
+msgstr ""
+
+#: lvresize.c:140
+#, c-format
+msgid "Volume group %s doesn't exist"
+msgstr ""
+
+#: lvresize.c:151
+#, c-format
+msgid "Volume group %s is exported"
+msgstr ""
+
+#: lvresize.c:156
+#, c-format
+msgid "Volume group %s is read-only"
+msgstr ""
+
+#: lvresize.c:162
+#, c-format
+msgid "Logical volume %s not found in volume group %s"
+msgstr ""
+
+#: lvresize.c:171
+msgid "Varied striping not supported. Ignoring."
+msgstr ""
+
+#: lvresize.c:178
+msgid "Mirrors not supported. Ignoring."
+msgstr ""
+
+#: lvresize.c:187
+msgid "Stripesize may not be negative."
+msgstr ""
+
+#: lvresize.c:198
+msgid "Varied stripesize not supported. Ignoring."
+msgstr ""
+
+#: lvresize.c:200
+#, c-format
+msgid "Reducing stripe size %s to maximum, physical extent size %s"
+msgstr ""
+
+#: lvresize.c:211
+msgid "Mirrors and striping cannot be combined yet."
+msgstr ""
+
+#: lvresize.c:215
+msgid "Stripe size must be power of 2"
+msgstr ""
+
+#: lvresize.c:223
+#, c-format
+msgid "Can't resize locked LV %s"
+msgstr ""
+
+#: lvresize.c:263
+#, c-format
+msgid "Unable to reduce %s below 1 extent"
+msgstr ""
+
+#: lvresize.c:272
+msgid "New size of 0 not permitted"
+msgstr ""
+
+#: lvresize.c:277 lvresize.c:414
+#, c-format
+msgid "New size (%d extents) matches existing size (%d extents)"
+msgstr ""
+
+#: lvresize.c:291
+#, c-format
+msgid "VolumeType does not match (%s)"
+msgstr ""
+
+#: lvresize.c:308
+msgid "Please specify number of stripes (-i) and stripesize (-I)"
+msgstr ""
+
+#: lvresize.c:322
+#, c-format
+msgid "Using stripesize of last segment %s"
+msgstr ""
+
+#: lvresize.c:346
+#, c-format
+msgid "Extending %u mirror images."
+msgstr ""
+
+#: lvresize.c:352
+msgid "Cannot vary number of mirrors in LV yet."
+msgstr ""
+
+#: lvresize.c:362
+msgid "Ignoring stripes, stripesize and mirrors arguments when reducing"
+msgstr ""
+
+#: lvresize.c:391
+msgid "Stripesize for striped segment should not be 0!"
+msgstr ""
+
+#: lvresize.c:400
+#, c-format
+msgid ""
+"Rounding size (%d extents) down to stripe boundary size for segment (%d "
+"extents)"
+msgstr ""
+
+#: lvresize.c:421
+#, c-format
+msgid "New size given (%d extents) not larger than existing size (%d extents)"
+msgstr ""
+
+#: lvresize.c:431
+#, c-format
+msgid "New size given (%d extents) not less than existing size (%d extents)"
+msgstr ""
+
+#: lvresize.c:441
+msgid "Mirrors cannot be resized while active yet."
+msgstr ""
+
+#: lvresize.c:447
+msgid "Snapshot origin volumes cannot be reduced in size yet."
+msgstr ""
+
+#: lvresize.c:455
+msgid ""
+"Snapshot origin volumes can be resized only while inactive: try lvchange -an"
+msgstr ""
+
+#: lvresize.c:463
+msgid "Ignoring PVs on command line when reducing"
+msgstr ""
+
+#: lvresize.c:474
+msgid "lv_info failed: aborting"
+msgstr ""
+
+#: lvresize.c:479
+#, c-format
+msgid "Logical volume %s must be activated before resizing filesystem"
+msgstr ""
+
+#: lvresize.c:485
+#, c-format
+msgid "WARNING: Reducing active%s logical volume to %s"
+msgstr ""
+
+#: lvresize.c:490
+msgid "THIS MAY DESTROY YOUR DATA (filesystem etc.)"
+msgstr ""
+
+#: lvresize.c:497
+#, c-format
+msgid "Logical volume %s NOT reduced"
+msgstr ""
+
+#: lvresize.c:508
+#, c-format
+msgid "Couldn't create LV path for %s"
+msgstr ""
+
+#: lvresize.c:516
+msgid "Couldn't generate new LV size string"
+msgstr ""
+
+#: lvresize.c:540
+#, c-format
+msgid "%sing logical volume %s to %s"
+msgstr ""
+
+#: lvresize.c:589
+#, c-format
+msgid "Logical volume %s successfully resized"
+msgstr ""
+
+#: lvresize.c:611
+#, c-format
+msgid "Finding volume group %s"
+msgstr ""
+
+#: lvscan.c:64
+#, c-format
+msgid "%s%s '%s%s/%s' [%s] %s"
+msgstr ""
+
+#: lvscan.c:79
+msgid "No additional command line arguments allowed"
+msgstr ""
+
+#: metadata/lv_manip.c:96
+msgid "alloc_lv_segment: Missing segtype."
+msgstr ""
+
+#: metadata/lv_manip.c:131
+msgid "Failed to find snapshot segtype"
+msgstr ""
+
+#: metadata/lv_manip.c:139
+msgid "Couldn't allocate new snapshot segment."
+msgstr ""
+
+#: metadata/lv_manip.c:280
+#, c-format
+msgid "Segment extent reduction %unot divisible by #stripes %u"
+msgstr ""
+
+#: metadata/lv_manip.c:445
+msgid "Striped mirrors are not supported yet"
+msgstr ""
+
+#: metadata/lv_manip.c:450
+msgid "Can't mix striping or mirroring with creation of a mirrored PV yet"
+msgstr ""
+
+#: metadata/lv_manip.c:456
+msgid "Can't mix striping or pvmove with a mirror log yet."
+msgstr ""
+
+#: metadata/lv_manip.c:471
+msgid "allocation handle allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:481
+msgid "allocation pool creation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:516 report/report.c:92 report/report.c:152
+msgid "dm_pool_begin_object failed"
+msgstr ""
+
+#: metadata/lv_manip.c:523 metadata/lv_manip.c:528 metadata/lv_manip.c:535
+#: report/report.c:112 report/report.c:123 report/report.c:129
+#: report/report.c:135 report/report.c:159 report/report.c:165
+msgid "dm_pool_grow_object failed"
+msgstr ""
+
+#: metadata/lv_manip.c:541
+#, c-format
+msgid "Parallel PVs at LE %u length %u: %s"
+msgstr ""
+
+#: metadata/lv_manip.c:574
+msgid "Couldn't allocate new LV segment."
+msgstr ""
+
+#: metadata/lv_manip.c:654
+msgid "alloced_area allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:705
+#, c-format
+msgid "Failed to find segment for %s extent %u"
+msgstr ""
+
+#: metadata/lv_manip.c:907
+#, c-format
+msgid "Insufficient free space: %u extents needed, but only %u available"
+msgstr ""
+
+#: metadata/lv_manip.c:1081
+msgid "_allocate called with no work to do!"
+msgstr ""
+
+#: metadata/lv_manip.c:1105
+msgid "Not enough PVs with free space available for parallel allocation."
+msgstr ""
+
+#: metadata/lv_manip.c:1107
+msgid "Consider --alloc anywhere if desperate."
+msgstr ""
+
+#: metadata/lv_manip.c:1120
+msgid "Couldn't allocate areas array."
+msgstr ""
+
+#: metadata/lv_manip.c:1137
+#, c-format
+msgid ""
+"Insufficient suitable %sallocatable extents for logical volume %s: %u more "
+"required"
+msgstr ""
+
+#: metadata/lv_manip.c:1147
+#, c-format
+msgid "Insufficient extents for log allocation for logical volume %s."
+msgstr ""
+
+#: metadata/lv_manip.c:1168
+msgid "Couldn't allocate new zero segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1201
+msgid "allocate_extents does not handle virtual segments"
+msgstr ""
+
+#: metadata/lv_manip.c:1207
+#, c-format
+msgid "Metadata format (%s) does not support required LV segment type (%s)."
+msgstr ""
+
+#: metadata/lv_manip.c:1210
+msgid "Consider changing the metadata format by running vgconvert."
+msgstr ""
+
+#: metadata/lv_manip.c:1251
+msgid "Missing segtype in lv_add_segment()."
+msgstr ""
+
+#: metadata/lv_manip.c:1256
+msgid "lv_add_segment cannot handle virtual segments"
+msgstr ""
+
+#: metadata/lv_manip.c:1270
+msgid "Couldn't merge segments after extending logical volume."
+msgstr ""
+
+#: metadata/lv_manip.c:1292
+msgid "Log segments can only be added to an empty LV"
+msgstr ""
+
+#: metadata/lv_manip.c:1301
+msgid "Couldn't allocate new mirror log segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1339
+#, c-format
+msgid "Log LV %s is empty."
+msgstr ""
+
+#: metadata/lv_manip.c:1349
+msgid "Couldn't allocate new mirror segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1384
+msgid "Mirrored LV must only have one segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1394
+#, c-format
+msgid "Failed to allocate widened LV segment for %s."
+msgstr ""
+
+#: metadata/lv_manip.c:1446
+#, c-format
+msgid "Aborting. Failed to extend %s."
+msgstr ""
+
+#: metadata/lv_manip.c:1499
+#, c-format
+msgid "Maximum number of logical volumes (%u) reached in volume group %s"
+msgstr ""
+
+#: metadata/lv_manip.c:1506
+msgid "Failed to generate unique name for the new logical volume"
+msgstr ""
+
+#: metadata/lv_manip.c:1512
+#, c-format
+msgid "Creating logical volume %s"
+msgstr ""
+
+#: metadata/lv_manip.c:1516
+msgid "lv_list allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1526
+msgid "lv name strdup failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1574 metadata/metadata.c:986
+msgid "pv_list allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1596
+msgid "parallel_areas allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1604
+msgid "allocation failed"
+msgstr ""
+
+#: metadata/merge.c:72
+#, c-format
+msgid "LV %s invalid: segment %u should begin at LE %u (found %u)."
+msgstr ""
+
+#: metadata/merge.c:82
+#, c-format
+msgid "LV %s: segment %u has inconsistent area_len %u"
+msgstr ""
+
+#: metadata/merge.c:90
+#, c-format
+msgid "LV %s: segment %u has log LV but is not mirrored"
+msgstr ""
+
+#: metadata/merge.c:97
+#, c-format
+msgid "LV %s: segment %u log LV %s is not a mirror log"
+msgstr ""
+
+#: metadata/merge.c:105
+#, c-format
+msgid "LV %s: segment %u log LV does not point back to mirror segment"
+msgstr ""
+
+#: metadata/merge.c:115
+#, c-format
+msgid "LV %s: segment %u mirror image is not mirrored"
+msgstr ""
+
+#: metadata/merge.c:124
+#, c-format
+msgid "LV %s: segment %u has unassigned area %u."
+msgstr ""
+
+#: metadata/merge.c:132
+#, c-format
+msgid "LV %s: segment %u has inconsistent PV area %u"
+msgstr ""
+
+#: metadata/merge.c:141
+#, c-format
+msgid "LV %s: segment %u has inconsistent LV area %u"
+msgstr ""
+
+#: metadata/merge.c:152
+#, c-format
+msgid "LV %s: segment %u mirror image %u missing mirror ptr"
+msgstr ""
+
+#: metadata/merge.c:174
+#, c-format
+msgid "LV %s: inconsistent LE count %u != %u"
+msgstr ""
+
+#: metadata/merge.c:195
+#, c-format
+msgid "Unable to split the %s segment at LE %u in LV %s"
+msgstr ""
+
+#: metadata/merge.c:208
+msgid "Couldn't allocate cloned LV segment."
+msgstr ""
+
+#: metadata/merge.c:213
+msgid "LV segment tags duplication failed"
+msgstr ""
+
+#: metadata/merge.c:240
+#, c-format
+msgid "Split %s:%u[%u] at %u: %s LE %u"
+msgstr ""
+
+#: metadata/merge.c:256
+#, c-format
+msgid "Split %s:%u[%u] at %u: %s PE %u"
+msgstr ""
+
+#: metadata/merge.c:263 metadata/metadata.c:495
+#, c-format
+msgid "Unassigned area %u found in segment"
+msgstr ""
+
+#: metadata/merge.c:282
+#, c-format
+msgid "Segment with extent %u in LV %s not found"
+msgstr ""
+
+#: metadata/metadata.c:43
+#, c-format
+msgid "Adding physical volume '%s' to volume group '%s'"
+msgstr ""
+
+#: metadata/metadata.c:47 metadata/metadata.c:1008
+#, c-format
+msgid "pv_list allocation for '%s' failed"
+msgstr ""
+
+#: metadata/metadata.c:53
+#, c-format
+msgid "%s not identified as an existing physical volume"
+msgstr ""
+
+#: metadata/metadata.c:59
+#, c-format
+msgid "Physical volume '%s' is already in volume group '%s'"
+msgstr ""
+
+#: metadata/metadata.c:65
+#, c-format
+msgid "Physical volume %s is of different format type (%s)"
+msgstr ""
+
+#: metadata/metadata.c:72
+#, c-format
+msgid "Physical volume %s might be constructed from same volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:78 metadata/metadata.c:199
+#, c-format
+msgid "vg->name allocation failed for '%s'"
+msgstr ""
+
+#: metadata/metadata.c:100
+#, c-format
+msgid "Format-specific setup of physical volume '%s' failed."
+msgstr ""
+
+#: metadata/metadata.c:106
+#, c-format
+msgid "Physical volume '%s' listed more than once."
+msgstr ""
+
+#: metadata/metadata.c:112
+#, c-format
+msgid "No space for '%s' - volume group '%s' holds max %d physical volume(s)."
+msgstr ""
+
+#: metadata/metadata.c:127
+#, c-format
+msgid "Unable to add %s to %s: new extent count (%lu) exceeds limit (%u)."
+msgstr ""
+
+#: metadata/metadata.c:148
+msgid "PV tags duplication failed"
+msgstr ""
+
+#: metadata/metadata.c:170
+#, c-format
+msgid "get_pv_from_vg_by_id: vg_read failed to read VG %s"
+msgstr ""
+
+#: metadata/metadata.c:176
+#, c-format
+msgid "Warning: Volume group %s is not consistent"
+msgstr ""
+
+#: metadata/metadata.c:205
+#, c-format
+msgid "pv->vg_name allocation failed for '%s'"
+msgstr ""
+
+#: metadata/metadata.c:222
+#, c-format
+msgid "Unable to add physical volume '%s' to volume group '%s'."
+msgstr ""
+
+#: metadata/metadata.c:260
+#, c-format
+msgid "A volume group called '%s' already exists."
+msgstr ""
+
+#: metadata/metadata.c:266
+#, c-format
+msgid "Couldn't create uuid for volume group '%s'."
+msgstr ""
+
+#: metadata/metadata.c:309 metadata/metadata.c:1085 metadata/metadata.c:1151
+msgid "Failed to create format instance"
+msgstr ""
+
+#: metadata/metadata.c:315
+#, c-format
+msgid "Format specific setup of volume group '%s' failed."
+msgstr ""
+
+#: metadata/metadata.c:338
+#, c-format
+msgid "New size %lu for %s%s not an exact number of new extents."
+msgstr ""
+
+#: metadata/metadata.c:346
+#, c-format
+msgid "New extent count %lu for %s%s exceeds 32 bits."
+msgstr ""
+
+#: metadata/metadata.c:556
+#, c-format
+msgid "Failed to create random uuid for %s."
+msgstr ""
+
+#: metadata/metadata.c:575 pvresize.c:128
+#, c-format
+msgid "WARNING: %s: Overriding real size. You could lose data."
+msgstr ""
+
+#: metadata/metadata.c:577
+#, c-format
+msgid "%s: Pretending size is %lu sectors."
+msgstr ""
+
+#: metadata/metadata.c:583 pvresize.c:136
+#, c-format
+msgid "%s: Size must exceed minimum of %ld sectors."
+msgstr ""
+
+#: metadata/metadata.c:601
+#, c-format
+msgid "%s: Format-specific setup of physical volume failed."
+msgstr ""
+
+#: metadata/metadata.c:699
+#, c-format
+msgid "Physical volume %s not found"
+msgstr ""
+
+#: metadata/metadata.c:704
+#, c-format
+msgid "Physical volume %s not in a volume group"
+msgstr ""
+
+#: metadata/metadata.c:780
+#, c-format
+msgid "Internal error: Duplicate PV id %s detected for %s in %s."
+msgstr ""
+
+#: metadata/metadata.c:789
+#, c-format
+msgid "Internal error: VG name for PV %s is corrupted"
+msgstr ""
+
+#: metadata/metadata.c:796 metadata/metadata.c:1278
+#, c-format
+msgid "Internal error: PV segments corrupted in %s."
+msgstr ""
+
+#: metadata/metadata.c:806
+#, c-format
+msgid "Internal error: Duplicate LV name %s detected in %s."
+msgstr ""
+
+#: metadata/metadata.c:816
+#, c-format
+msgid "Internal error: Duplicate LV id %s detected for %s and %s in %s."
+msgstr ""
+
+#: metadata/metadata.c:827 metadata/metadata.c:1285
+#, c-format
+msgid "Internal error: LV segments corrupted in %s."
+msgstr ""
+
+#: metadata/metadata.c:851
+#, c-format
+msgid "Cannot change metadata for partial volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:857
+msgid "Aborting vg_write: No metadata areas to write to!"
+msgstr ""
+
+#: metadata/metadata.c:866
+msgid "Format does not support writing volumegroup metadata areas"
+msgstr ""
+
+#: metadata/metadata.c:969
+msgid "vg allocation failed"
+msgstr ""
+
+#: metadata/metadata.c:977
+msgid "vg name allocation failed"
+msgstr ""
+
+#: metadata/metadata.c:1049
+msgid "Internal error: vg_read requires vgname with pre-commit."
+msgstr ""
+
+#: metadata/metadata.c:1113 metadata/metadata.c:1122
+#, c-format
+msgid "Cached VG %s had incorrect PV list"
+msgstr ""
+
+#: metadata/metadata.c:1201
+#, c-format
+msgid "Inconsistent pre-commit metadata copies for volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1212
+#, c-format
+msgid "Inconsistent metadata copies found for partial volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1220
+#, c-format
+msgid "Inconsistent metadata UUIDs found for volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1226
+#, c-format
+msgid "Inconsistent metadata found for VG %s - updating to use version %u"
+msgstr ""
+
+#: metadata/metadata.c:1230
+msgid "Automatic metadata correction failed"
+msgstr ""
+
+#: metadata/metadata.c:1235
+msgid "Automatic metadata correction commit failed"
+msgstr ""
+
+#: metadata/metadata.c:1247
+#, c-format
+msgid "Removing PV %s (%s) that no longer belongs to VG %s"
+msgstr ""
+
+#: metadata/metadata.c:1257
+#, c-format
+msgid "WARNING: Interrupted pvmove detected in volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1259
+msgid "Please restore the metadata by running vgcfgrestore."
+msgstr ""
+
+#: metadata/metadata.c:1316 metadata/metadata.c:1348
+#, c-format
+msgid "Volume group %s metadata is inconsistent"
+msgstr ""
+
+#: metadata/metadata.c:1335
+msgid "vg_read_by_vgid: get_vgs failed"
+msgstr ""
+
+#: metadata/metadata.c:1369
+#, c-format
+msgid "Finding volume group for uuid %s"
+msgstr ""
+
+#: metadata/metadata.c:1371
+#, c-format
+msgid "Volume group for uuid not found: %s"
+msgstr ""
+
+#: metadata/metadata.c:1375
+#, c-format
+msgid "Found volume group \"%s\""
+msgstr ""
+
+#: metadata/metadata.c:1381
+#, c-format
+msgid "Can't find logical volume id %s"
+msgstr ""
+
+#: metadata/metadata.c:1405
+#, c-format
+msgid "No physical volume label read from %s"
+msgstr ""
+
+#: metadata/metadata.c:1415
+#, c-format
+msgid "pv allocation for '%s' failed"
+msgstr ""
+
+#: metadata/metadata.c:1424
+#, c-format
+msgid "Failed to read existing physical volume '%s'"
+msgstr ""
+
+#: metadata/metadata.c:1466
+msgid "PV list allocation failed"
+msgstr ""
+
+#: metadata/metadata.c:1474
+msgid "get_pvs: get_vgs failed"
+msgstr ""
+
+#: metadata/metadata.c:1498
+#, c-format
+msgid "Warning: Volume Group %s is not consistent"
+msgstr ""
+
+#: metadata/metadata.c:1516
+msgid "Format does not support writing physical volumes"
+msgstr ""
+
+#: metadata/metadata.c:1521
+#, c-format
+msgid "Assertion failed: can't _pv_write non-orphan PV (in VG %s)"
+msgstr ""
+
+#: metadata/metadata.c:1547 vgreduce.c:410
+#, c-format
+msgid ""
+"Failed to clear metadata from physical volume \"%s\" after removal from \"%s"
+"\""
+msgstr ""
+
+#: metadata/metadata.c:1570 pvcreate.c:81
+#, c-format
+msgid "Device %s not found (or ignored by filtering)."
+msgstr ""
+
+#: metadata/metadata.c:1579
+#, c-format
+msgid "Could not find LVM label on %s"
+msgstr ""
+
+#: metadata/metadata.c:1584
+#, c-format
+msgid "Found label on %s, sector %lu, type=%s"
+msgstr ""
+
+#: metadata/mirror.c:52 mirror/mirrored.c:322
+#, c-format
+msgid "Using reduced mirror region size of %u sectors"
+msgstr ""
+
+#: metadata/mirror.c:94
+msgid "Aborting. Unable to tag."
+msgstr ""
+
+#: metadata/mirror.c:100
+msgid "Intermediate VG commit for orphan volume failed."
+msgstr ""
+
+#: metadata/mirror.c:138
+#, c-format
+msgid "Reducing mirror set from %u to %u image(s)%s."
+msgstr ""
+
+#: metadata/mirror.c:183
+msgid "No mirror images found using specified PVs."
+msgstr ""
+
+#: metadata/mirror.c:222
+msgid "intermediate VG write failed."
+msgstr ""
+
+#: metadata/mirror.c:277
+msgid "Bad activation/mirror_log_fault_policy"
+msgstr ""
+
+#: metadata/mirror.c:279
+msgid "Bad activation/mirror_device_fault_policy"
+msgstr ""
+
+#: metadata/mirror.c:317
+#, c-format
+msgid "WARNING: Failed to replace mirror device in %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:321
+#, c-format
+msgid ""
+"WARNING: Use 'lvconvert -m %d %s/%s --corelog' to replace failed devices"
+msgstr ""
+
+#: metadata/mirror.c:324 metadata/mirror.c:341
+#, c-format
+msgid "WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices"
+msgstr ""
+
+#: metadata/mirror.c:338
+#, c-format
+msgid "WARNING: Failed to replace mirror log device in %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:362
+#, c-format
+msgid "WARNING: Unable to determine mirror sync status of %s/%s."
+msgstr ""
+
+#: metadata/mirror.c:380
+#, c-format
+msgid "WARNING: Bad device removed from mirror volume, %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:393
+#, c-format
+msgid "WARNING: Unable to find substitute device for mirror volume, %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:397
+#, c-format
+msgid ""
+"WARNING: Mirror volume, %s/%s restored - substitute for failed device found."
+msgstr ""
+
+#: metadata/mirror.c:402
+#, c-format
+msgid ""
+"WARNING: Mirror volume, %s/%s converted to linear due to device failure."
+msgstr ""
+
+#: metadata/mirror.c:405
+#, c-format
+msgid "WARNING: Mirror volume, %s/%s disk log removed due to device failure."
+msgstr ""
+
+#: metadata/mirror.c:428 metadata/mirror.c:434
+msgid "img_name allocation failed. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:443
+msgid "Aborting. Failed to create mirror image LV. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:455
+#, c-format
+msgid ""
+"Aborting. Failed to add mirror image segment to %s. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:477 metadata/mirror.c:518
+msgid "img_lvs allocation failed. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:499
+msgid "Aborting. Failed to add mirror segment. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:632
+#, c-format
+msgid "Matched PE range %u-%u against %s %u len %u"
+msgstr ""
+
+#: metadata/mirror.c:641 metadata/mirror.c:872 vgreduce.c:139
+msgid "lv_list alloc failed"
+msgstr ""
+
+#: metadata/mirror.c:651
+#, c-format
+msgid "Moving %s:%u-%u of %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:664
+msgid "Unable to allocate temporary LV for pvmove."
+msgstr ""
+
+#: metadata/mirror.c:679
+#, c-format
+msgid "Moving %u extents of logical volume %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:711
+msgid "No segment found with LE"
+msgstr ""
+
+#: metadata/mirror.c:722
+msgid "Incompatible segments"
+msgstr ""
+
+#: metadata/mirror.c:747
+msgid "Missing error segtype"
+msgstr ""
+
+#: metadata/mirror.c:853
+msgid "lvs list alloc failed"
+msgstr ""
+
+#: metadata/pv_manip.c:30
+msgid "pv_segment allocation failed"
+msgstr ""
+
+#: metadata/pv_manip.c:121
+#, c-format
+msgid "Segment with extent %u in PV %s not found"
+msgstr ""
+
+#: metadata/pv_manip.c:161
+#, c-format
+msgid "Missing PV segment on %s at %u."
+msgstr ""
+
+#: metadata/pv_manip.c:178
+#, c-format
+msgid "release_pv_segment with unallocated segment: %s PE %u"
+msgstr ""
+
+#: metadata/pv_manip.c:238
+#, c-format
+msgid "%s %u: %6u %6u: %s(%u:%u)"
+msgstr ""
+
+#: metadata/pv_manip.c:244
+#, c-format
+msgid "Gap in pvsegs: %u, %u"
+msgstr ""
+
+#: metadata/pv_manip.c:250
+msgid "Wrong lvseg area type"
+msgstr ""
+
+#: metadata/pv_manip.c:254
+msgid "Inconsistent pvseg pointers"
+msgstr ""
+
+#: metadata/pv_manip.c:258
+#, c-format
+msgid "Inconsistent length: %u %u"
+msgstr ""
+
+#: metadata/pv_manip.c:269
+#, c-format
+msgid "PV segment pe_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:275
+#, c-format
+msgid "PV segment pe_alloc_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:285
+#, c-format
+msgid "PV segment VG pv_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:291
+#, c-format
+msgid "PV segment VG free_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:297
+#, c-format
+msgid "PV segment VG extent_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:311
+#, c-format
+msgid "%s: cannot resize to %u extents as %u are allocated."
+msgstr ""
+
+#: metadata/pv_manip.c:324
+#, c-format
+msgid "%s: cannot resize to %u extents as later ones are allocated."
+msgstr ""
+
+#: metadata/pv_manip.c:356
+#, c-format
+msgid "%s: cannot resize to %u extents as there is only room for %lu."
+msgstr ""
+
+#: metadata/pv_manip.c:385
+#, c-format
+msgid "No change to size of physical volume %s."
+msgstr ""
+
+#: metadata/pv_manip.c:390
+#, c-format
+msgid "Resizing physical volume %s from %u to %u extents."
+msgstr ""
+
+#: metadata/pv_map.c:48
+#, c-format
+msgid "Allowing allocation on %s start PE %u length %u"
+msgstr ""
+
+#: metadata/pv_map.c:176
+msgid "create_pv_maps alloc failed"
+msgstr ""
+
+#: metadata/pv_map.c:183
+#, c-format
+msgid "Couldn't create physical volume maps in %s"
+msgstr ""
+
+#: metadata/segtype.c:30
+#, c-format
+msgid "Unrecognised segment type %s"
+msgstr ""
+
+#: metadata/snapshot_manip.c:63
+#, c-format
+msgid "'%s' is already in use as a snapshot."
+msgstr ""
+
+#: metadata/snapshot_manip.c:104
+#, c-format
+msgid "Failed to remove internal snapshot LV %s"
+msgstr ""
+
+#: mirror/mirrored.c:57
+#, c-format
+msgid "  Mirrors\t\t%u"
+msgstr ""
+
+#: mirror/mirrored.c:58
+#, c-format
+msgid "  Mirror size\t\t%u"
+msgstr ""
+
+#: mirror/mirrored.c:60
+#, c-format
+msgid "  Mirror log volume\t%s"
+msgstr ""
+
+#: mirror/mirrored.c:65
+#, c-format
+msgid "  Mirror region size\t%s"
+msgstr ""
+
+#: mirror/mirrored.c:68
+msgid "  Mirror original:"
+msgstr ""
+
+#: mirror/mirrored.c:70
+msgid "  Mirror destinations:"
+msgstr ""
+
+#: mirror/mirrored.c:79
+#, c-format
+msgid "Couldn't read 'mirror_count' for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:98
+#, c-format
+msgid "Couldn't read 'extents_moved' for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:107
+#, c-format
+msgid "Couldn't read 'region_size' for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:115
+msgid "Mirror log type must be a string."
+msgstr ""
+
+#: mirror/mirrored.c:120
+#, c-format
+msgid "Unrecognised mirror log in segment %s."
+msgstr ""
+
+#: mirror/mirrored.c:128
+#, c-format
+msgid "Missing region size for mirror log for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:134
+#, c-format
+msgid "Couldn't find mirrors array for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:163
+msgid "struct mirr_state allocation failed"
+msgstr ""
+
+#: mirror/mirrored.c:193
+#, c-format
+msgid "Mirror status: %s"
+msgstr ""
+
+#: mirror/mirrored.c:196
+#, c-format
+msgid "Failure parsing mirror status mirror count: %s"
+msgstr ""
+
+#: mirror/mirrored.c:204
+#, c-format
+msgid "Failure parsing mirror status devices: %s"
+msgstr ""
+
+#: mirror/mirrored.c:213
+#, c-format
+msgid "Failure parsing mirror status fraction: %s"
+msgstr ""
+
+#: mirror/mirrored.c:245
+#, c-format
+msgid "Failed to build uuid for log LV %s."
+msgstr ""
+
+#: mirror/mirrored.c:252
+#, c-format
+msgid "Failed to build uuid for mirror LV %s."
+msgstr ""
+
+#: mirror/mirrored.c:310
+msgid "Missing region size for mirror segment."
+msgstr ""
+
+#: mirror/mirrored.c:505
+msgid "cluster log string list allocation failed"
+msgstr ""
+
+#: mirror/mirrored.c:510
+msgid "mirror string list allocation failed"
+msgstr ""
+
+#: misc/lvm-exec.c:31
+#, c-format
+msgid "Executing: %s %s %s %s"
+msgstr ""
+
+#: misc/lvm-exec.c:34 polldaemon.c:39
+#, c-format
+msgid "fork failed: %s"
+msgstr ""
+
+#: misc/lvm-exec.c:48
+#, c-format
+msgid "wait4 child process %u failed: %s"
+msgstr ""
+
+#: misc/lvm-exec.c:54
+#, c-format
+msgid "Child %u exited abnormally"
+msgstr ""
+
+#: misc/lvm-exec.c:59
+#, c-format
+msgid "%s failed: %u"
+msgstr ""
+
+#: misc/lvm-file.c:55
+msgid "Not enough space to build temporary file string."
+msgstr ""
+
+#: misc/lvm-file.c:102
+#, c-format
+msgid "%s: rename to %s failed"
+msgstr ""
+
+#: misc/lvm-file.c:148
+#, c-format
+msgid "Creating directory \"%s\""
+msgstr ""
+
+#: misc/lvm-file.c:189
+#, c-format
+msgid "Directory \"%s\" not found"
+msgstr ""
+
+#: misc/lvm-file.c:220
+msgid "sync_dir failed in strdup"
+msgstr ""
+
+#: misc/lvm-file.c:269
+msgid "fcntl_lock_file failed in strdup."
+msgstr ""
+
+#: misc/lvm-file.c:283
+#, c-format
+msgid "Locking %s (%s, %hd)"
+msgstr ""
+
+#: misc/lvm-file.c:313
+#, c-format
+msgid "Unlocking fd %d"
+msgstr ""
+
+#: misc/lvm-file.c:316
+#, c-format
+msgid "fcntl unlock failed on fd %d: %s"
+msgstr ""
+
+#: misc/lvm-file.c:320
+#, c-format
+msgid "lock file close failed on fd %d: %s"
+msgstr ""
+
+#: misc/lvm-string.c:107
+#, c-format
+msgid "build_dm_name: Allocation failed for %zu for %s %s %s."
+msgstr ""
+
+#: misc/sharedlib.c:48
+#, c-format
+msgid "Not loading shared %s library %s in static mode."
+msgstr ""
+
+#: misc/sharedlib.c:55
+#, c-format
+msgid "Opening shared %s library %s"
+msgstr ""
+
+#: misc/sharedlib.c:59 misc/sharedlib.c:62
+#, c-format
+msgid "Unable to open external %s library %s: %s"
+msgstr ""
+
+#: mm/memlock.c:99
+msgid "Locking memory"
+msgstr ""
+
+#: mm/memlock.c:108 mm/memlock.c:122
+#, c-format
+msgid "setpriority %u failed: %s"
+msgstr ""
+
+#: mm/memlock.c:118
+msgid "Unlocking memory"
+msgstr ""
+
+#: mm/memlock.c:130
+#, c-format
+msgid "memlock_count inc to %d"
+msgstr ""
+
+#: mm/memlock.c:137
+#, c-format
+msgid "memlock_count dec to %d"
+msgstr ""
+
+#: polldaemon.c:34
+msgid "Forking background process"
+msgstr ""
+
+#: polldaemon.c:49
+#, c-format
+msgid "Background process failed to setsid: %s"
+msgstr ""
+
+#: polldaemon.c:80
+msgid "Failed to generate list of copied LVs: can't abort."
+msgstr ""
+
+#: polldaemon.c:90
+msgid "ABORTING: Mirror percentage check failed."
+msgstr ""
+
+#: polldaemon.c:96 polldaemon.c:98
+#, c-format
+msgid "%s: Moved: %.1f%%"
+msgstr ""
+
+#: polldaemon.c:107
+msgid "ABORTING: Failed to generate list of copied LVs"
+msgstr ""
+
+#: polldaemon.c:119
+msgid "ABORTING: Segment progression failed."
+msgstr ""
+
+#: polldaemon.c:149
+#, c-format
+msgid "ABORTING: Can't reread VG for %s"
+msgstr ""
+
+#: polldaemon.c:156
+#, c-format
+msgid "ABORTING: Can't find mirror LV in %s for %s"
+msgstr ""
+
+#: polldaemon.c:184
+#, c-format
+msgid "Couldn't read volume group %s"
+msgstr ""
+
+#: polldaemon.c:189
+#, c-format
+msgid "Volume Group %s inconsistent - skipping"
+msgstr ""
+
+#: polldaemon.c:241
+#, c-format
+msgid "Checking progress every %u seconds"
+msgstr ""
+
+#: pvchange.c:55
+#, c-format
+msgid "Finding volume group of physical volume \"%s\""
+msgstr ""
+
+#: pvchange.c:65 pvresize.c:75
+#, c-format
+msgid "Unable to find volume group of \"%s\""
+msgstr ""
+
+#: pvchange.c:90 pvresize.c:101
+#, c-format
+msgid "Unable to find \"%s\" in volume group \"%s\""
+msgstr ""
+
+#: pvchange.c:97
+#, c-format
+msgid "Volume group containing %s does not support tags"
+msgstr ""
+
+#: pvchange.c:103
+#, c-format
+msgid "Volume group containing %s has active logical volumes"
+msgstr ""
+
+#: pvchange.c:112
+#, c-format
+msgid "Can't change tag on Physical Volume %s not in volume group"
+msgstr ""
+
+#: pvchange.c:117 pvresize.c:48
+msgid "Can't get lock for orphans"
+msgstr ""
+
+#: pvchange.c:123 pvresize.c:54
+#, c-format
+msgid "Unable to read PV \"%s\""
+msgstr ""
+
+#: pvchange.c:132
+#, c-format
+msgid "Allocatability not supported by orphan %s format PV %s"
+msgstr ""
+
+#: pvchange.c:140
+#, c-format
+msgid "Physical volume \"%s\" is already allocatable"
+msgstr ""
+
+#: pvchange.c:150
+#, c-format
+msgid "Physical volume \"%s\" is already unallocatable"
+msgstr ""
+
+#: pvchange.c:160
+#, c-format
+msgid "Setting physical volume \"%s\" allocatable"
+msgstr ""
+
+#: pvchange.c:164
+#, c-format
+msgid "Setting physical volume \"%s\" NOT allocatable"
+msgstr ""
+
+#: pvchange.c:172
+#, c-format
+msgid "Failed to add tag %s to physical volume %s"
+msgstr ""
+
+#: pvchange.c:178
+#, c-format
+msgid "Failed to remove tag %s from physical volume%s"
+msgstr ""
+
+#: pvchange.c:186
+#, c-format
+msgid "Failed to generate new random UUID for %s."
+msgstr ""
+
+#: pvchange.c:194
+#, c-format
+msgid "Changing uuid of %s to %s."
+msgstr ""
+
+#: pvchange.c:201
+#, c-format
+msgid "pv_write with new uuid failed for %s."
+msgstr ""
+
+#: pvchange.c:210 pvresize.c:174
+#, c-format
+msgid "Updating physical volume \"%s\""
+msgstr ""
+
+#: pvchange.c:214 pvresize.c:178
+#, c-format
+msgid "Failed to store physical volume \"%s\" in volume group \"%s\""
+msgstr ""
+
+#: pvchange.c:223 pvresize.c:187
+#, c-format
+msgid "Failed to store physical volume \"%s\""
+msgstr ""
+
+#: pvchange.c:230 pvresize.c:194
+#, c-format
+msgid "Physical volume \"%s\" changed"
+msgstr ""
+
+#: pvchange.c:252
+msgid "Please give exactly one option of -x, -uuid, --addtag or --deltag"
+msgstr ""
+
+#: pvchange.c:258
+msgid "Please give a physical volume path"
+msgstr ""
+
+#: pvchange.c:263
+msgid "Option a and PhysicalVolumePath are exclusive"
+msgstr ""
+
+#: pvchange.c:268 toollib.c:683
+msgid "Using physical volume(s) on command line"
+msgstr ""
+
+#: pvchange.c:273
+#, c-format
+msgid "Failed to read physical volume %s"
+msgstr ""
+
+#: pvchange.c:281 toollib.c:766
+msgid "Scanning for physical volume names"
+msgstr ""
+
+#: pvchange.c:292
+#, c-format
+msgid "%d physical volume%s changed / %d physical volume%s not changed"
+msgstr ""
+
+#: pvck.c:32
+#, c-format
+msgid "Scanning %s"
+msgstr ""
+
+#: pvcreate.c:37 pvremove.c:31
+#, c-format
+msgid "%s: Not LVM partition type: use -f to override"
+msgstr ""
+
+#: pvcreate.c:49
+#, c-format
+msgid ""
+"Can't initialize physical volume \"%s\" of volume group \"%s\" without -ff"
+msgstr ""
+
+#: pvcreate.c:57
+#, c-format
+msgid "%s: physical volume not initialized"
+msgstr ""
+
+#: pvcreate.c:72 pvcreate.c:168 pvremove.c:81 vgcreate.c:135 vgextend.c:40
+#: vgremove.c:96
+msgid "Can't get lock for orphan PVs"
+msgstr ""
+
+#: pvcreate.c:86
+#, c-format
+msgid "Can't open %s exclusively.  Mounted filesystem?"
+msgstr ""
+
+#: pvcreate.c:98
+#, c-format
+msgid "Wiping software RAID md superblock on %s"
+msgstr ""
+
+#: pvcreate.c:100
+#, c-format
+msgid "Failed to wipe RAID md superblock on %s"
+msgstr ""
+
+#: pvcreate.c:107
+#, c-format
+msgid "WARNING: Forcing physical volume creation on %s%s%s%s"
+msgstr ""
+
+#: pvcreate.c:140
+#, c-format
+msgid "uuid %s already in use on \"%s\""
+msgstr ""
+
+#: pvcreate.c:152
+#, c-format
+msgid "Unable to read volume group from %s"
+msgstr ""
+
+#: pvcreate.c:158
+#, c-format
+msgid "Can't find uuid %s in backup file %s"
+msgstr ""
+
+#: pvcreate.c:176 pvresize.c:212
+msgid "Physical volume size may not be negative"
+msgstr ""
+
+#: pvcreate.c:182 vgconvert.c:66
+msgid "Metadata size may not be negative"
+msgstr ""
+
+#: pvcreate.c:199 pvremove.c:89
+#, c-format
+msgid "%s: Couldn't find device.  Check your filters?"
+msgstr ""
+
+#: pvcreate.c:208 vgconvert.c:127
+#, c-format
+msgid "Failed to setup physical volume \"%s\""
+msgstr ""
+
+#: pvcreate.c:212 vgconvert.c:138
+#, c-format
+msgid "Set up physical volume for \"%s\" with %lu available sectors"
+msgstr ""
+
+#: pvcreate.c:217 vgconvert.c:143
+#, c-format
+msgid "Failed to wipe existing label on %s"
+msgstr ""
+
+#: pvcreate.c:222
+#, c-format
+msgid "Zeroing start of device %s"
+msgstr ""
+
+#: pvcreate.c:224
+#, c-format
+msgid "%s not opened: device not zeroed"
+msgstr ""
+
+#: pvcreate.c:229
+#, c-format
+msgid "%s not wiped: aborting"
+msgstr ""
+
+#: pvcreate.c:236 vgconvert.c:150
+#, c-format
+msgid "Writing physical volume data to disk \"%s\""
+msgstr ""
+
+#: pvcreate.c:240 vgconvert.c:155
+#, c-format
+msgid "Failed to write physical volume \"%s\""
+msgstr ""
+
+#: pvcreate.c:244 vgconvert.c:161
+#, c-format
+msgid "Physical volume \"%s\" successfully created"
+msgstr ""
+
+#: pvcreate.c:261 pvremove.c:123
+msgid "Please enter a physical volume path"
+msgstr ""
+
+#: pvcreate.c:266
+msgid "--uuid is required with --restorefile"
+msgstr ""
+
+#: pvcreate.c:271
+msgid "Can only set uuid on one volume at once"
+msgstr ""
+
+#: pvcreate.c:276 pvremove.c:128
+msgid "Option y can only be given with option f"
+msgstr ""
+
+#: pvcreate.c:281 vgconvert.c:205
+#, c-format
+msgid "labelsector must be less than %lu"
+msgstr ""
+
+#: pvcreate.c:289 vgconvert.c:213
+msgid "Metadata parameters only apply to text format"
+msgstr ""
+
+#: pvcreate.c:295 vgconvert.c:219
+msgid "Metadatacopies may only be 0, 1 or 2"
+msgstr ""
+
+#: pvdisplay.c:30 reporter.c:65 reporter.c:113 toollib.c:347 toollib.c:477
+#, c-format
+msgid "Can't lock %s: skipping"
+msgstr ""
+
+#: pvdisplay.c:35 reporter.c:70 reporter.c:118
+#, c-format
+msgid "Can't read %s: skipping"
+msgstr ""
+
+#: pvdisplay.c:54
+#, c-format
+msgid "Device \"%s\" has a capacity of %s"
+msgstr ""
+
+#: pvdisplay.c:60
+#, c-format
+msgid "Physical volume \"%s\" of volume group \"%s\" is exported"
+msgstr ""
+
+#: pvdisplay.c:64
+#, c-format
+msgid "\"%s\" is a new physical volume of \"%s\""
+msgstr ""
+
+#: pvdisplay.c:104
+msgid "Option -v not allowed with option -c"
+msgstr ""
+
+#: pvmove.c:34
+msgid "--name takes a logical volume name"
+msgstr ""
+
+#: pvmove.c:39
+msgid "Named LV and old PV must be in the same VG"
+msgstr ""
+
+#: pvmove.c:45
+msgid "Incomplete LV name supplied with --name"
+msgstr ""
+
+#: pvmove.c:127
+msgid "No extents available for allocation"
+msgstr ""
+
+#: pvmove.c:150
+msgid "Creation of temporary pvmove LV failed"
+msgstr ""
+
+#: pvmove.c:157
+msgid "lvs_changed list struct allocation failed"
+msgstr ""
+
+#: pvmove.c:170
+#, c-format
+msgid "Skipping snapshot-related LV %s"
+msgstr ""
+
+#: pvmove.c:174
+#, c-format
+msgid "Skipping mirror LV %s"
+msgstr ""
+
+#: pvmove.c:178
+#, c-format
+msgid "Skipping mirror log LV %s"
+msgstr ""
+
+#: pvmove.c:182
+#, c-format
+msgid "Skipping mirror image LV %s"
+msgstr ""
+
+#: pvmove.c:186
+#, c-format
+msgid "Skipping locked LV %s"
+msgstr ""
+
+#: pvmove.c:199
+#, c-format
+msgid "No data to move for %s"
+msgstr ""
+
+#: pvmove.c:210
+msgid "Updating volume group metadata"
+msgstr ""
+
+#: pvmove.c:212 pvmove.c:236
+msgid "ABORTING: Volume group metadata update failed."
+msgstr ""
+
+#: pvmove.c:249
+msgid "ABORTING: Temporary mirror activation failed.  Run pvmove --abort."
+msgstr ""
+
+#: pvmove.c:257 pvmove.c:438
+#, c-format
+msgid "Unable to reactivate logical volume \"%s\""
+msgstr ""
+
+#: pvmove.c:265
+msgid "Unable to resume logical volumes"
+msgstr ""
+
+#: pvmove.c:313
+#, c-format
+msgid "Detected pvmove in progress for %s"
+msgstr ""
+
+#: pvmove.c:315
+msgid "Ignoring remaining command line arguments"
+msgstr ""
+
+#: pvmove.c:318
+msgid "ABORTING: Failed to generate list of moving LVs"
+msgstr ""
+
+#: pvmove.c:326
+msgid "ABORTING: Temporary mirror activation failed."
+msgstr ""
+
+#: pvmove.c:403
+msgid "ABORTING: Removal of temporary mirror failed"
+msgstr ""
+
+#: pvmove.c:409 pvmove.c:428 pvmove.c:462
+msgid "ABORTING: Failed to write new data locations to disk."
+msgstr ""
+
+#: pvmove.c:416
+msgid "Locking LVs to remove temporary mirror failed"
+msgstr ""
+
+#: pvmove.c:422
+msgid "Suspension of temporary mirror LV failed"
+msgstr ""
+
+#: pvmove.c:448
+#, c-format
+msgid "ABORTING: Unable to deactivate temporary logical volume \"%s\""
+msgstr ""
+
+#: pvmove.c:453
+msgid "Removing temporary pvmove LV"
+msgstr ""
+
+#: pvmove.c:455
+msgid "ABORTING: Removal of temporary pvmove LV failed"
+msgstr ""
+
+#: pvmove.c:460
+msgid "Writing out final volume group after pvmove"
+msgstr ""
+
+#: pvmove.c:480
+#, c-format
+msgid "ABORTING: Can't reread PV %s"
+msgstr ""
+
+#: pvmove.c:516 toollib.c:1074
+msgid "Failed to clone PV name"
+msgstr ""
+
+#: pvremove.c:41 vgsplit.c:107
+#, c-format
+msgid "Physical Volume %s not found"
+msgstr ""
+
+#: pvremove.c:52
+#, c-format
+msgid ""
+"Can't pvremove physical volume \"%s\" of volume group \"%s\" without -ff"
+msgstr ""
+
+#: pvremove.c:60
+#, c-format
+msgid "%s: physical volume label not removed"
+msgstr ""
+
+#: pvremove.c:65
+#, c-format
+msgid "WARNING: Wiping physical volume label from %s%s%s%s"
+msgstr ""
+
+#: pvremove.c:95
+#, c-format
+msgid "Can't open %s exclusively - not removing. Mounted filesystem?"
+msgstr ""
+
+#: pvremove.c:102
+#, c-format
+msgid "Failed to wipe existing label(s) on %s"
+msgstr ""
+
+#: pvremove.c:106
+#, c-format
+msgid "Labels on physical volume \"%s\" successfully wiped"
+msgstr ""
+
+#: pvresize.c:60
+#, c-format
+msgid "%s: too many metadata areas for pvresize"
+msgstr ""
+
+#: pvresize.c:113
+#, c-format
+msgid "Physical volume %s format does not support resizing."
+msgstr ""
+
+#: pvresize.c:130
+#, c-format
+msgid "%s: Pretending size is %lu not %lu sectors."
+msgstr ""
+
+#: pvresize.c:143
+#, c-format
+msgid "%s: Size must exceed physical extent start of %lu sectors."
+msgstr ""
+
+#: pvresize.c:156
+#, c-format
+msgid ""
+"%s: Size must leave space for at least one physical extent of %u sectors."
+msgstr ""
+
+#: pvresize.c:171
+#, c-format
+msgid "Resizing volume \"%s\" to %lu sectors."
+msgstr ""
+
+#: pvresize.c:207
+msgid "Please supply physical volume(s)"
+msgstr ""
+
+#: pvresize.c:224
+#, c-format
+msgid "%d physical volume(s) resized / %d physical volume(s) not resized"
+msgstr ""
+
+#: pvscan.c:66
+#, c-format
+msgid "PV %-*s    %-*s %s [%s]"
+msgstr ""
+
+#: pvscan.c:76
+#, c-format
+msgid "PV %-*s  is in exported VG %s [%s / %s free]"
+msgstr ""
+
+#: pvscan.c:89
+#, c-format
+msgid "PV %-*s VG %-*s %s [%s / %s free]"
+msgstr ""
+
+#: pvscan.c:117
+msgid "Options -e and -n are incompatible"
+msgstr ""
+
+#: pvscan.c:122
+#, c-format
+msgid "WARNING: only considering physical volumes %s"
+msgstr ""
+
+#: pvscan.c:129
+msgid "Walking through all physical volumes"
+msgstr ""
+
+#: pvscan.c:182
+msgid "No matching physical volumes found"
+msgstr ""
+
+#: pvscan.c:186
+#, c-format
+msgid "Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]"
+msgstr ""
+
+#: report/report.c:118
+msgid "Extent number dm_snprintf failed"
+msgstr ""
+
+#: report/report.c:182
+msgid "modules str_list allocation failed"
+msgstr ""
+
+#: report/report.c:259 report/report.c:342 report/report.c:368
+#: report/report.c:466 report/report.c:523 report/report.c:553
+#: report/report.c:694 report/report.c:750 report/report.c:768
+#: report/report.c:793 report/report.c:807
+msgid "dm_pool_alloc failed"
+msgstr ""
+
+#: report/report.c:471
+msgid "lvname snprintf failed"
+msgstr ""
+
+#: report/report.c:476 report/report.c:518 report/report.c:548
+msgid "dm_pool_strdup failed"
+msgstr ""
+
+#: report/report.c:773
+msgid "snapshot percentage too large"
+msgstr ""
+
+#: report/report.c:812
+msgid "copy percentage too large"
+msgstr ""
+
+#: reporter.c:24 reporter.c:146 reporter.c:158
+#, c-format
+msgid "Volume group %s not found"
+msgstr ""
+
+#: reporter.c:254
+#, c-format
+msgid "Invalid options string: %s"
+msgstr ""
+
+#: reporter.c:260
+msgid "options string allocation failed"
+msgstr ""
+
+#: reporter.c:297
+msgid "Can't report LV and PV fields at the same time"
+msgstr ""
+
+#: snapshot/snapshot.c:40
+msgid "Couldn't read chunk size for snapshot."
+msgstr ""
+
+#: snapshot/snapshot.c:48
+msgid "Snapshot cow storage not specified."
+msgstr ""
+
+#: snapshot/snapshot.c:54
+msgid "Snapshot origin not specified."
+msgstr ""
+
+#: snapshot/snapshot.c:61
+msgid "Unknown logical volume specified for snapshot cow store."
+msgstr ""
+
+#: snapshot/snapshot.c:67
+msgid "Unknown logical volume specified for snapshot origin."
+msgstr ""
+
+#: snapshot/snapshot.c:135
+msgid "snapshot string list allocation failed"
+msgstr ""
+
+#: striped/striped.c:41
+#, c-format
+msgid "  Stripes\t\t%u"
+msgstr ""
+
+#: striped/striped.c:42
+#, c-format
+msgid "  Stripe size\t\t%u KB"
+msgstr ""
+
+#: striped/striped.c:45
+#, c-format
+msgid "  Stripe %d:"
+msgstr ""
+
+#: striped/striped.c:55
+#, c-format
+msgid "Couldn't read 'stripe_count' for segment '%s'."
+msgstr ""
+
+#: striped/striped.c:70
+#, c-format
+msgid "Couldn't read stripe_size for segment '%s'."
+msgstr ""
+
+#: striped/striped.c:76
+#, c-format
+msgid "Couldn't find stripes array for segment '%s'."
+msgstr ""
+
+#: striped/striped.c:163
+#, c-format
+msgid "Internal error: striped add_target_line called with no areas for %s."
+msgstr ""
+
+#: stub.h:24 stub.h:31
+msgid "Command not implemented yet."
+msgstr ""
+
+#: stub.h:38
+msgid "There's no 'pvdata' command in LVM2."
+msgstr ""
+
+#: stub.h:39
+msgid ""
+"Use lvs, pvs, vgs instead; or use vgcfgbackup and read the text file backup."
+msgstr ""
+
+#: stub.h:40
+msgid ""
+"Metadata in LVM1 format can still be displayed using LVM1's pvdata command."
+msgstr ""
+
+#: toollib.c:115
+#, c-format
+msgid "skip_dev_dir: Couldn't split up device name %s"
+msgstr ""
+
+#: toollib.c:124 toollib.c:322
+msgid "vg/lv string alloc failed"
+msgstr ""
+
+#: toollib.c:215
+msgid "One or more specified logical volume(s) not found."
+msgstr ""
+
+#: toollib.c:251
+msgid "Using logical volume(s) on command line"
+msgstr ""
+
+#: toollib.c:264 toollib.c:540 toollib.c:689 toollib.c:1051
+#, c-format
+msgid "Skipping invalid tag %s"
+msgstr ""
+
+#: toollib.c:281 toollib.c:807 toollib.c:818
+#, c-format
+msgid "\"%s\": Invalid path for Logical Volume"
+msgstr ""
+
+#: toollib.c:335
+msgid "Finding all logical volumes"
+msgstr ""
+
+#: toollib.c:337 toollib.c:572
+msgid "No volume groups found"
+msgstr ""
+
+#: toollib.c:357 toollib.c:483 toollib.c:731 vgcfgbackup.c:59 vgck.c:24
+#: vgreduce.c:505 vgscan.c:23
+#, c-format
+msgid "Volume group \"%s\" not found"
+msgstr ""
+
+#: toollib.c:369 vgchange.c:523 vgck.c:29 vgconvert.c:43 vgscan.c:30
+#, c-format
+msgid "Volume group \"%s\" inconsistent"
+msgstr ""
+
+#: toollib.c:534
+msgid "Using volume group(s) on command line"
+msgstr ""
+
+#: toollib.c:555
+#, c-format
+msgid "Invalid volume group name: %s"
+msgstr ""
+
+#: toollib.c:570
+msgid "Finding all volume groups"
+msgstr ""
+
+#: toollib.c:705 toollib.c:1080
+#, c-format
+msgid "Physical Volume \"%s\" not found in Volume Group \"%s\""
+msgstr ""
+
+#: toollib.c:716
+#, c-format
+msgid "Failed to read physical volume \"%s\""
+msgstr ""
+
+#: toollib.c:755
+msgid "Using all physical volume(s) in volume group"
+msgstr ""
+
+#: toollib.c:825
+msgid "Allocation of vg_name failed"
+msgstr ""
+
+#: toollib.c:835
+#, c-format
+msgid "Path required for Logical Volume \"%s\""
+msgstr ""
+
+#: toollib.c:858
+#, c-format
+msgid "Environment Volume Group in LVM_VG_NAME invalid: \"%s\""
+msgstr ""
+
+#: toollib.c:874
+#, c-format
+msgid "Adding PE range: start PE %u length %u on %s"
+msgstr ""
+
+#: toollib.c:882
+#, c-format
+msgid "Overlapping PE ranges specified (%u-%u, %u-%u) on %s"
+msgstr ""
+
+#: toollib.c:892 toollib.c:1039 toollib.c:1103
+msgid "Allocation of list failed"
+msgstr ""
+
+#: toollib.c:956
+#, c-format
+msgid "PE range error: start extent %u to end extent %u"
+msgstr ""
+
+#: toollib.c:971
+#, c-format
+msgid "Physical extent parsing error at %s"
+msgstr ""
+
+#: toollib.c:984
+#, c-format
+msgid "Physical volume %s not allocatable"
+msgstr ""
+
+#: toollib.c:990
+#, c-format
+msgid "No free extents on physical volume \"%s\""
+msgstr ""
+
+#: toollib.c:1002 toollib.c:1110
+msgid "Unable to allocate physical volume list."
+msgstr ""
+
+#: toollib.c:1009
+msgid "Allocation of pe_ranges list failed"
+msgstr ""
+
+#: toollib.c:1091
+msgid "No specified PVs have space available"
+msgstr ""
+
+#: toollib.c:1137
+#, c-format
+msgid "Can't lock %s for metadata recovery: skipping"
+msgstr ""
+
+#: toollib.c:1148
+msgid ""
+"Names starting \"snapshot\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1154
+msgid ""
+"Names starting \"pvmove\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1160
+msgid ""
+"Names including \"_mlog\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1166
+msgid ""
+"Names including \"_mimage\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1183
+#, c-format
+msgid "%s: already exists in filesystem"
+msgstr ""
+
+#: toollib.c:1227
+msgid "Name allocation failed - device not cleared"
+msgstr ""
+
+#: toollib.c:1233
+#, c-format
+msgid "Name too long - device not cleared (%s)"
+msgstr ""
+
+#: toollib.c:1237
+#, c-format
+msgid "Clearing start of logical volume \"%s\""
+msgstr ""
+
+#: toollib.c:1240
+#, c-format
+msgid "%s: not found: device not cleared"
+msgstr ""
+
+#: toollib.c:1276
+#, c-format
+msgid "Name allocation failed - log header not written (%s)"
+msgstr ""
+
+#: toollib.c:1283
+#, c-format
+msgid "Name too long - log header not written (%s)"
+msgstr ""
+
+#: toollib.c:1287
+#, c-format
+msgid "Writing log header to device, %s"
+msgstr ""
+
+#: toollib.c:1290
+#, c-format
+msgid "%s: not found: log header not written"
+msgstr ""
+
+#: toollib.c:1298
+#, c-format
+msgid "Failed to write log header to %s"
+msgstr ""
+
+#: toollib.c:1324
+msgid "log_name allocation failed. Remove new LV and retry."
+msgstr ""
+
+#: toollib.c:1344
+msgid "Aborting. Unable to tag mirror log."
+msgstr ""
+
+#: toollib.c:1362
+msgid ""
+"Aborting. Unable to create in-sync mirror log while activation is disabled."
+msgstr ""
+
+#: toollib.c:1368
+msgid "Aborting. Failed to activate mirror log. Remove new LVs and retry."
+msgstr ""
+
+#: toollib.c:1375
+#, c-format
+msgid "Failed to remove tag %s from mirror log."
+msgstr ""
+
+#: toollib.c:1380
+msgid "Aborting. Failed to wipe mirror log. Remove new LV and retry."
+msgstr ""
+
+#: toollib.c:1386
+msgid "Aborting. Failed to write mirror log header. Remove new LV and retry."
+msgstr ""
+
+#: toollib.c:1392
+msgid "Aborting. Failed to deactivate mirror log. Remove new LV and retry."
+msgstr ""
+
+#: uuid/uuid.c:132
+msgid "UUID contains invalid character"
+msgstr ""
+
+#: uuid/uuid.c:156
+msgid "Couldn't write uuid, buffer too small."
+msgstr ""
+
+#: uuid/uuid.c:184
+msgid "Too many characters to be uuid."
+msgstr ""
+
+#: uuid/uuid.c:192
+msgid "Couldn't read uuid, incorrect number of characters."
+msgstr ""
+
+#: vgcfgbackup.c:27
+msgid "Failed to allocate filename."
+msgstr ""
+
+#: vgcfgbackup.c:32
+#, c-format
+msgid "Error processing filename template %s"
+msgstr ""
+
+#: vgcfgbackup.c:39
+#, c-format
+msgid ""
+"VGs must be backed up into different files. Use %%s in filename for VG name."
+msgstr ""
+
+#: vgcfgbackup.c:64
+#, c-format
+msgid "Warning: Volume group \"%s\" inconsistent"
+msgstr ""
+
+#: vgcfgbackup.c:76
+msgid "No backup taken: specify filename with -f to backup an inconsistent VG"
+msgstr ""
+
+#: vgcfgbackup.c:90
+#, c-format
+msgid "Volume group \"%s\" successfully backed up."
+msgstr ""
+
+#: vgcfgrestore.c:23
+msgid "Please specify a *single* volume group to restore."
+msgstr ""
+
+#: vgcfgrestore.c:30 vgextend.c:45 vgreduce.c:469 vgsplit.c:228
+#, c-format
+msgid "Volume group name \"%s\" is invalid"
+msgstr ""
+
+#: vgcfgrestore.c:46
+msgid "Unable to lock orphans"
+msgstr ""
+
+#: vgcfgrestore.c:51
+#, c-format
+msgid "Unable to lock volume group %s"
+msgstr ""
+
+#: vgcfgrestore.c:62
+msgid "Restore failed."
+msgstr ""
+
+#: vgcfgrestore.c:66
+#, c-format
+msgid "Restored volume group %s"
+msgstr ""
+
+#: vgchange.c:92
+#, c-format
+msgid "Spawning background process for %s %s"
+msgstr ""
+
+#: vgchange.c:111
+#, c-format
+msgid "%d logical volume(s) in volume group \"%s\" %smonitored"
+msgstr ""
+
+#: vgchange.c:132
+#, c-format
+msgid "Can't deactivate volume group \"%s\" with %d open logical volume(s)"
+msgstr ""
+
+#: vgchange.c:138
+#, c-format
+msgid "Locking inactive: ignoring clustered volume group %s"
+msgstr ""
+
+#: vgchange.c:148
+#, c-format
+msgid "%d logical volume(s) in volume group \"%s\" already active"
+msgstr ""
+
+#: vgchange.c:152
+#, c-format
+msgid "%d existing logical volume(s) in volume group \"%s\" %smonitored"
+msgstr ""
+
+#: vgchange.c:160
+#, c-format
+msgid "Activated logical volumes in volume group \"%s\""
+msgstr ""
+
+#: vgchange.c:164
+#, c-format
+msgid "Deactivated logical volumes in volume group \"%s\""
+msgstr ""
+
+#: vgchange.c:167
+#, c-format
+msgid "%d logical volume(s) in volume group \"%s\" now active"
+msgstr ""
+
+#: vgchange.c:179 vgcreate.c:47
+msgid "Volume Group allocation policy cannot inherit from anything"
+msgstr ""
+
+#: vgchange.c:185
+#, c-format
+msgid "Volume group allocation policy is already %s"
+msgstr ""
+
+#: vgchange.c:200 vgchange.c:235 vgchange.c:282 vgchange.c:324 vgchange.c:371
+#: vgchange.c:429 vgchange.c:471 vgchange.c:504
+#, c-format
+msgid "Volume group \"%s\" successfully changed"
+msgstr ""
+
+#: vgchange.c:211
+#, c-format
+msgid "Volume group \"%s\" is already resizeable"
+msgstr ""
+
+#: vgchange.c:217
+#, c-format
+msgid "Volume group \"%s\" is already not resizeable"
+msgstr ""
+
+#: vgchange.c:247
+#, c-format
+msgid "Volume group \"%s\" is already clustered"
+msgstr ""
+
+#: vgchange.c:253
+#, c-format
+msgid "Volume group \"%s\" is already not clustered"
+msgstr ""
+
+#: vgchange.c:261
+#, c-format
+msgid "Volume group %s contains snapshots that are not yet supported."
+msgstr ""
+
+#: vgchange.c:293
+#, c-format
+msgid "Volume group \"%s\" must be resizeable to change MaxLogicalVolume"
+msgstr ""
+
+#: vgchange.c:302
+msgid "MaxLogicalVolume limit is 255"
+msgstr ""
+
+#: vgchange.c:308
+#, c-format
+msgid "MaxLogicalVolume is less than the current number %d of LVs for \"%s\""
+msgstr ""
+
+#: vgchange.c:335
+#, c-format
+msgid "Volume group \"%s\" must be resizeable to change MaxPhysicalVolumes"
+msgstr ""
+
+#: vgchange.c:341
+msgid "MaxPhysicalVolumes may not be negative"
+msgstr ""
+
+#: vgchange.c:349
+msgid "MaxPhysicalVolume limit is 255"
+msgstr ""
+
+#: vgchange.c:355
+#, c-format
+msgid "MaxPhysicalVolumes is less than the current number %d of PVs for \"%s\""
+msgstr ""
+
+#: vgchange.c:381
+#, c-format
+msgid "Volume group \"%s\" must be resizeable to change PE size"
+msgstr ""
+
+#: vgchange.c:387 vgcreate.c:64
+msgid "Physical extent size may not be negative"
+msgstr ""
+
+#: vgchange.c:393 vgcreate.c:83
+msgid "Physical extent size may not be zero"
+msgstr ""
+
+#: vgchange.c:398
+#, c-format
+msgid "Physical extent size of VG %s is already %s"
+msgstr ""
+
+#: vgchange.c:404
+msgid "Physical extent size must be a power of 2."
+msgstr ""
+
+#: vgchange.c:411
+msgid "New extent size is not a perfect fit"
+msgstr ""
+
+#: vgchange.c:454 vgcreate.c:117
+#, c-format
+msgid "Failed to add tag %s to volume group %s"
+msgstr ""
+
+#: vgchange.c:460
+#, c-format
+msgid "Failed to remove tag %s from volume group %s"
+msgstr ""
+
+#: vgchange.c:482
+msgid "Volume group has active logical volumes"
+msgstr ""
+
+#: vgchange.c:490
+#, c-format
+msgid "Failed to generate new random UUID for VG %s."
+msgstr ""
+
+#: vgchange.c:516 vgconvert.c:36 vgexport.c:27
+#, c-format
+msgid "Unable to find volume group \"%s\""
+msgstr ""
+
+#: vgchange.c:588
+msgid ""
+"One of -a, -c, -l, -p, -s, -x, --uuid, --alloc, --addtag or --deltag required"
+msgstr ""
+
+#: vgchange.c:600
+msgid ""
+"Only one of -a, -c, -l, -p, -s, -x, --uuid, --alloc, --addtag or --deltag "
+"allowed"
+msgstr ""
+
+#: vgchange.c:607
+msgid "--ignorelockingfailure only available with -a"
+msgstr ""
+
+#: vgchange.c:613
+msgid "-A option not necessary with -a option"
+msgstr ""
+
+#: vgconvert.c:59
+#, c-format
+msgid "Volume group \"%s\" already uses format %s"
+msgstr ""
+
+#: vgconvert.c:87
+#, c-format
+msgid "Archive of \"%s\" metadata failed."
+msgstr ""
+
+#: vgconvert.c:100
+#, c-format
+msgid "Logical volume %s must be deactivated before conversion."
+msgstr ""
+
+#: vgconvert.c:130 vgconvert.c:145 vgconvert.c:157 vgconvert.c:170
+#: vgconvert.c:186
+msgid "Use pvcreate and vgcfgrestore to repair from archived metadata."
+msgstr ""
+
+#: vgconvert.c:166
+#, c-format
+msgid "Deleting existing metadata for VG %s"
+msgstr ""
+
+#: vgconvert.c:168
+#, c-format
+msgid "Removal of existing metadata for %s failed."
+msgstr ""
+
+#: vgconvert.c:177
+#, c-format
+msgid "Test mode: Skipping metadata writing for VG %s in format %s"
+msgstr ""
+
+#: vgconvert.c:182
+#, c-format
+msgid "Writing metadata for VG %s using format %s"
+msgstr ""
+
+#: vgconvert.c:185
+#, c-format
+msgid "Conversion failed for volume group %s."
+msgstr ""
+
+#: vgconvert.c:190
+#, c-format
+msgid "Volume group %s successfully converted"
+msgstr ""
+
+#: vgconvert.c:200
+msgid "Please enter volume group(s)"
+msgstr ""
+
+#: vgcreate.c:31
+msgid "Please provide volume group name and physical volumes"
+msgstr ""
+
+#: vgcreate.c:37
+msgid "Please enter physical volume name(s)"
+msgstr ""
+
+#: vgcreate.c:58
+msgid "Number of volumes may not exceed 255"
+msgstr ""
+
+#: vgcreate.c:69
+msgid "Max Logical Volumes may not be negative"
+msgstr ""
+
+#: vgcreate.c:74
+msgid "Max Physical Volumes may not be negative"
+msgstr ""
+
+#: vgcreate.c:88 vgrename.c:52 vgsplit.c:290
+#, c-format
+msgid "New volume group name \"%s\" is invalid"
+msgstr ""
+
+#: vgcreate.c:98
+#, c-format
+msgid "Warning: Setting maxlogicalvolumes to %d (0 means unlimited)"
+msgstr ""
+
+#: vgcreate.c:102
+#, c-format
+msgid "Warning: Setting maxphysicalvolumes to %d (0 means unlimited)"
+msgstr ""
+
+#: vgcreate.c:112
+msgid "Volume group format does not support tags"
+msgstr ""
+
+#: vgcreate.c:163
+#, c-format
+msgid "Volume group \"%s\" successfully created"
+msgstr ""
+
+#: vgdisplay.c:29
+#, c-format
+msgid "WARNING: Volume group \"%s\" inconsistent"
+msgstr ""
+
+#: vgdisplay.c:32
+#, c-format
+msgid "WARNING: volume group \"%s\" is exported"
+msgstr ""
+
+#: vgdisplay.c:52
+msgid "--- Physical volumes ---"
+msgstr ""
+
+#: vgdisplay.c:81
+msgid "Option -c is not allowed with option -s"
+msgstr ""
+
+#: vgdisplay.c:86
+msgid "Option -A is not allowed with volume group names"
+msgstr ""
+
+#: vgexport.c:32
+#, c-format
+msgid "Volume group %s inconsistent"
+msgstr ""
+
+#: vgexport.c:37
+#, c-format
+msgid "Volume group \"%s\" is already exported"
+msgstr ""
+
+#: vgexport.c:47
+#, c-format
+msgid "Volume group \"%s\" has active logical volumes"
+msgstr ""
+
+#: vgexport.c:67
+#, c-format
+msgid "Volume group \"%s\" successfully exported"
+msgstr ""
+
+#: vgexport.c:78 vgimport.c:68
+msgid "Please supply volume groups or use -a for all."
+msgstr ""
+
+#: vgexport.c:83 vgimport.c:73
+msgid "No arguments permitted when using -a for all."
+msgstr ""
+
+#: vgextend.c:25
+msgid "Please enter volume group name and physical volume(s)"
+msgstr ""
+
+#: vgextend.c:31
+msgid "Please enter physical volume(s)"
+msgstr ""
+
+#: vgextend.c:50 vgmerge.c:32 vgmerge.c:63 vgsplit.c:238 vgsplit.c:275
+#, c-format
+msgid "Checking for volume group \"%s\""
+msgstr ""
+
+#: vgextend.c:58
+#, c-format
+msgid "Volume group \"%s\" not found."
+msgstr ""
+
+#: vgextend.c:79
+#, c-format
+msgid "Volume group \"%s\" is not resizeable."
+msgstr ""
+
+#: vgextend.c:98
+#, c-format
+msgid "Volume group \"%s\" will be extended by %d new physical volumes"
+msgstr ""
+
+#: vgextend.c:110
+#, c-format
+msgid "Volume group \"%s\" successfully extended"
+msgstr ""
+
+#: vgimport.c:27
+#, c-format
+msgid "Unable to find exported volume group \"%s\""
+msgstr ""
+
+#: vgimport.c:33
+#, c-format
+msgid "Volume group \"%s\" is not exported"
+msgstr ""
+
+#: vgimport.c:38
+#, c-format
+msgid "Volume group \"%s\" is partially missing"
+msgstr ""
+
+#: vgimport.c:57
+#, c-format
+msgid "Volume group \"%s\" successfully imported"
+msgstr ""
+
+#: vgmerge.c:28 vgsplit.c:234
+#, c-format
+msgid "Duplicate volume group name \"%s\""
+msgstr ""
+
+#: vgmerge.c:93 vgsplit.c:297
+#, c-format
+msgid "Logical volumes in \"%s\" must be inactive"
+msgstr ""
+
+#: vgmerge.c:100
+#, c-format
+msgid "Extent sizes differ: %d (%s) and %d (%s)"
+msgstr ""
+
+#: vgmerge.c:108
+#, c-format
+msgid "Maximum number of physical volumes (%d) exceeded  for \"%s\" and \"%s\""
+msgstr ""
+
+#: vgmerge.c:116
+#, c-format
+msgid "Maximum number of logical volumes (%d) exceeded  for \"%s\" and \"%s\""
+msgstr ""
+
+#: vgmerge.c:130
+#, c-format
+msgid "Duplicate logical volume name \"%s\" in \"%s\" and \"%s\""
+msgstr ""
+
+#: vgmerge.c:142 vgmerge.c:151
+#, c-format
+msgid "Physical volume %s might be constructed from same volume group %s."
+msgstr ""
+
+#: vgmerge.c:186
+#, c-format
+msgid "Failed to generate new random LVID for %s"
+msgstr ""
+
+#: vgmerge.c:197
+#, c-format
+msgid "Changed LVID for %s to %s"
+msgstr ""
+
+#: vgmerge.c:235
+#, c-format
+msgid "Volume group \"%s\" successfully merged into \"%s\""
+msgstr ""
+
+#: vgmerge.c:252
+msgid "Please enter 2 or more volume groups to merge"
+msgstr ""
+
+#: vgreduce.c:24
+msgid "Volume Groups must always contain at least one PV"
+msgstr ""
+
+#: vgreduce.c:33
+#, c-format
+msgid "Removing PV with UUID %s from VG %s"
+msgstr ""
+
+#: vgreduce.c:36
+#, c-format
+msgid "LVs still present on PV with UUID %s: Can't remove from VG %s"
+msgstr ""
+
+#: vgreduce.c:61
+#, c-format
+msgid "%s/%s has missing extents: removing (including dependencies)"
+msgstr ""
+
+#: vgreduce.c:68
+#, c-format
+msgid "Deactivating (if active) logical volume %s (origin of %s)"
+msgstr ""
+
+#: vgreduce.c:72 vgreduce.c:89 vgreduce.c:333
+#, c-format
+msgid "Failed to deactivate LV %s"
+msgstr ""
+
+#: vgreduce.c:99 vgreduce.c:146 vgreduce.c:348
+#, c-format
+msgid "Removing LV %s from VG %s"
+msgstr ""
+
+#: vgreduce.c:191
+#, c-format
+msgid "Non-mirror-image LV %s found: can't remove."
+msgstr ""
+
+#: vgreduce.c:207
+msgid "Aborting because --mirrorsonly was specified."
+msgstr ""
+
+#: vgreduce.c:232 vgreduce.c:529
+#, c-format
+msgid "Failed to write out a consistent VG for %s"
+msgstr ""
+
+#: vgreduce.c:250
+#, c-format
+msgid "Failed to commit consistent VG for %s"
+msgstr ""
+
+#: vgreduce.c:258
+msgid "Failed to resume LVs using error segments."
+msgstr ""
+
+#: vgreduce.c:290
+#, c-format
+msgid "The log device for %s/%s has failed."
+msgstr ""
+
+#: vgreduce.c:296
+#, c-format
+msgid "Log device for %s/%s has failed."
+msgstr ""
+
+#: vgreduce.c:312
+#, c-format
+msgid "Failed to write out updated VG for %s"
+msgstr ""
+
+#: vgreduce.c:318
+#, c-format
+msgid "Failed to commit updated VG for %s"
+msgstr ""
+
+#: vgreduce.c:329
+#, c-format
+msgid "Deactivating (if active) logical volume %s"
+msgstr ""
+
+#: vgreduce.c:371
+#, c-format
+msgid "Physical volume \"%s\" still in use"
+msgstr ""
+
+#: vgreduce.c:376
+#, c-format
+msgid "Can't remove final physical volume \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgreduce.c:386
+#, c-format
+msgid "Removing \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgreduce.c:404
+#, c-format
+msgid "Removal of physical volume \"%s\" from \"%s\" failed"
+msgstr ""
+
+#: vgreduce.c:418
+#, c-format
+msgid "Removed \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgreduce.c:431
+msgid "Please give volume group name and physical volume paths"
+msgstr ""
+
+#: vgreduce.c:437
+msgid "Please give volume group name"
+msgstr ""
+
+#: vgreduce.c:443
+msgid "--mirrorsonly requires --removemissing"
+msgstr ""
+
+#: vgreduce.c:449
+msgid "Please enter physical volume paths or option -a"
+msgstr ""
+
+#: vgreduce.c:454
+msgid "Option -a and physical volume paths mutually exclusive"
+msgstr ""
+
+#: vgreduce.c:460
+msgid "Please only specify the volume group"
+msgstr ""
+
+#: vgreduce.c:496
+#, c-format
+msgid "Volume group \"%s\" is already consistent"
+msgstr ""
+
+#: vgreduce.c:537
+#, c-format
+msgid "Wrote out consistent volume group %s"
+msgstr ""
+
+#: vgreduce.c:553
+#, c-format
+msgid "Volume group \"%s\" is not reducible"
+msgstr ""
+
+#: vgremove.c:27
+#, c-format
+msgid "Volume group \"%s\" not found or inconsistent."
+msgstr ""
+
+#: vgremove.c:29
+msgid "Consider vgreduce --removemissing if metadata is inconsistent."
+msgstr ""
+
+#: vgremove.c:40
+#, c-format
+msgid "Volume group \"%s\" still contains %d logical volume(s)"
+msgstr ""
+
+#: vgremove.c:49
+#, c-format
+msgid "vg_remove %s failed"
+msgstr ""
+
+#: vgremove.c:56
+#, c-format
+msgid "Removing physical volume \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgremove.c:69
+#, c-format
+msgid "Failed to remove physical volume \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgremove.c:79
+#, c-format
+msgid "Volume group \"%s\" successfully removed"
+msgstr ""
+
+#: vgremove.c:81
+#, c-format
+msgid "Volume group \"%s\" not properly removed"
+msgstr ""
+
+#: vgremove.c:91
+msgid "Please enter one or more volume group paths"
+msgstr ""
+
+#: vgrename.c:34
+msgid "Old and new volume group names need specifying"
+msgstr ""
+
+#: vgrename.c:46
+#, c-format
+msgid "New volume group path exceeds maximum length of %d!"
+msgstr ""
+
+#: vgrename.c:58
+msgid "Old and new volume group names must differ"
+msgstr ""
+
+#: vgrename.c:66
+msgid "No complete volume groups found"
+msgstr ""
+
+#: vgrename.c:76
+#, c-format
+msgid "Found more than one VG called %s. Please supply VG uuid."
+msgstr ""
+
+#: vgrename.c:99
+#, c-format
+msgid "Volume group %s %s%s%snot found."
+msgstr ""
+
+#: vgrename.c:123
+#, c-format
+msgid "Volume group \"%s\" still has active LVs"
+msgstr ""
+
+#: vgrename.c:129
+#, c-format
+msgid "Checking for new volume group \"%s\""
+msgstr ""
+
+#: vgrename.c:139
+#, c-format
+msgid "New volume group \"%s\" already exists"
+msgstr ""
+
+#: vgrename.c:154
+#, c-format
+msgid "Renaming \"%s\" to \"%s\""
+msgstr ""
+
+#: vgrename.c:156
+msgid "Test mode: Skipping rename."
+msgstr ""
+
+#: vgrename.c:158
+#, c-format
+msgid "Renaming \"%s\" to \"%s\" failed: %s"
+msgstr ""
+
+#: vgrename.c:177
+#, c-format
+msgid "Volume group \"%s\" successfully renamed to \"%s\""
+msgstr ""
+
+#: vgscan.c:36
+#, c-format
+msgid "Found %svolume group \"%s\" using metadata type %s"
+msgstr ""
+
+#: vgscan.c:50
+msgid "Too many parameters on command line"
+msgstr ""
+
+#: vgscan.c:57
+msgid "Reading all physical volumes.  This may take a while..."
+msgstr ""
+
+#: vgsplit.c:25
+#, c-format
+msgid "Physical volume %s not in volume group %s"
+msgstr ""
+
+#: vgsplit.c:90
+#, c-format
+msgid "Can't split Logical Volume %s between two Volume Groups"
+msgstr ""
+
+#: vgsplit.c:152
+#, c-format
+msgid "Snapshot %s split"
+msgstr ""
+
+#: vgsplit.c:193
+#, c-format
+msgid "Mirror %s split"
+msgstr ""
+
+#: vgsplit.c:218
+msgid "Existing VG, new VG and physical volumes required."
+msgstr ""
+
+#: vgsplit.c:264
+#, c-format
+msgid "Volume group \"%s\" is not resizeable"
+msgstr ""
+
+#: vgsplit.c:285
+#, c-format
+msgid "Volume group \"%s\" already exists"
+msgstr ""
+
+#: vgsplit.c:339
+msgid "Cannot split: Nowhere to store metadata for new Volume Group"
+msgstr ""
+
+#: vgsplit.c:348
+msgid "Writing out updated volume groups"
+msgstr ""
+
+#: vgsplit.c:370
+#, c-format
+msgid "Volume group \"%s\" became inconsistent: please fix manually"
+msgstr ""
+
+#: vgsplit.c:385
+#, c-format
+msgid "Volume group \"%s\" successfully split from \"%s\""
+msgstr ""
+
+#: zero/zero.c:71
+msgid "zero module string list allocation failed"
+msgstr ""
diff --git a/po/pogen.h b/po/pogen.h
new file mode 100644 (file)
index 0000000..66940b9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/*
+ * Macros to change log messages into a format that xgettext can handle.
+ *
+ * Note that different PRI* definitions lead to different strings for
+ * different architectures.
+ */
+
+#define print_log(level, dm_errno, file, line, format, args...) print_log(format, args)
+#define dm_log(level, file, line, format, args...) dm_log(format, args)
+#define dm_log_with_errno(level, dm_errno, file, line, format, args...) \
+    dm_log(level, file, line, format, args)
+
diff --git a/report-generators/lib/log.rb b/report-generators/lib/log.rb
new file mode 100644 (file)
index 0000000..c71d877
--- /dev/null
@@ -0,0 +1,40 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Merely wraps the logger library with a bit of standard policy.
+require 'logger'
+
+module Log
+  $log = Logger.new(STDERR)
+
+  def init(io_)
+    $log = Logger.new(io_)
+  end
+end
+
+def fatal(*args)
+  $log.fatal(*args)
+end
+
+def error(*args)
+  $log.error(*args)
+end
+
+def info(*args)
+  $log.info(*args)
+end
+
+def warning(*args)
+  $log.warn(*args)
+end
+
+def debug(*args)
+  $log.debug(*args)
+end
diff --git a/report-generators/lib/report_templates.rb b/report-generators/lib/report_templates.rb
new file mode 100644 (file)
index 0000000..4c0bc78
--- /dev/null
@@ -0,0 +1,38 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Policy for the location of report templates
+require 'string-store'
+
+class TemplateStringStore < StringStore
+  def initialize()
+    super(['report-generators/templates'])
+  end
+end
+
+module ReportTemplates
+  def generate_report(report, bs, dest_path = nil)
+    include Reports
+    reports = ReportRegister.new
+    template_store = TemplateStringStore.new
+    report = reports.get_report(report)
+    erb = ERB.new(template_store.lookup(report.template))
+    body = erb.result(bs)
+    title = report.short_desc
+
+    erb = ERB.new(template_store.lookup("boiler_plate.rhtml"))
+    txt = erb.result(binding)
+
+    dest_path = dest_path.nil? ? report.path : dest_path
+    dest_path.open("w") do |out|
+      out.puts txt
+    end
+  end
+end
diff --git a/report-generators/lib/reports.rb b/report-generators/lib/reports.rb
new file mode 100644 (file)
index 0000000..c61bb20
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Data about the various reports we support
+require 'log'
+require 'pathname'
+
+module Reports
+  Report = Struct.new(:short_desc, :desc, :path, :template)
+
+  class ReportRegister
+    attr_reader :reports
+
+    private
+    def add_report(sym, *args)
+      @reports[sym] = Report.new(*args)
+    end
+
+    public
+    def initialize()
+      @reports = Hash.new
+
+      add_report(:unit_test,
+                 "Unit Tests",
+                 "unit tests",
+                 Pathname.new("reports/unit.html"),
+                 Pathname.new("unit_test.rhtml"))
+
+      add_report(:memcheck,
+                 "Memory Tests",
+                 "unit tests with valgrind memory checking",
+                 Pathname.new("reports/memcheck.html"),
+                 Pathname.new("memcheck.rhtml"))
+
+      add_report(:unit_detail,
+                 "Unit Test Detail",
+                 "unit test detail",
+                 Pathname.new("reports/unit_detail.html"), # FIXME replace this with a lambda
+                 Pathname.new("unit_detail.rhtml"))
+    end
+
+    def get_report(sym)
+      raise RuntimeError, "unknown report '#{sym}'" unless @reports.member?(sym)
+      @reports[sym]
+    end
+
+    def each(&block)
+      @reports.each(&block)
+    end
+  end
+end
diff --git a/report-generators/lib/schedule_file.rb b/report-generators/lib/schedule_file.rb
new file mode 100644 (file)
index 0000000..6a339bc
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Parses the simple colon delimited test schedule files.
+
+ScheduledTest = Struct.new(:desc, :command_line, :status, :output)
+
+class Schedule
+  attr_reader :dir, :schedules
+
+  def initialize(dir, ss)
+    @dir = dir
+    @schedules = ss
+  end
+
+  def run
+    Dir::chdir(@dir.to_s) do
+      @schedules.each do |s|
+        reader, writer = IO.pipe
+        print "#{s.desc} ... "
+        pid = spawn(s.command_line, [ STDERR, STDOUT ] => writer)
+        writer.close
+        _, s.status = Process::waitpid2(pid)
+        puts (s.status.success? ? "pass" : "fail")
+        s.output = reader.read
+      end
+    end
+  end
+
+  def self.read(dir, io)
+    ss = Array.new
+
+    io.readlines.each do |line|
+      case line.strip
+      when /^\#.*/
+        next
+
+      when /([^:]+):(.*)/
+        ss << ScheduledTest.new($1.strip, $2.strip)
+
+      else
+        raise RuntimeError, "badly formatted schedule line"
+      end  
+    end
+
+    Schedule.new(dir, ss)
+  end
+end
+
diff --git a/report-generators/lib/string-store.rb b/report-generators/lib/string-store.rb
new file mode 100644 (file)
index 0000000..b3b8cc9
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Provides a simple way of accessing the contents of files by a symbol
+# name.  Useful for erb templates.
+
+require 'pathname'
+
+class StringStore
+  attr_accessor :path
+
+  def initialize(p)
+    @paths = p.nil? ? Array.new : p # FIXME: do we need to copy p ?
+  end
+
+  def lookup(sym)
+    files = expansions(sym)
+
+    @paths.each do |p|
+      files.each do |f|
+        pn = Pathname.new("#{p}/#{f}")
+        if pn.file?
+          return pn.read
+        end
+      end
+    end
+
+    raise RuntimeError, "unknown string entry: #{sym}"
+  end
+
+  private
+  def expansions(sym)
+    ["#{sym}", "#{sym}.txt"]
+  end
+end
diff --git a/report-generators/memcheck.rb b/report-generators/memcheck.rb
new file mode 100644 (file)
index 0000000..e616bc8
--- /dev/null
@@ -0,0 +1,86 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Reads the schedule files given on the command line.  Runs them and
+# generates the reports.
+
+# FIXME: a lot of duplication with unit_test.rb
+
+require 'schedule_file'
+require 'pathname'
+require 'reports'
+require 'erb'
+require 'report_templates'
+
+include ReportTemplates
+
+schedules = ARGV.map do |f|
+  p = Pathname.new(f)
+  Schedule.read(p.dirname, p)
+end
+
+total_passed = 0
+total_failed = 0
+
+# We need to make sure the lvm shared libs are in the LD_LIBRARY_PATH
+ENV['LD_LIBRARY_PATH'] = `pwd`.strip + "/libdm:" + (ENV['LD_LIBRARY_PATH'] || '')
+
+ENV['TEST_TOOL'] = "valgrind --leak-check=full --show-reachable=yes"
+
+schedules.each do |s|
+  s.run
+
+  s.schedules.each do |t|
+    if t.status.success?
+      total_passed += 1
+    else
+      total_failed += 1
+    end
+  end
+end
+
+def mangle(txt)
+  txt.gsub(/\s+/, '_')
+end
+
+MemcheckStats = Struct.new(:definitely_lost, :indirectly_lost, :possibly_lost, :reachable)
+
+def format(bytes, blocks)
+  "#{bytes} bytes, #{blocks} blocks"
+end
+
+# Examines the output for details of leaks
+def extract_stats(t)
+  d = i = p = r = '-'
+
+  t.output.split("\n").each do |l|
+    case l
+    when /==\d+==    definitely lost: ([0-9,]+) bytes in ([0-9,]+) blocks/
+        d = format($1, $2)
+    when /==\d+==    indirectly lost: ([0-9,]+) bytes in ([0-9,]+) blocks/
+        i = format($1, $2)
+    when /==\d+==    possibly lost: ([0-9,]+) bytes in ([0-9,]+) blocks/
+        p = format($1, $2)
+    when /==\d+==    still reachable: ([0-9,]+) bytes in ([0-9,]+) blocks/
+        r = format($1, $2)
+    end
+  end
+
+  MemcheckStats.new(d, i, p, r)
+end
+
+generate_report(:memcheck, binding)
+
+# now we generate a detail report for each schedule
+schedules.each do |s|
+  s.schedules.each do |t|
+    generate_report(:unit_detail, binding, Pathname.new("reports/memcheck_#{mangle(t.desc)}.html"))
+  end
+end
diff --git a/report-generators/templates/boiler_plate.rhtml b/report-generators/templates/boiler_plate.rhtml
new file mode 100644 (file)
index 0000000..23f01cb
--- /dev/null
@@ -0,0 +1,25 @@
+<html>
+<head>
+<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
+<title><%= title %></title>
+<link title="Style" type="text/css" rel="stylesheet" href="stylesheet.css">
+</head>
+
+<body>
+<div id="banner">
+<h2><%= title %></h2>
+</div>
+<div id="main">
+  <div id="controls">
+    <table>
+      <tr><td><a href="index.html">Generation times</a></td></tr>
+      <tr><td><a href="unit.html">Unit tests</a></td></tr>
+      <tr><td><a href="memcheck.html">Memory tests</a></td></tr>
+    </table>
+  </div>
+
+  <div id="body">
+    <%= body %>
+  </div>
+</div>
+</body>
diff --git a/report-generators/templates/index.rhtml b/report-generators/templates/index.rhtml
new file mode 100644 (file)
index 0000000..6d72081
--- /dev/null
@@ -0,0 +1,17 @@
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Report</th><th>Generation time</th></tr>
+<% [:unit_test, :memcheck].each do |sym| %>
+<% r = reports.get_report(sym) %>
+<tr>
+  <td>
+    <% if r.path.file? %>
+    <a href="<%= r.path.to_s.gsub(/^reports\//, '') %>"><%= r.short_desc %></a>
+    <% else %>
+    <%= r.short_desc %>
+    <% end %>
+  </td>
+  <td><%= safe_mtime(r) %></td>
+</tr>
+<% end %>
+</table>
+
diff --git a/report-generators/templates/memcheck.rhtml b/report-generators/templates/memcheck.rhtml
new file mode 100644 (file)
index 0000000..75872ed
--- /dev/null
@@ -0,0 +1,30 @@
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+  <tr><th>Tests passed</th><th>Tests failed</th></tr>
+  <tr><td class="pass"><%= total_passed %></td><td <%= total_failed == 0 ? "" : "class=\"fail\""%>><%= total_failed %></td></tr>
+</table>
+
+<% schedules.each do |s| %>
+<h3><%= s.dir.sub('./unit-tests/', '') %></h3>
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Test</th><th>Result</th><th>Definitely lost</th><th>indirectly lost</th><th>possibly lost</th><th>still reachable</th><tr>
+
+<% s.schedules.each do |t| %>
+<tr>
+  <td>
+  <a href="memcheck_<%= mangle(t.desc) %>.html"><%= t.desc %></a>
+  </td>
+  <% if t.status.success? %>
+  <td class="pass">pass</td>
+  <% else %>
+  <td class="fail">fail</td>
+  <% end %>
+  <% stats = extract_stats(t) %>
+  <td><%= stats.definitely_lost %></td>
+  <td><%= stats.indirectly_lost %></td>
+  <td><%= stats.possibly_lost %></td>
+  <td><%= stats.reachable %></td>
+</tr>
+<% end %>
+</table>
+<% end %>
diff --git a/report-generators/templates/unit_detail.rhtml b/report-generators/templates/unit_detail.rhtml
new file mode 100644 (file)
index 0000000..5324f07
--- /dev/null
@@ -0,0 +1,37 @@
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Test</th><th>Result</th></tr>
+<tr>
+  <td>
+  <%= t.desc %>
+  </td>
+  <% if t.status.success? %>
+  <td class="pass">pass</td>
+  <% else %>
+  <td class="fail">fail</td>
+  <% end %>
+</tr>
+</table>
+
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Command line</th></tr>
+<tr>
+  <td>
+  <pre>
+<%= t.command_line %>
+  </pre>
+  </td>
+</tr>
+</table>
+
+
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Output</th></tr>
+<tr>
+  <td>
+  <pre>
+<%= t.output %>
+  </pre>
+  </td>
+</tr>
+</table>
+
diff --git a/report-generators/templates/unit_test.rhtml b/report-generators/templates/unit_test.rhtml
new file mode 100644 (file)
index 0000000..3137abd
--- /dev/null
@@ -0,0 +1,23 @@
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+  <tr><th>Tests passed</th><th>Tests failed</th></tr>
+  <tr><td class="pass"><%= total_passed %></td><td <%= total_failed == 0 ? "" : "class=\"fail\""%>><%= total_failed %></td></tr>
+</table>
+
+<% schedules.each do |s| %>
+<h3><%= s.dir.sub('./unit-tests/', '') %></h3>
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Test</th><th>Result</th></tr>
+<% s.schedules.each do |t| %>
+<tr>
+  <td>
+  <a href="detail_<%= mangle(t.desc) %>.html"><%= t.desc %></a>
+  </td>
+  <% if t.status.success? %>
+  <td class="pass">pass</td>
+  <% else %>
+  <td class="fail">fail</td>
+  <% end %>
+</tr>
+<% end %>
+</table>
+<% end %>
diff --git a/report-generators/test/example.schedule b/report-generators/test/example.schedule
new file mode 100644 (file)
index 0000000..f617187
--- /dev/null
@@ -0,0 +1,4 @@
+# This is a comment
+description number 1:$TEST_TOOL ls
+foo bar:   $TEST_TOOL du -hs .
+       this comment is prefixed with whitespace: $TEST_TOOL date
\ No newline at end of file
diff --git a/report-generators/test/strings/more_strings/test3.txt b/report-generators/test/strings/more_strings/test3.txt
new file mode 100644 (file)
index 0000000..3e9ffe0
--- /dev/null
@@ -0,0 +1 @@
+lorem
diff --git a/report-generators/test/strings/test1.txt b/report-generators/test/strings/test1.txt
new file mode 100644 (file)
index 0000000..af5626b
--- /dev/null
@@ -0,0 +1 @@
+Hello, world!
diff --git a/report-generators/test/strings/test2 b/report-generators/test/strings/test2
new file mode 100644 (file)
index 0000000..54d55bf
--- /dev/null
@@ -0,0 +1,3 @@
+one
+two
+three
\ No newline at end of file
diff --git a/report-generators/test/tc_log.rb b/report-generators/test/tc_log.rb
new file mode 100644 (file)
index 0000000..96ce7c0
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+require 'test/unit'
+require 'stringio'
+require 'log'
+
+class TestLog < Test::Unit::TestCase
+  include Log
+
+  private
+  def remove_timestamps(l)
+    l.gsub(/\[[^\]]*\]/, '')
+  end
+
+  public
+  def test_log
+    StringIO.open do |out|
+      init(out)
+
+      info("msg1")
+      warning("msg2")
+      debug("msg3")
+
+      assert_equal("I,   INFO -- : msg1\nW,   WARN -- : msg2\nD,  DEBUG -- : msg3\n",
+                   remove_timestamps(out.string))
+    end
+  end
+end
diff --git a/report-generators/test/tc_schedule_file.rb b/report-generators/test/tc_schedule_file.rb
new file mode 100644 (file)
index 0000000..70aefdd
--- /dev/null
@@ -0,0 +1,38 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+require 'test/unit'
+require 'pathname'
+require 'schedule_file'
+
+class TestScheduleFile < Test::Unit::TestCase
+  def test_reading
+    p = Pathname.new("report-generators/test/example.schedule")
+    p.open do |f|
+      s = Schedule.read(p.dirname, f)
+
+      assert_equal(3, s.schedules.size)
+      assert_equal(s.schedules[2].desc, "this comment is prefixed with whitespace")
+      assert_equal(s.schedules[0].command_line, "$TEST_TOOL ls")
+    end
+  end
+
+  def test_running
+    p = Pathname.new("report-generators/test/example.schedule")
+    p.open do |f|
+      s = Schedule.read(p.dirname, f)
+      s.run
+
+      s.schedules.each do |t|
+        assert(t.status.success?)
+      end
+    end
+  end
+end
diff --git a/report-generators/test/tc_string_store.rb b/report-generators/test/tc_string_store.rb
new file mode 100644 (file)
index 0000000..05c6719
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+require 'string-store'
+require 'test/unit'
+
+class TestStringStore < Test::Unit::TestCase
+  def setup
+    @ss = StringStore.new(['report-generators/test/strings',
+                           'report-generators/test/strings/more_strings'])
+  end
+
+  def test_lookup
+    assert_equal("Hello, world!\n", @ss.lookup(:test1))
+    assert_equal("one\ntwo\nthree", @ss.lookup(:test2))
+    assert_equal("lorem\n", @ss.lookup(:test3))
+
+    assert_raises(RuntimeError) do
+      @ss.lookup(:unlikely_name)
+    end
+  end
+end
diff --git a/report-generators/test/ts.rb b/report-generators/test/ts.rb
new file mode 100644 (file)
index 0000000..4e99e68
--- /dev/null
@@ -0,0 +1,13 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+require 'tc_log'
+require 'tc_string_store'
+require 'tc_schedule_file'
diff --git a/report-generators/title_page.rb b/report-generators/title_page.rb
new file mode 100644 (file)
index 0000000..1c1cd1d
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# This generates the index for the reports, including generation
+# times.
+
+require 'log'
+require 'string-store'
+require 'reports'
+require 'erb'
+require 'report_templates'
+
+include Reports
+
+reports = ReportRegister.new
+
+def safe_mtime(r)
+  r.path.file? ? r.path.mtime.to_s : "not generated"
+end
+
+template_store = TemplateStringStore.new
+
+# FIXME: use generate_report() method
+erb = ERB.new(template_store.lookup("index.rhtml"))
+body = erb.result(binding)
+title = "Generation times"
+
+erb = ERB.new(template_store.lookup("boiler_plate.rhtml"))
+txt = erb.result(binding)
+
+Pathname.new("reports/index.html").open("w") do |f|
+  f.puts txt
+end
+
+
diff --git a/report-generators/unit_test.rb b/report-generators/unit_test.rb
new file mode 100644 (file)
index 0000000..1e590bc
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Reads the schedule files given on the command line.  Runs them and
+# generates the reports.
+
+require 'schedule_file'
+require 'pathname'
+require 'reports'
+require 'erb'
+require 'report_templates'
+
+include ReportTemplates
+
+schedules = ARGV.map do |f|
+  p = Pathname.new(f)
+  Schedule.read(p.dirname, p)
+end
+
+total_passed = 0
+total_failed = 0
+
+# We need to make sure the lvm shared libs are in the LD_LIBRARY_PATH
+ENV['LD_LIBRARY_PATH'] = `pwd`.strip + "/libdm:" + (ENV['LD_LIBRARY_PATH'] || '')
+
+schedules.each do |s|
+  s.run
+
+  s.schedules.each do |t|
+    if t.status.success?
+      total_passed += 1
+    else
+      total_failed += 1
+    end
+  end
+end
+
+def mangle(txt)
+  txt.gsub(/\s+/, '_')
+end
+
+generate_report(:unit_test, binding)
+
+# now we generate a detail report for each schedule
+schedules.each do |s|
+  s.schedules.each do |t|
+    generate_report(:unit_detail, binding, Pathname.new("reports/detail_#{mangle(t.desc)}.html"))
+  end
+end
diff --git a/reports/stylesheet.css b/reports/stylesheet.css
new file mode 100644 (file)
index 0000000..3d41926
--- /dev/null
@@ -0,0 +1,77 @@
+/* Styles for main page */
+#banner {
+  background: #9c9;
+  padding-top: 5px;
+  padding-bottom: 5px;
+  border-bottom: 2px solid;
+  font: small-caps 20px/20px "Times New Roman", serif;
+  color: #282;
+  text-align: center;
+}
+
+#banner img {
+  float: left;
+}
+
+#main {
+  margin-left: 0em;
+  padding-top: 4ex;
+  padding-left: 2em;
+  background: white;
+}
+
+h1 {
+  font:  150% sans-serif;
+  color: #226;
+  border-bottom: 3px dotted #77d;
+}
+
+body {
+    font: normal 75% verdana,arial,helvetica;
+    color:#000000;
+}
+
+table tr td, table tr th {
+  font-size: 75%;
+}
+
+table.stripes tr th {
+    font-weight: bold;
+    text-align: left;
+    background: #a0a0a0;
+}
+
+table.stripes tr td {
+    background: #ccccc0;
+}
+
+td.pass {
+  color: green;
+}
+
+td.fail {
+  color: red;
+  font-weight: bold;
+}
+
+#main {
+    padding-left: 0em;
+}
+
+#controls {
+  float: left;
+  padding-top: 1em;
+  padding-left: 1em;
+  padding-right: 1em;
+  padding-bottom: 1em;
+  width: 14em;
+  border-right: 2px solid;
+}
+
+#body {
+  margin-left: 16em;
+  padding-top: 4ex;
+  padding-left: 2em;
+  background: white;
+  border-left: 2px solid;
+}
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
new file mode 100644 (file)
index 0000000..5293cc3
--- /dev/null
@@ -0,0 +1,56 @@
+#
+# Copyright (C) 2006-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+include $(top_builddir)/make.tmpl
+
+SCRIPTS = lvmdump.sh lvmconf.sh vgimportclone.sh
+ifeq ("@FSADM@", "yes")
+       SCRIPTS += fsadm.sh
+endif
+
+OCF_SCRIPTS =
+ifeq ("@OCF@", "yes")
+        OCF_SCRIPTS += VolumeGroup.ocf
+endif
+
+vpath %.sh $(srcdir)
+
+%_install: %.sh
+       $(INSTALL_PROGRAM) -D $< $(sbindir)/$(basename $(<F))
+
+%_install: %.ocf
+       $(INSTALL_DIR) $(ocf_scriptdir)
+       $(INSTALL_SCRIPT) $< $(ocf_scriptdir)/$(basename $(<F))
+
+install_lvm2: $(SCRIPTS:.sh=_install)
+
+install_ocf: $(OCF_SCRIPTS:.ocf=_install)
+
+install: install_lvm2 install_ocf
+
+# FIXME Customise for other distributions
+install_initscripts:
+       $(INSTALL_DIR) $(initdir)
+       $(INSTALL_SCRIPT) lvm2_monitoring_init_red_hat $(initdir)/lvm2-monitor
+ifneq ("@CLVMD@", "none")
+       $(INSTALL_SCRIPT) clvmd_init_red_hat $(initdir)/clvmd
+endif
+ifeq ("@BUILD_CMIRRORD@", "yes")
+       $(INSTALL_SCRIPT) cmirrord_init_red_hat $(initdir)/cmirrord
+endif
+
+DISTCLEAN_TARGETS += clvmd_init_red_hat cmirrord_init_red_hat lvm2_monitoring_init_red_hat
diff --git a/scripts/VolumeGroup.ocf b/scripts/VolumeGroup.ocf
new file mode 100644 (file)
index 0000000..8a39582
--- /dev/null
@@ -0,0 +1,279 @@
+#!/bin/sh
+#
+# VolumeGroup
+#
+# Description: Manages an LVM2 volume group as an HA resource in
+#               an OCF-compliant cluster
+#
+#
+# Authors:     Alan Robertson, Lars Marowsky-Bree, Florian Haas,
+#               and others from the Linux-HA project
+# License:     GNU General Public License (GPL)
+# Copyright:   (C) 2002 - 2005 International Business Machines, Inc.
+#               (C) 2010 LINBIT HA-Solutions GmbH
+#
+#      This code significantly inspired by the LVM resource
+#      in FailSafe by Lars Marowsky-Bree
+#
+#######################################################################
+# Initialization:
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
+
+#######################################################################
+
+
+usage() {
+  methods=`VolumeGroup_methods`
+  methods=`echo $methods | tr ' ' '|'`
+  cat <<EOF
+       usage: $0 $methods
+
+       $0 manages an LVM Volume Group (VG) as an HA resource
+
+       The 'start' operation brings the given volume online
+       The 'stop' operation takes the given volume offline
+       The 'status' operation reports whether the volume is available
+       The 'monitor' operation reports whether the volume seems present
+       The 'validate-all' operation checks whether the OCF parameters are valid
+       The 'methods' operation reports on the methods $0 supports
+
+EOF
+}
+
+meta_data() {
+       cat <<EOF
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="VolumeGroup">
+<version>1.0</version>
+
+<longdesc lang="en">
+Resource script for an LVM Volume Group.
+</longdesc>
+<shortdesc lang="en">Controls the availability of an LVM Volume Group</shortdesc>
+
+<parameters>
+<parameter name="volgrpname" unique="0" required="1">
+<longdesc lang="en">
+The name of volume group.
+</longdesc>
+<shortdesc lang="en">Volume group name</shortdesc>
+<content type="string" default="" />
+</parameter>
+<parameter name="exclusive" unique="0" required="0">
+<longdesc lang="en">
+If set, the volume group will be activated exclusively.
+</longdesc>
+<shortdesc lang="en">Exclusive activation</shortdesc>
+<content type="boolean" default="false" />
+</parameter>
+</parameters>
+
+<actions>
+<action name="start" timeout="30" />
+<action name="stop" timeout="30" />
+<action name="status" timeout="30" />
+<action name="monitor" depth="0" timeout="30" interval="10" />
+<action name="methods" timeout="5" />
+<action name="meta-data" timeout="5" />
+<action name="validate-all" timeout="5" />
+</actions>
+</resource-agent>
+EOF
+}
+
+#
+# methods: What methods/operations do we support?
+#
+VolumeGroup_methods() {
+  cat <<EOF
+       start
+       stop
+       status
+       monitor
+       methods
+       validate-all
+       usage
+EOF
+}
+
+#
+# Report on LVM volume status. VG may be reported as active
+# ($OCF_SUCCESS) or inactive ($OCF_NOT_RUNNING)
+#
+VolumeGroup_status() {
+
+    VGOUT=`vgdisplay -v $OCF_RESKEY_volgrpname 2>&1` || exit $OCF_ERR_GENERIC
+    echo "$VGOUT" | grep -i 'Status[ \t]*available' >/dev/null
+    rc=$?
+
+    if [ $rc -eq 0 ]; then
+       ocf_log debug "LVM Volume Group $OCF_RESKEY_volgrpname is available (started)"
+    else
+       ocf_log debug "LVM Volume Group $OCF_RESKEY_volgrpname is not available (stopped)"
+       return $OCF_NOT_RUNNING
+    fi
+
+    if echo "$VGOUT" | grep -i 'Access.*read/write' >/dev/null; then
+       ocf_log debug "Volume $OCF_RESKEY_volgrpname is available read/write (running)"
+    else
+       ocf_log debug "Volume $OCF_RESKEY_volgrpname is available read-only (running)"
+    fi
+
+    return $OCF_SUCCESS
+}
+
+#
+# Monitor the volume - does it really seem to be working? May report
+# $OCF_SUCCESS or $OCF_NOT_RUNNING like VolumeGroup_status, plus
+# $OCF_ERR_GENERIC in case vgck reports an error.
+#
+VolumeGroup_monitor() {
+    if ! VolumeGroup_status $OCF_RESKEY_volgrpname; then
+       ocf_log info "LVM Volume Group $OCF_RESKEY_volgrpname is offline"
+       return $OCF_NOT_RUNNING
+    fi
+
+    ocf_run vgck $OCF_RESKEY_volgrpname || exit $OCF_ERR_GENERIC
+
+    return $OCF_SUCCESS
+}
+
+#
+# Activate the volume group, either locally (if $OCF_RESKEY_exclusive
+# is false or unset), or exclusively (if $OCF_RESKEY_exclusive is
+# true).
+# Either returns successfully, or exits with $OCF_ERR_GENERIC.
+#
+VolumeGroup_start() {
+
+  ocf_log info "Activating volume group $OCF_RESKEY_volgrpname"
+  ocf_run vgscan
+
+  local active_mode
+  active_mode="ly"
+  if ocf_is_true "$OCF_RESKEY_exclusive" ; then
+      active_mode="ey"
+  fi
+
+  ocf_run vgchange -a $active_mode $OCF_RESKEY_volgrpname || exit $OCF_ERR_GENERIC
+
+  if ! VolumeGroup_status $OCF_RESKEY_volgrpname; then
+    ocf_log err "LVM: $OCF_RESKEY_volgrpname did not activate correctly"
+    exit $OCF_ERR_GENERIC
+  fi
+
+  return $OCF_SUCCESS
+}
+
+#
+# Deactivate the volume group.
+# Either returns successfully, or exits with $OCF_ERR_GENERIC.
+#
+VolumeGroup_stop() {
+    if ! VolumeGroup_status; then
+       ocf_log debug "Volume Group $OCF_RESKEY_volgrpname already stopped"
+       return $OCF_SUCCESS
+    fi
+
+    ocf_log info "Deactivating volume group $OCF_RESKEY_volgrpname"
+    ocf_run vgchange -a ln $OCF_RESKEY_volgrpname || exit $OCF_ERR_GENERIC
+
+    if VolumeGroup_status; then
+       ocf_log err "LVM: $OCF_RESKEY_volgrpname did not stop correctly"
+       exit $OCF_ERR_GENERIC
+    fi
+
+    return $OCF_SUCCESS
+}
+
+#
+# Check whether the OCF instance parameters are valid.
+# Either returns successfully, or exits with
+# $OCF_ERR_CONFIGURED if required parameters are missing;
+# $OCF_ERR_INSTALLED if required binaries are missing;
+# $OCF_ERR_GENERIC in case of any other error.
+#
+VolumeGroup_validate_all() {
+
+    if [ -z $OCF_RESKEY_volgrpname ]; then
+       ocf_log err 'Missing required parameter "volgrpname"!'
+       exit $OCF_ERR_CONFIGURED
+    fi
+
+    check_binary vgchange
+    check_binary vgck
+    check_binary vgdisplay
+
+    # Run the following tests only if we're not invoked by a probe
+    # operation
+    if ! ocf_is_probe; then
+        # Off-the-shelf tests...
+       vgck "$OCF_RESKEY_volgrpname" >/dev/null 2>&1
+       if [ $? -ne 0 ]; then
+           ocf_log err "Volume group $OCF_RESKEY_volgrpname does not exist or contains error!"
+           exit $OCF_ERR_GENERIC
+       fi
+
+        # Double-check
+       vgdisplay -v "$OCF_RESKEY_volgrpname" >/dev/null 2>&1
+       if [ $? -ne 0 ]; then
+           ocf_log err "Volume group $OCF_RESKEY_volgrpname does not exist or contains error!"
+           exit $OCF_ERR_GENERIC
+       fi
+    fi
+
+    return $OCF_SUCCESS
+}
+
+#
+# 'main' starts here...
+#
+if [ $# -ne 1 ]; then
+    usage
+    exit $OCF_ERR_ARGS
+fi
+
+case $1 in
+  meta-data)   meta_data
+               exit $OCF_SUCCESS;;
+
+  methods)     VolumeGroup_methods
+               exit $OCF_SUCCESS;;
+
+  usage)       usage
+               exit $OCF_SUCCESS;;
+  *)           ;;
+esac
+
+# Everything except usage and meta-data must pass the validate test
+VolumeGroup_validate_all
+
+# What kind of method was invoked?
+case "$1" in
+    start)
+       VolumeGroup_start
+       ;;
+    stop)
+       VolumeGroup_stop
+       ;;
+    status)
+       VolumeGroup_status
+       ;;
+    monitor)
+       VolumeGroup_monitor
+       ;;
+    validate-all)
+       ;;
+    notify|promote|demote|migrate_from|migrate_to)
+       usage
+       exit $OCF_ERR_UNIMPLEMENTED
+       ;;
+    *) usage
+       exit $OCF_ERR_ARGS
+       ;;
+esac
+
+exit $?
diff --git a/scripts/clvmd_fix_conf.sh b/scripts/clvmd_fix_conf.sh
new file mode 100644 (file)
index 0000000..cc2c50d
--- /dev/null
@@ -0,0 +1,162 @@
+#!/bin/bash
+#
+# Edit an lvm.conf file to enable cluster locking.
+#
+# $1 is the directory where the locking library is installed.
+# $2 (optional) is the config file
+# $3 (optional) is the locking library name
+#
+#
+PREFIX=$1
+LVMCONF=$2
+LIB=$3
+
+if [ -z "$PREFIX" ]
+then
+  echo "usage: $0 <prefix> [<config file>] [<library>]"
+  echo ""
+  echo "<prefix>|UNDO  location of the cluster locking shared library. (no default)"
+  echo "               UNDO will reset the locking back to local"
+  echo "<config file>  name of the LVM config file (default: /etc/lvm/lvm.conf)"
+  echo "<library>      name of the shared library (default: liblvm2clusterlock.so)"
+  echo ""
+  exit 0
+fi
+
+[ -z "$LVMCONF" ] && LVMCONF="/etc/lvm/lvm.conf"
+[ -z "$LIB" ] && LIB="liblvm2clusterlock.so"
+
+if [ "$PREFIX" = "UNDO" ]
+then
+  locking_type="1"
+else
+  locking_type="2"
+
+  if [ "${PREFIX:0:1}" != "/" ]
+  then
+    echo "Prefix must be an absolute path name (starting with a /)"
+    exit 12
+  fi
+
+  if [ ! -f "$PREFIX/$LIB" ]
+  then
+    echo "$PREFIX/$LIB does not exist, did you do a \"make install\" ?"
+    exit 11
+  fi
+fi
+
+if [ ! -f "$LVMCONF" ]
+then
+  echo "$LVMCONF does not exist"
+  exit 10
+fi
+
+
+SCRIPTFILE=`mktemp -t lvmscript.XXXXXXXXXX`
+TMPFILE=`mktemp -t lvmtmp.XXXXXXXXXX`
+
+
+# Flags so we know which parts of the file we can replace and which need
+# adding. These are return codes from grep, so zero means it IS present!
+have_type=1
+have_dir=1
+have_library=1
+have_global=1
+
+grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $LVMCONF
+have_type=$?
+
+grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $LVMCONF
+have_dir=$?
+
+grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $LVMCONF
+have_library=$?
+
+# Those options are in section "global {" so we must have one if any are present.
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+then
+
+    # See if we can find it...
+    grep -q '^[[:blank:]]*global[[:blank:]]*{' $LVMCONF
+    have_global=$?
+    
+    if [ "$have_global" = "1" ] 
+       then
+       echo "global keys but no 'global {' found, can't edit file"
+       exit 12
+    fi
+fi
+
+# So if we don't have "global {" we need to create one and 
+# populate it
+
+if [ "$have_global" = "1" ]
+then
+    cat $LVMCONF - <<EOF > $TMPFILE
+global {
+    # Enable locking for cluster LVM
+    locking_type = $locking_type
+    library_dir = "$PREFIX"
+    locking_library = "$LIB"
+}
+EOF
+    if [ $? != 0 ]
+    then
+       echo "failed to create temporary config file, $LVMCONF not updated"
+       exit 1
+    fi
+else
+    #
+    # We have a "global {" section, so add or replace the
+    # locking entries as appropriate
+    #
+
+    if [ "$have_type" = "0" ] 
+    then
+       SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = $locking_type/g"
+    else
+       SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = 2"
+    fi
+    
+    if [ "$have_dir" = "0" ] 
+    then
+       SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$PREFIX\"'g"
+    else
+       SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$PREFIX\""
+    fi
+
+    if [ "$have_library" = "0" ] 
+    then
+       SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LIB\"/g"
+    else
+       SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LIB\""
+    fi
+
+    echo -e $SEDCMD > $SCRIPTFILE
+    sed  <$LVMCONF >$TMPFILE -f $SCRIPTFILE
+    if [ $? != 0 ]
+    then
+       echo "sed failed, $LVMCONF not updated"
+       exit 1
+    fi
+fi
+
+# Now we have a suitably editted config file in a temp place,
+# backup the original and copy our new one into place.
+
+cp $LVMCONF $LVMCONF.nocluster
+if [ $? != 0 ]
+    then
+    echo "failed to backup old config file, $LVMCONF not updated"
+    exit 2
+fi
+
+cp $TMPFILE $LVMCONF
+if [ $? != 0 ]
+    then
+    echo "failed to copy new config file into place, check $LVMCONF is still OK"
+    exit 3
+fi
+
+rm -f $SCRIPTFILE $TMPFILE
+
diff --git a/scripts/clvmd_init_red_hat.in b/scripts/clvmd_init_red_hat.in
new file mode 100644 (file)
index 0000000..3fc90c5
--- /dev/null
@@ -0,0 +1,218 @@
+#!/bin/bash
+#
+# clvmd - Clustered LVM Daemon init script
+#
+# chkconfig: - 24 76
+# description: Cluster daemon for userland logical volume management tools.
+#
+# For Red-Hat-based distributions such as Fedora, RHEL, CentOS.
+#
+### BEGIN INIT INFO
+# Provides:            clvmd
+# Required-Start:      $local_fs@CLVMD_CMANAGERS@
+# Required-Stop:       $local_fs@CLVMD_CMANAGERS@
+# Short-Description:   This service is Clusterd LVM Daemon.
+# Description:         Cluster daemon for userland logical volume management tools.
+### END INIT INFO
+
+. /etc/rc.d/init.d/functions
+
+DAEMON=clvmd
+
+exec_prefix=@exec_prefix@
+sbindir=@sbindir@
+
+lvm_vgchange=${sbindir}/vgchange
+lvm_vgdisplay=${sbindir}/vgdisplay
+lvm_vgscan=${sbindir}/vgscan
+lvm_lvdisplay=${sbindir}/lvdisplay
+
+CLVMDOPTS="-T30"
+
+[ -f /etc/sysconfig/cluster ] && . /etc/sysconfig/cluster
+[ -f /etc/sysconfig/$DAEMON ] && . /etc/sysconfig/$DAEMON
+
+[ -n "$CLVMD_CLUSTER_IFACE" ] && CLVMDOPTS="$CLVMDOPTS -I $CLVMD_CLUSTER_IFACE" 
+
+# allow up to $CLVMD_STOP_TIMEOUT seconds to clvmd to complete exit operations
+# default to 10 seconds
+
+[ -z $CLMVD_STOP_TIMEOUT ] && CLVMD_STOP_TIMEOUT=10
+
+LOCK_FILE="/var/lock/subsys/$DAEMON"
+
+# NOTE: replace this with vgs, once display filter per attr is implemented.
+clustered_vgs() {
+       ${lvm_vgdisplay} 2>/dev/null | \
+               awk 'BEGIN {RS="VG Name"} {if (/Clustered/) print $1;}'
+}
+
+clustered_active_lvs() {
+       for i in $(clustered_vgs); do
+               ${lvm_lvdisplay} $i 2>/dev/null | \
+               awk 'BEGIN {RS="LV Name"} {if (/[^N^O^T] available/) print $1;}'
+       done
+}
+
+rh_status() {
+       status $DAEMON
+}
+
+rh_status_q() {
+       rh_status >/dev/null 2>&1
+}
+
+start()
+{
+       if ! rh_status_q; then
+               echo -n "Starting $DAEMON: "
+               $DAEMON $CLVMDOPTS || return $?
+               echo
+       fi
+
+       # Refresh local cache.
+       #
+       # It's possible that new PVs were added to this, or other VGs
+       # while this node was down. So we run vgscan here to avoid
+       # any potential "Missing UUID" messages with subsequent
+       # LVM commands.
+
+       # The following step would be better and more informative to the user:
+       # 'action "Refreshing VG(s) local cache:" ${lvm_vgscan}'
+       # but it could show warnings such as:
+       # 'clvmd not running on node x-y-z  Unable to obtain global lock.'
+       # and the action would be shown as FAILED when in reality it didn't.
+       # Ideally vgscan should have a startup mode that would not print
+       # unnecessary warnings.
+
+       ${lvm_vgscan} > /dev/null 2>&1
+
+       action "Activating VG(s):" ${lvm_vgchange} -ayl $LVM_VGS || return $?
+
+       touch $LOCK_FILE
+
+       return 0
+}
+
+wait_for_finish()
+{
+       count=0
+       while [ "$count" -le "$CLVMD_STOP_TIMEOUT" ] && \
+                rh_status_q ]; do
+               sleep 1
+               count=$((count+1))
+       done
+
+       ! rh_status_q
+}
+
+stop()
+{
+       rh_status_q || return 0
+
+       [ -z "$LVM_VGS" ] && LVM_VGS="$(clustered_vgs)"
+       if [ -n "$LVM_VGS" ]; then
+               action "Deactivating clustered VG(s):" ${lvm_vgchange} -anl $LVM_VGS || return $?
+       fi
+
+       action "Signaling $DAEMON to exit" kill -TERM $(pidofproc $DAEMON) || return $?
+
+       # wait half second before we start the waiting loop or we will show
+       # the loop more time than really necessary
+       usleep 500000
+
+       # clvmd could take some time to stop
+       rh_status_q && action "Waiting for $DAEMON to exit:" wait_for_finish
+
+       if rh_status_q; then
+               echo -n "$DAEMON failed to exit"
+               failure
+               echo
+               return 1
+       else
+               echo -n "$DAEMON terminated"
+               success
+               echo
+       fi
+
+       rm -f $LOCK_FILE
+
+       return 0
+}
+
+reload() {
+       rh_status_q || exit 7
+       action "Reloading $DAEMON configuration: " $DAEMON -R || return $?
+}
+
+restart() {
+       # if stop fails, restart will return the error and not attempt
+       # another start. Even if start is protected by rh_status_q,
+       # that would avoid spawning another daemon, it would try to
+       # reactivate the VGs.
+
+       # Try to get clvmd to restart itself. This will preserve 
+       # exclusive LV locks
+       action "Restarting $DAEMON: " $DAEMON -S
+
+       # If that fails then do a normal stop & restart
+       if  [ $? != 0 ]; then
+           stop && start
+           return $?
+       else
+           touch $LOCK_FILE
+           return 0
+       fi
+}
+
+[ "$EUID" != "0" ] && {
+       echo "clvmd init script can only be executed as root user"
+       exit 4
+}
+
+# See how we were called.
+case "$1" in
+  start)
+       start
+       rtrn=$?
+       ;;
+
+  stop)
+       stop
+       rtrn=$?
+       ;;
+
+  restart|force-reload)
+       restart
+       rtrn=$?
+       ;;
+
+  condrestart|try-restart)
+       rh_status_q || exit 0
+       restart
+       rtrn=$? 
+       ;;
+
+  reload)
+       reload
+       rtrn=$?
+       ;;
+
+  status)
+       rh_status
+       rtrn=$?
+       if [ $rtrn = 0 ]; then
+               cvgs="$(clustered_vgs)"
+               echo Clustered Volume Groups: ${cvgs:-"(none)"}
+               clvs="$(clustered_active_lvs)"
+               echo Active clustered Logical Volumes: ${clvs:-"(none)"}
+       fi
+       ;;
+
+  *)
+       echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+       rtrn=2
+       ;;
+esac
+
+exit $rtrn
diff --git a/scripts/cmirrord_init_red_hat.in b/scripts/cmirrord_init_red_hat.in
new file mode 100755 (executable)
index 0000000..ab3b7fb
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/bash
+#
+# chkconfig: - 22 78
+# description: Starts and stops cmirrord
+#
+# For Red-Hat-based distributions such as Fedora, RHEL, CentOS.
+#
+### BEGIN INIT INFO
+# Provides: cmirrord
+# Required-Start:       $network $time $local_fs
+# Required-Stop:       $network $time $local_fs
+# Short-Description:    Starts and stops cmirrord
+# Description:   Starts and stops the cluster mirror log daemon
+### END INIT INFO
+
+. /etc/init.d/functions
+
+DAEMON=cmirrord
+
+LOCK_FILE="/var/lock/subsys/$DAEMON"
+
+start()
+{
+       if ! pidof $DAEMON > /dev/null 
+       then 
+               echo -n "Starting $DAEMON: "
+               daemon $DAEMON
+               rtrn=$?
+               echo
+       fi
+
+       return $rtrn
+}
+
+stop()
+{
+       echo -n "Stopping $DAEMON:"
+       killproc $DAEMON -TERM
+       rtrn=$?
+       echo
+
+       return $rtrn
+}
+
+wait_for_finish()
+{
+       count=0
+
+       while [ "$count" -le 10 -a -n "`pidof $DAEMON`" ]
+       do
+               sleep 1
+               count=$((count + 1))
+       done
+       
+       if [ `pidof $DAEMON` ]
+       then
+               return 1
+       else
+               return 0
+       fi
+}
+
+cmirror_status()
+{
+       status $DAEMON
+}
+
+rtrn=1
+
+# See how we were called.
+case "$1" in
+       start)
+               start
+               rtrn=$?
+               [ $rtrn = 0 ] && touch $LOCK_FILE
+               ;;
+
+       stop)
+               stop
+               rtrn=$?
+               [ $rtrn = 0 ] && rm -f $LOCK_FILE
+               ;;
+
+       restart)
+               if stop
+               then
+                       wait_for_finish
+                       start
+               fi
+               rtrn=$?
+               ;;
+
+       status)
+               cmirror_status
+               rtrn=$?
+               if [ $rtrn -eq 0 ]; then
+                       echo "cmirror is running."
+               fi
+               ;;
+
+       *)
+               echo $"Usage: $0 {start|stop|restart|status}"
+               ;;
+esac
+
+exit $rtrn
diff --git a/scripts/fsadm.sh b/scripts/fsadm.sh
new file mode 100644 (file)
index 0000000..c59dcb6
--- /dev/null
@@ -0,0 +1,489 @@
+#!/bin/bash
+#
+# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+#
+# Author: Zdenek Kabelac <zkabelac at redhat.com>
+#
+# Script for resizing devices (usable for LVM resize)
+#
+# Needed utilities:
+#   mount, umount, grep, readlink, blockdev, blkid, fsck, xfs_check
+#
+# ext2/ext3/ext4: resize2fs, tune2fs
+# reiserfs: resize_reiserfs, reiserfstune
+# xfs: xfs_growfs, xfs_info
+#
+# Return values:
+#   0 success
+#   1 error
+#   2 break detected
+#   3 unsupported online filesystem check for given mounted fs
+
+TOOL=fsadm
+
+PATH=/sbin:/usr/sbin:/bin:/usr/sbin:$PATH
+
+# utilities
+TUNE_EXT=tune2fs
+RESIZE_EXT=resize2fs
+TUNE_REISER=reiserfstune
+RESIZE_REISER=resize_reiserfs
+TUNE_XFS=xfs_info
+RESIZE_XFS=xfs_growfs
+
+MOUNT=mount
+UMOUNT=umount
+MKDIR=mkdir
+RMDIR=rmdir
+BLOCKDEV=blockdev
+BLKID=blkid
+DATE=date
+GREP=grep
+READLINK=readlink
+READLINK_E="-e"
+FSCK=fsck
+XFS_CHECK=xfs_check
+
+# user may override lvm location by setting LVM_BINARY
+LVM=${LVM_BINARY:-lvm}
+
+YES=${_FSADM_YES}
+DRY=0
+VERB=
+FORCE=
+EXTOFF=0
+DO_LVRESIZE=0
+FSTYPE=unknown
+VOLUME=unknown
+TEMPDIR="${TMPDIR:-/tmp}/${TOOL}_${RANDOM}$$/m"
+BLOCKSIZE=
+BLOCKCOUNT=
+MOUNTPOINT=
+MOUNTED=
+REMOUNT=
+PROCMOUNTS="/proc/mounts"
+
+IFS_OLD=$IFS
+# without bash $'\n'
+NL='
+'
+
+tool_usage() {
+       echo "${TOOL}: Utility to resize or check the filesystem on a device"
+       echo
+       echo "  ${TOOL} [options] check device"
+       echo "    - Check the filesystem on device using fsck"
+       echo
+       echo "  ${TOOL} [options] resize device [new_size[BKMGTPE]]"
+       echo "    - Change the size of the filesystem on device to new_size"
+       echo
+       echo "  Options:"
+       echo "    -h | --help         Show this help message"
+       echo "    -v | --verbose      Be verbose"
+       echo "    -e | --ext-offline  unmount filesystem before ext2/ext3/ext4 resize"
+       echo "    -f | --force        Bypass sanity checks"
+       echo "    -n | --dry-run      Print commands without running them"
+       echo "    -l | --lvresize     Resize given device (if it is LVM device)"
+       echo "    -y | --yes          Answer \"yes\" at any prompts"
+       echo
+       echo "  new_size - Absolute number of filesystem blocks to be in the filesystem,"
+       echo "             or an absolute size using a suffix (in powers of 1024)."
+       echo "             If new_size is not supplied, the whole device is used."
+
+       exit
+}
+
+verbose() {
+       test -n "$VERB" && echo "$TOOL: $@" || true
+}
+
+error() {
+       echo "$TOOL: $@" >&2
+       cleanup 1
+}
+
+dry() {
+       if [ "$DRY" -ne 0 ]; then
+               verbose "Dry execution $@"
+               return 0
+       fi
+       verbose "Executing $@"
+       $@
+}
+
+cleanup() {
+       trap '' 2
+       # reset MOUNTPOINT - avoid recursion
+       test "$MOUNTPOINT" = "$TEMPDIR" && MOUNTPOINT="" temp_umount
+       if [ -n "$REMOUNT" ]; then
+               verbose "Remounting unmounted filesystem back"
+               dry $MOUNT "$VOLUME" "$MOUNTED"
+       fi
+       IFS=$IFS_OLD
+       trap 2
+
+       test "$1" -eq 2 && verbose "Break detected"
+
+       if [ "$DO_LVRESIZE" -eq 2 ]; then
+               # start LVRESIZE with the filesystem modification flag
+               # and allow recursive call of fsadm
+               _FSADM_YES=$YES
+               export _FSADM_YES
+               unset FSADM_RUNNING
+               dry exec $LVM lvresize $VERB $FORCE -r -L${NEWSIZE}b $VOLUME_ORIG
+       fi
+        
+       # error exit status for break
+       exit ${1:-1}
+}
+
+# convert parameter from Exa/Peta/Tera/Giga/Mega/Kilo/Bytes and blocks
+# (2^(60/50/40/30/20/10/0))
+decode_size() {
+       case "$1" in
+        *[eE]) NEWSIZE=$(( ${1%[eE]} * 1152921504606846976 )) ;;
+        *[pP]) NEWSIZE=$(( ${1%[pP]} * 1125899906842624 )) ;;
+        *[tT]) NEWSIZE=$(( ${1%[tT]} * 1099511627776 )) ;;
+        *[gG]) NEWSIZE=$(( ${1%[gG]} * 1073741824 )) ;;
+        *[mM]) NEWSIZE=$(( ${1%[mM]} * 1048576 )) ;;
+        *[kK]) NEWSIZE=$(( ${1%[kK]} * 1024 )) ;;
+        *[bB]) NEWSIZE=${1%[bB]} ;;
+            *) NEWSIZE=$(( $1 * $2 )) ;;
+       esac
+       #NEWBLOCKCOUNT=$(round_block_size $NEWSIZE $2)
+       NEWBLOCKCOUNT=$(( $NEWSIZE / $2 ))
+
+       if [ $DO_LVRESIZE -eq 1 ]; then
+               # start lvresize, but first cleanup mounted dirs
+               DO_LVRESIZE=2
+               cleanup 0
+       fi
+}
+
+# detect filesystem on the given device
+# dereference device name if it is symbolic link
+detect_fs() {
+       VOLUME_ORIG=$1
+       VOLUME=${1#/dev/}
+       VOLUME=$($READLINK $READLINK_E "/dev/$VOLUME") || error "Cannot get readlink $1"
+       RVOLUME=$VOLUME
+       case "$RVOLUME" in
+         /dev/dm-[0-9]*)
+               read </sys/block/${RVOLUME#/dev/}/dm/name SYSVOLUME 2>&1 && VOLUME="/dev/mapper/$SYSVOLUME"
+               ;;
+       esac
+       # use /dev/null as cache file to be sure about the result
+       # not using option '-o value' to be compatible with older version of blkid
+       FSTYPE=$($BLKID -c /dev/null -s TYPE "$VOLUME") || error "Cannot get FSTYPE of \"$VOLUME\""
+       FSTYPE=${FSTYPE##*TYPE=\"} # cut quotation marks
+       FSTYPE=${FSTYPE%%\"*}
+       verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\""
+}
+
+# check if the given device is already mounted and where
+# FIXME: resolve swap usage and device stacking
+detect_mounted()  {
+       test -e $PROCMOUNTS || error "Cannot detect mounted device $VOLUME"
+
+       MOUNTED=$($GREP ^"$VOLUME" $PROCMOUNTS)
+
+       # for empty string try again with real volume name
+       test -z "$MOUNTED" && MOUNTED=$($GREP ^"$RVOLUME" $PROCMOUNTS)
+
+       # cut device name prefix and trim everything past mountpoint
+       # echo translates \040 to spaces
+       MOUNTED=${MOUNTED#* }
+       MOUNTED=$(echo -n -e ${MOUNTED%% *})
+
+       # for systems with different device names - check also mount output
+       if test -z "$MOUNTED" ; then
+               MOUNTED=$(LANG=C $MOUNT | $GREP ^"$VOLUME")
+               test -z "$MOUNTED" && MOUNTED=$(LANG=C $MOUNT | $GREP ^"$RVOLUME")
+               MOUNTED=${MOUNTED##* on }
+               MOUNTED=${MOUNTED% type *} # allow type in the mount name
+       fi
+
+       test -n "$MOUNTED"
+}
+
+# get the full size of device in bytes
+detect_device_size() {
+       # check if blockdev supports getsize64
+       $BLOCKDEV 2>&1 | $GREP getsize64 >/dev/null
+       if test $? -eq 0; then
+               DEVSIZE=$($BLOCKDEV --getsize64 "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
+       else
+               DEVSIZE=$($BLOCKDEV --getsize "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
+               SSSIZE=$($BLOCKDEV --getss "$VOLUME") || error "Cannot block size read device \"$VOLUME\""
+               DEVSIZE=$(($DEVSIZE * $SSSIZE))
+       fi
+}
+
+# round up $1 / $2
+# could be needed to gaurantee 'at least given size'
+# but it makes many troubles
+round_up_block_size() {
+       echo $(( ($1 + $2 - 1) / $2 ))
+}
+
+temp_mount() {
+       dry $MKDIR -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR"
+       dry $MOUNT "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR"
+}
+
+temp_umount() {
+       dry $UMOUNT "$TEMPDIR" || error "Failed to umount $TEMPDIR"
+       dry $RMDIR "${TEMPDIR}" || error "Failed to remove $TEMPDIR"
+       dry $RMDIR "${TEMPDIR%%m}" || error "Failed to remove ${TEMPDIR%%m}"
+}
+
+yes_no() {
+       echo -n "$@? [Y|n] "
+
+       if [ -n "$YES" ]; then
+               echo y ; return 0
+       fi
+
+       while read -r -s -n 1 ANS ; do
+               case "$ANS" in
+                "y" | "Y" | "") echo y ; return 0 ;;
+                "n" | "N") echo n ; return 1 ;;
+               esac
+       done
+}
+
+try_umount() {
+       yes_no "Do you want to unmount \"$MOUNTED\"" && dry $UMOUNT "$MOUNTED" && return 0
+       error "Cannot proceed with mounted filesystem \"$MOUNTED\""
+}
+
+validate_parsing() {
+       test -n "$BLOCKSIZE" -a -n "$BLOCKCOUNT" || error "Cannot parse $1 output"
+}
+####################################
+# Resize ext2/ext3/ext4 filesystem
+# - unmounted or mounted for upsize
+# - unmounted for downsize
+####################################
+resize_ext() {
+       verbose "Parsing $TUNE_EXT -l \"$VOLUME\""
+       for i in $(LANG=C $TUNE_EXT -l "$VOLUME"); do
+               case "$i" in
+                 "Block size"*) BLOCKSIZE=${i##*  } ;;
+                 "Block count"*) BLOCKCOUNT=${i##*  } ;;
+               esac
+       done
+       validate_parsing $TUNE_EXT
+       decode_size $1 $BLOCKSIZE
+       FSFORCE=$FORCE
+
+       if [ "$NEWBLOCKCOUNT" -lt "$BLOCKCOUNT" -o "$EXTOFF" -eq 1 ]; then
+               detect_mounted && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount
+               REMOUNT=$MOUNTED
+               if test -n "$MOUNTED" ; then
+                       # Forced fsck -f for umounted extX filesystem.
+                       case "$-" in
+                         *i*) dry $FSCK $YES -f "$VOLUME" ;;
+                         *) dry $FSCK -f -p "$VOLUME" ;;
+                       esac
+               fi
+       fi
+
+       verbose "Resizing filesystem on device \"$VOLUME\" to $NEWSIZE bytes ($BLOCKCOUNT -> $NEWBLOCKCOUNT blocks of $BLOCKSIZE bytes)"
+       dry $RESIZE_EXT $FSFORCE "$VOLUME" $NEWBLOCKCOUNT
+}
+
+#############################
+# Resize reiserfs filesystem
+# - unmounted for upsize
+# - unmounted for downsize
+#############################
+resize_reiser() {
+       detect_mounted && verbose "ReiserFS resizes only unmounted filesystem" && try_umount
+       REMOUNT=$MOUNTED
+       verbose "Parsing $TUNE_REISER \"$VOLUME\""
+       for i in $(LANG=C $TUNE_REISER "$VOLUME"); do
+               case "$i" in
+                 "Blocksize"*) BLOCKSIZE=${i##*: } ;;
+                 "Count of blocks"*) BLOCKCOUNT=${i##*: } ;;
+               esac
+       done
+       validate_parsing $TUNE_REISER
+       decode_size $1 $BLOCKSIZE
+       verbose "Resizing \"$VOLUME\" $BLOCKCOUNT -> $NEWBLOCKCOUNT blocks ($NEWSIZE bytes, bs: $NEWBLOCKCOUNT)"
+       if [ -n "$YES" ]; then
+               echo y | dry $RESIZE_REISER -s $NEWSIZE "$VOLUME"
+       else
+               dry $RESIZE_REISER -s $NEWSIZE "$VOLUME"
+       fi
+}
+
+########################
+# Resize XFS filesystem
+# - mounted for upsize
+# - cannot downsize
+########################
+resize_xfs() {
+       detect_mounted
+       MOUNTPOINT=$MOUNTED
+       if [ -z "$MOUNTED" ]; then
+               MOUNTPOINT=$TEMPDIR
+               temp_mount || error "Cannot mount Xfs filesystem"
+       fi
+       verbose "Parsing $TUNE_XFS \"$MOUNTPOINT\""
+       for i in $(LANG=C $TUNE_XFS "$MOUNTPOINT"); do
+               case "$i" in
+                 "data"*) BLOCKSIZE=${i##*bsize=} ; BLOCKCOUNT=${i##*blocks=} ;;
+               esac
+       done
+       BLOCKSIZE=${BLOCKSIZE%%[^0-9]*}
+       BLOCKCOUNT=${BLOCKCOUNT%%[^0-9]*}
+       validate_parsing $TUNE_XFS
+       decode_size $1 $BLOCKSIZE
+       if [ $NEWBLOCKCOUNT -gt $BLOCKCOUNT ]; then
+               verbose "Resizing Xfs mounted on \"$MOUNTPOINT\" to fill device \"$VOLUME\""
+               dry $RESIZE_XFS $MOUNTPOINT
+       elif [ $NEWBLOCKCOUNT -eq $BLOCKCOUNT ]; then
+               verbose "Xfs filesystem already has the right size"
+       else
+               error "Xfs filesystem shrinking is unsupported"
+       fi
+}
+
+####################
+# Resize filesystem
+####################
+resize() {
+       NEWSIZE=$2
+       detect_fs "$1"
+       detect_device_size
+       verbose "Device \"$VOLUME\" size is $DEVSIZE bytes"
+       # if the size parameter is missing use device size
+       #if [ -n "$NEWSIZE" -a $NEWSIZE <
+       test -z "$NEWSIZE" && NEWSIZE=${DEVSIZE}b
+       IFS=$NL
+       case "$FSTYPE" in
+         "ext3"|"ext2"|"ext4") resize_ext $NEWSIZE ;;
+         "reiserfs") resize_reiser $NEWSIZE ;;
+         "xfs") resize_xfs $NEWSIZE ;;
+         *) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool" ;;
+       esac || error "Resize $FSTYPE failed"
+       cleanup 0
+}
+
+####################################
+# Calclulate diff between two dates
+#  LANG=C input is expected the
+#  only one supported
+####################################
+diff_dates() {
+         echo $(( $($DATE -u -d"$1" +%s 2>/dev/null) - $($DATE -u -d"$2" +%s 2>/dev/null) ))
+}
+
+###################
+# Check filesystem
+###################
+check() {
+       detect_fs "$1"
+       if detect_mounted ; then
+               verbose "Skipping filesystem check for device \"$VOLUME\" as the filesystem is mounted on $MOUNTED";
+               cleanup 3
+       fi
+
+       case "$FSTYPE" in
+         "ext2"|"ext3"|"ext4")
+               IFS_CHECK=$IFS
+               IFS=$NL
+               for i in $(LANG=C $TUNE_EXT -l "$VOLUME"); do
+                       case "$i" in
+                         "Last mount"*) LASTMOUNT=${i##*: } ;;
+                         "Last checked"*) LASTCHECKED=${i##*: } ;;
+                       esac
+               done
+               case "$LASTMOUNT" in
+                 *"n/a") ;; # nothing to do - system was not mounted yet
+                 *)
+                       LASTDIFF=$(diff_dates $LASTMOUNT $LASTCHECKED)
+                       if test "$LASTDIFF" -gt 0 ; then
+                               verbose "Filesystem has not been checked after the last mount, using fsck -f"
+                               FORCE="-f"
+                       fi
+                       ;;
+               esac
+               IFS=$IFS_CHECK
+       esac
+
+       case "$FSTYPE" in
+         "xfs") dry $XFS_CHECK "$VOLUME" ;;
+         *)    # check if executed from interactive shell environment
+               case "$-" in
+                 *i*) dry $FSCK $YES $FORCE "$VOLUME" ;;
+                 *) dry $FSCK $FORCE -p "$VOLUME" ;;
+               esac
+       esac
+}
+
+#############################
+# start point of this script
+# - parsing parameters
+#############################
+trap "cleanup 2" 2
+
+# test if we are not invoked recursively
+test -n "$FSADM_RUNNING" && exit 0
+
+# test some prerequisities
+test -n "$TUNE_EXT" -a -n "$RESIZE_EXT" -a -n "$TUNE_REISER" -a -n "$RESIZE_REISER" \
+  -a -n "$TUNE_XFS" -a -n "$RESIZE_XFS" -a -n "$MOUNT" -a -n "$UMOUNT" -a -n "$MKDIR" \
+  -a -n "$RMDIR" -a -n "$BLOCKDEV" -a -n "$BLKID" -a -n "$GREP" -a -n "$READLINK" \
+  -a -n "$DATE" -a -n "$FSCK" -a -n "$XFS_CHECK" -a -n "LVM" \
+  || error "Required command definitions in the script are missing!"
+
+$LVM version >/dev/null 2>&1 || error "Could not run lvm binary '$LVM'"
+$($READLINK -e / >/dev/null 2>&1) || READLINK_E="-f"
+TEST64BIT=$(( 1000 * 1000000000000 ))
+test $TEST64BIT -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic"
+$(echo Y | $GREP Y >/dev/null) || error "Grep does not work properly"
+test $($DATE -u -d"Jan 01 00:00:01 1970" +%s) -eq 1 || error "Date translation does not work"
+
+
+if [ "$#" -eq 0 ] ; then
+       tool_usage
+fi
+
+while [ "$#" -ne 0 ]
+do
+        case "$1" in
+         "") ;;
+         "-h"|"--help") tool_usage ;;
+         "-v"|"--verbose") VERB="-v" ;;
+         "-n"|"--dry-run") DRY=1 ;;
+         "-f"|"--force") FORCE="-f" ;;
+         "-e"|"--ext-offline") EXTOFF=1 ;;
+         "-y"|"--yes") YES="-y" ;;
+         "-l"|"--lvresize") DO_LVRESIZE=1 ;;
+         "check") CHECK="$2" ; shift ;;
+         "resize") RESIZE="$2"; NEWSIZE="$3" ; shift 2 ;;
+         *) error "Wrong argument \"$1\". (see: $TOOL --help)"
+       esac
+       shift
+done
+
+if [ -n "$CHECK" ]; then
+       check "$CHECK"
+elif [ -n "$RESIZE" ]; then
+       export FSADM_RUNNING="fsadm"
+       resize "$RESIZE" "$NEWSIZE"
+else
+       error "Missing command. (see: $TOOL --help)"
+fi
diff --git a/scripts/last_cvs_update.sh b/scripts/last_cvs_update.sh
new file mode 100755 (executable)
index 0000000..97b77c1
--- /dev/null
@@ -0,0 +1,163 @@
+#!/bin/bash
+#$Header: /sourceware/projects/lvm2-home/cvsfiles/LVM2/scripts/last_cvs_update.sh,v 1.2 2010/05/14 11:33:21 zkabelac Exp $
+################################################################################
+##
+##    Copyright 2001 Sistina Software, Inc.
+##
+##    This is free software released under the GNU General Public License.
+##    There is no warranty for this software.  See the file COPYING for
+##    details.
+##
+##    See the file CONTRIBUTORS for a list of contributors.
+##
+##    This file is maintained by:
+##      AJ Lewis <lewis@sistina.com>
+## 
+##    File name: last_cvs_update.sh
+##
+##    Description: displays the last file updated by CVS commands and the date
+##                 it was updated.  May be given a relative or absolute path.
+##                 Based on this information, you should be able to do a 
+##                 cvs co -D $date GFS and get the same version of the source
+##                 tree as this tool was run on.
+##                 
+##                 Will also give you the CVS tag the source tree is based off
+##                 off when appropriate
+## 
+##                 Output format:
+##                 [Tag:  $TAG]
+##                 The last file updated by CVS was:
+##                 $path/$file
+##                 on
+##                 $date
+##
+################################################################################
+
+if [[ -z $1 ]];
+ then path=.;
+else
+ if [[ $1 == "-h" ]];
+   then echo "usage: $0 [ -h | path ]"
+        exit 0;
+ else
+   if [[ -d $1 ]]
+     then path=$1;
+   else
+     echo "usage: $0 [ -h | path ]"
+     exit 0;
+   fi
+ fi
+fi
+
+# grab the tag from the path passed in
+if [[ -f $path/CVS/Tag ]];
+  then echo "Tag: " `cat $path/CVS/Tag | sed -e 's/^[NT]//'`
+fi
+
+awk '
+BEGIN {                            
+  FS = "/"                         
+}
+{
+    # find the path for the Entries file
+    path = FILENAME
+    sub(/^\.\//, "", path)
+    
+    # remove the CVS part of it
+    sub(/CVS\/Entries/, "", path)
+    
+    # add the path the the filename that was modified, and put the date it was
+    # modified in as well
+    print path $2 " " $4
+
+}' `find $path -name "Entries" -printf "%h/%f "` | awk '
+# converts string name of month the a numeral
+function cvt_month(month) {
+  if(month == "Jan")
+    return 0
+  if(month == "Feb")
+    return 1
+  if(month == "Mar")
+    return 2
+  if(month == "Apr")
+    return 3
+  if(month == "May")
+    return 4
+  if(month == "Jun")
+    return 5
+  if(month == "Jul")
+    return 6
+  if(month == "Aug")
+    return 7
+  if(month == "Sep")
+    return 8
+  if(month == "Oct")
+    return 9
+  if(month == "Nov")
+    return 10
+  if(month == "Dec")
+    return 11
+  return -1
+}
+BEGIN {                            
+  FS = " "                         
+  latest=""
+  maxyear = 0
+  maxdate = 0
+  maxmonth = "Jan"
+  maxtime = "00:00:00"
+}
+{
+   # check year first
+   if (maxyear < $6) {
+      date = $2 " " $3 " " $4 " " $5 " " $6
+      file = $1
+      maxyear = $6
+      maxmonth = $3
+      maxdate = $4
+      maxtime = $5
+   }
+   else {
+      if (maxyear == $6) {
+        # then month if year is the same
+        if (cvt_month(maxmonth) < cvt_month($3)) {
+          date = $2 " " $3 " " $4 " " $5 " " $6
+          file = $1
+         maxmonth = $3
+         maxdate = $4
+         maxtime = $5
+        }
+        else {
+          if (cvt_month(maxmonth) == cvt_month($3)) {
+           #then date if month is the same
+            if (maxdate < $4) {
+              date = $2 " " $3 " " $4 " " $5 " " $6
+              file = $1
+             maxdate = $4
+             maxtime = $5
+           }
+           else {
+             if (maxdate == $4) {
+               # then time if date is the same
+               if (maxtime < $5) {
+                 date = $2 " " $3 " " $4 " " $5 " " $6
+                  file = $1
+                 maxtime = $5
+               }
+              }
+           }
+         }
+        }
+      }
+   }
+}
+
+END {
+   # strip leading "./" from filename
+   sub(/^\.\//, "", file)
+   print "The last file updated by CVS was:"
+   print file 
+   print "on"
+   print date " GMT"
+}'
+
diff --git a/scripts/lvm2_monitoring_init_red_hat.in b/scripts/lvm2_monitoring_init_red_hat.in
new file mode 100644 (file)
index 0000000..4812c75
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# Copyright (C) 2007-2009 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+#
+# This file is part of LVM2.
+# It is required for the proper handling of failures of LVM2 mirror
+# devices that were created using the -m option of lvcreate.
+#
+#
+# chkconfig: 12345 02 99
+# description: Starts and stops dmeventd monitoring for lvm2
+#
+# For Red-Hat-based distributions such as Fedora, RHEL, CentOS.
+#             
+### BEGIN INIT INFO
+# Provides: lvm2-monitor
+# Required-Start: $local_fs
+# Required-Stop: $local_fs
+# Default-Start: 1 2 3 4 5
+# Default-Stop: 0 6
+# Short-Description: Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
+### END INIT INFO
+
+. /etc/init.d/functions
+
+DAEMON=lvm2-monitor
+
+exec_prefix=@exec_prefix@
+sbindir=@sbindir@
+
+VGCHANGE=${sbindir}/vgchange
+VGS=${sbindir}/vgs
+
+LOCK_FILE="/var/lock/subsys/$DAEMON"
+
+WARN=1
+
+start()
+{
+       ret=0
+       # TODO do we want to separate out already active groups only?
+       VGSLIST=`$VGS --noheadings -o name 2> /dev/null`
+       for vg in $VGSLIST
+       do
+           action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y --poll y $vg || ret=$?
+       done
+
+       return $ret
+}
+
+
+stop()
+{
+       ret=0
+       # TODO do we want to separate out already active groups only?
+       if test "$WARN" = "1"; then
+          echo "Not stopping monitoring, this is a dangerous operation. Please use force-stop to override."
+          return 1
+       fi
+       VGSLIST=`$VGS --noheadings -o name 2> /dev/null`
+       for vg in $VGSLIST
+       do
+           action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n $vg || ret=$?
+       done
+       return $ret
+}
+
+rtrn=1
+
+# See how we were called.
+case "$1" in
+  start)
+       start
+       rtrn=$?
+       [ $rtrn = 0 ] && touch $LOCK_FILE
+       ;;
+
+  force-stop)
+       WARN=0
+       stop
+       rtrn=$?
+       [ $rtrn = 0 ] && rm -f $LOCK_FILE
+       ;;
+
+  stop)
+       test "$runlevel" = "0" && WARN=0
+       test "$runlevel" = "6" && WARN=0
+       stop
+       rtrn=$?
+       [ $rtrn = 0 ] && rm -f $LOCK_FILE
+       ;;
+
+  restart)
+       WARN=0
+       if stop
+       then
+               start
+       fi 
+       rtrn=$?
+       ;;
+
+  status)
+       # TODO anyone with an idea how to dump monitored volumes?
+       ;;
+
+  *)
+       echo $"Usage: $0 {start|stop|restart|status|force-stop}"
+       ;;
+esac
+
+exit $rtrn
diff --git a/scripts/lvm2_monitoring_init_rhel4 b/scripts/lvm2_monitoring_init_rhel4
new file mode 100644 (file)
index 0000000..0a42f55
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+#
+# This file is part of LVM2.
+# It is required for the proper handling of failures of LVM2 mirror
+# devices that were created using the -m option of lvcreate.
+#
+#
+# chkconfig: 12345 02 99
+# description: Starts and stops dmeventd monitoring for lvm2
+#             
+### BEGIN INIT INFO
+# Provides: 
+### END INIT INFO
+
+. /etc/init.d/functions
+
+VGCHANGE="/usr/sbin/vgchange"
+WARN=1
+
+start()
+{
+       ret=0
+       # TODO do we want to separate out already active groups only?
+       VGS=`vgs --noheadings -o name 2> /dev/null`
+       for vg in $VGS
+       do
+           action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y $vg || ret=$?
+       done
+
+       return $ret
+}
+
+
+stop()
+{
+       ret=0
+       # TODO do we want to separate out already active groups only?
+       if test "$WARN" = "1"; then
+          echo "Not stopping monitoring, this is a dangerous operation. Please use force-stop to override."
+          return 1
+       fi
+       VGS=`vgs --noheadings -o name 2> /dev/null`
+       for vg in $VGS
+       do
+           action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n $vg || ret=$?
+       done
+       return $ret
+}
+
+result=1
+
+# See how we were called.
+case "$1" in
+  start)
+       start
+       result=$?
+       ;;
+
+  force-stop)
+       WARN=0
+       stop
+       result=$?
+       ;;
+
+  stop)
+       test "$runlevel" = "0" && WARN=0
+       test "$runlevel" = "6" && WARN=0
+       stop
+       result=$?
+       ;;
+
+  restart)
+       WARN=0
+       if stop
+       then
+               start
+       fi 
+       result=$?
+       ;;
+
+  status)
+       # TODO anyone with an idea how to dump monitored volumes?
+       ;;
+
+  *)
+       echo $"Usage: $0 {start|stop|restart|status|force-stop}"
+       ;;
+esac
+
+exit $result
diff --git a/scripts/lvm2create_initrd/Makefile b/scripts/lvm2create_initrd/Makefile
new file mode 100644 (file)
index 0000000..acb189d
--- /dev/null
@@ -0,0 +1,6 @@
+all:
+       echo "Nothing to do for make all"
+
+manpage:
+       pod2man --center="create LVM2 initrd" --name='lvm2create_initrd' --section=8 -r 'lvm2create_initrd' ./lvm2create_initrd.pod > lvm2create_initrd.8
+
diff --git a/scripts/lvm2create_initrd/README b/scripts/lvm2create_initrd/README
new file mode 100644 (file)
index 0000000..ff5e9de
--- /dev/null
@@ -0,0 +1,40 @@
+http://poochiereds.net/svn/lvm2/
+
+This is the lvm2create_initrd script written by Miguel Cabeca, with some small
+modifications by myself.
+
+Here are some other requirements and tips for using it:
+
+1) this script uses busybox on the initrd image, hence busybox needs to be
+installed when you create your initrd.
+
+2) Make sure /etc/lvm/lvm.conf is set up correctly before running this. In
+particular, if you're using LVM on RAID, make sure that you have a filter that
+excludes the RAID component devices (this may not be necessary with the latest
+patch by Luca Berra, but it doesn't hurt).
+
+3) This initrd image does not support modules. If you need to plug in any
+kernel modules during the initrd phase, then you'll need to hand-modify the
+image.
+
+4) The generated initrd image supports an 'lvm2rescue' mode as well. If you add
+the parameter 'lvmrescue' on the kernel command line, it will run a shell at
+the end of the initrd 'init' script. This can be helpful when trying to fix a
+corrupt root volume or root LVM2 volume group.
+
+5) No userspace md tools are installed, so if you're using LVM on RAID, then
+you'll probably want to mark your RAID partitions as type 'fd' so that the
+kernel will start them automagically (or hand-modify the image).
+
+6) I'm not sure if devfs will work with this or not. udev, however does work,
+and is recommended. Because the dm-* devices use dynamically allocated major
+and minor numbers, kernel upgrades and the like can renumber your devices. To
+fix this, you need to run a 'vgscan --mknodes' prior to fscking and mounting
+your rootfs. Doing this with a static /dev creates a problem though -- you
+will be modifying the root filesystem before it has been fsck'ed. udev gets
+around this by mounting a ramdisk over /dev, but you'll probably need to add
+a startup script that creates devices in /dev. The lvm2udev script in this
+directory is an example of such a beast.
+
+--
+Jeffrey Layton <jtlayton@poochiereds.net>
diff --git a/scripts/lvm2create_initrd/lvm2create_initrd b/scripts/lvm2create_initrd/lvm2create_initrd
new file mode 100644 (file)
index 0000000..9d012d5
--- /dev/null
@@ -0,0 +1,502 @@
+#!/bin/bash
+#
+# lvm2create_initrd 
+#
+# Miguel Cabeca 
+# cabeca (at) ist (dot) utl (dot) pt
+#
+# Inspiration to write this script came from various sources
+#
+# Original LVM lvmcreate_initrd: ftp://ftp.sistina.com/pub/LVM/1.0/
+# Kernel initrd.txt: http://www.kernel.org/
+# EVMS INSTALL.initrd & linuxrc: http://evms.sourceforge.net/
+# Jeffrey Layton's lvm2create_initrd: http://poochiereds.net/svn/lvm2create_initrd/
+# Christophe Saout's initrd & linuxrc: http://www.saout.de/misc/
+#
+# This script was only tested with kernel 2.6 with everything required to boot 
+# the root filesystem built-in (not as modules). Ex: SCSI or IDE, RAID, device mapper
+# It does not support devfs as it is deprecated in the 2.6 kernel series
+#
+# It needs lvm2 tools, busybox, pivot_root, MAKEDEV
+#
+# It has been tested on Debian sid (unstable) only
+#
+# Changelog
+# 26/02/2004   Initial release -- Miguel Cabeca
+# 27/02/2004   Removed the BUSYBOXSYMLINKS var. The links are now determined at runtime.
+#              some changes in init script to call a shell if something goes wrong. -- Miguel Cabeca
+# 19/04/2004    Several small changes. Pass args to init so single user mode works. Add some
+#               PATH entries to /sbin/init shell script so chroot works without /usr mounted. Remove
+#               mkdir /initrd so we don't cause problems if root filesystem is corrupted. -- Jeff Layton
+# 15/05/2004   initial support for modules, create lvm.conf from lvm dumpconfig, other cleanups -- Jeff Layton
+# 14/11/2006   Update handling of ldd output to handle hardcoded library links and virtual dll linux-gate.
+#              Add support for Gentoo-style MAKEDEV. Remove hardcoded BINUTILS paths -- Douglas Mayle
+#
+# Copyright Miguel Cabeca, Jeffrey Layton, 2004
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU 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
+#
+# $Id: lvm2create_initrd,v 1.2 2006/11/21 22:41:56 agk Exp $
+
+TMPMNT=/tmp/mnt.$$
+DEVRAM=/tmp/initrd.$$
+
+# set defaults
+BINFILES=${BINFILES:-"`which lvm` `which bash` `which busybox` `which pivot_root`"}
+BASICDEVICES=${BASICDEVICES:-"std consoleonly fd"}
+BLOCKDEVICES=${BLOCKDEVICES:-"md hda hdb hdc hdd sda sdb sdc sdd"}
+MAKEDEV=${MAKEDEV:-"debian"}
+
+# Uncomment this if you want to disable automatic size detection
+#INITRDSIZE=4096
+
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH
+
+usage () {
+       echo "Create an initial ramdisk image for LVM2 root filesystem"
+       echo "$cmd: [-h] [-v] [-c lvm.conf] [-m modulelist] [-e extrafiles] -r [raiddevs] [-R mdadm.conf] [-M style] [kernel version]"
+       echo "      -h|--help      print this usage message"
+       echo "      -v|--verbose   verbose progress messages"
+       echo "      -c|--lvmconf   path to lvm.conf (/etc/lvm/lvm.conf)"
+       echo "      -m|--modules   modules to copy to initrd image"
+       echo "      -e|--extra     extra files to add to initrd"
+       echo "      -r|--raid      raid devices to start in initrd"
+       echo "      -R|--raidconf  location of mdadm.conf file to include"
+       echo "      -M|--makedev   set MAKEDEV type (debian or redhat)"
+}
+
+verbose () {
+   [ "$VERBOSE" ] && echo "`echo $cmd | tr '[a-z0-9/_]' ' '` -- $1" || true
+}
+
+cleanup () {
+  [ "`mount | grep $DEVRAM`" ] && verbose "unmounting $DEVRAM" && umount $DEVRAM
+  [ -f $DEVRAM ] && verbose "removing $DEVRAM" && rm $DEVRAM
+  [ -d $TMPMNT ] && verbose "removing $TMPMNT" && rmdir $TMPMNT
+  verbose "exit with code $1"
+  exit $1
+}
+
+trap "
+  verbose 'Caught interrupt'
+  echo 'Bye bye...'
+  cleanup 1
+" 1 2 3 15
+
+create_init () {
+   cat << 'INIT' > $TMPMNT/sbin/init
+#!/bin/bash
+
+# include in the path some dirs from the real root filesystem
+# for chroot, blockdev
+PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200:/initrd/bin:/initrd/sbin"
+PRE="initrd:"
+
+do_shell(){
+    /bin/echo
+    /bin/echo "*** Entering LVM2 rescue shell. Exit shell to continue booting. ***"
+    /bin/echo
+    /bin/bash
+}
+
+echo "$PRE Remounting / read/write"
+mount -t ext2 -o remount,rw /dev/ram0 /
+
+
+# We need /proc for device mapper
+echo "$PRE Mounting /proc"
+mount -t proc none /proc
+
+# plug in modules listed in /etc/modules
+if [ -f /etc/modules ]; then
+    echo -n "$PRE plugging in kernel modules:"
+    cat /etc/modules |
+    while read module; do
+        echo -n " $module"
+        modprobe $module
+    done
+    echo '.'
+fi
+
+# start raid devices if raid_autostart file exists
+if [ -f /etc/raid_autostart ]; then
+    if [ ! -f /etc/mdadm/mdadm.conf ]; then
+        mdoptions='--super-minor=dev'
+    fi
+    cat /etc/raid_autostart|
+    while read dev; do
+       echo "Starting RAID device $dev"
+        /sbin/mdadm --assemble $dev $mdoptions 
+    done
+fi
+
+# Create the /dev/mapper/control device for the ioctl
+# interface using the major and minor numbers that have been allocated
+# dynamically.
+
+echo -n "$PRE Finding device mapper major and minor numbers "
+
+MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices)
+MINOR=$(sed -n 's/^ *\([0-9]\+\) \+device-mapper$/\1/p' /proc/misc)
+if test -n "$MAJOR" -a -n "$MINOR" ; then
+       mkdir -p -m 755 /dev/mapper
+       mknod -m 600 /dev/mapper/control c $MAJOR $MINOR
+fi
+
+echo "($MAJOR,$MINOR)"
+
+# Device-Mapper dynamically allocates all device numbers. This means it is possible 
+# that the root volume specified to LILO or Grub may have a different number when the
+# initrd runs than when the system was last running. In order to make sure the
+# correct volume is mounted as root, the init script must determine what the
+# desired root volume name is by getting the LVM2 root volume name from the kernel command line. In order for
+# this to work correctly, "lvm2root=/dev/Volume_Group_Name/Root_Volume_Name" needs to be passed 
+# to the kernel command line (where Root_Volume_Name is replaced by your actual
+# root volume's name.
+for arg in `cat /proc/cmdline`; do
+       echo $arg | grep '^lvm2root=' > /dev/null
+       if [ $? -eq 0 ]; then
+               rootvol=${arg#lvm2root=}
+               break
+       fi
+done
+
+echo "$PRE Activating LVM2 volumes"
+
+
+# run a shell if we're passed lvm2rescue on commandline
+grep lvm2rescue /proc/cmdline 1>/dev/null 2>&1
+if [ $? -eq 0 ]; then
+    lvm vgchange --ignorelockingfailure -P -a y
+    do_shell
+else
+    lvm vgchange --ignorelockingfailure -a y
+fi
+
+echo "$PRE Mounting root filesystem $rootvol ro"
+mkdir /rootvol
+if ! mount -t auto -o ro $rootvol /rootvol; then
+       echo "\t*FAILED*";
+       do_shell
+fi
+
+echo "$PRE Umounting /proc"
+umount /proc
+
+echo "$PRE Changing roots"
+cd /rootvol
+if ! pivot_root . initrd ; then
+       echo "\t*FAILED*"
+       do_shell
+fi
+
+echo "$PRE Proceeding with boot..."
+
+exec chroot . /bin/sh -c "umount /initrd; blockdev --flushbufs /dev/ram0 ; exec /sbin/init $*" < dev/console > dev/console 2>&1
+
+INIT
+   chmod 555 $TMPMNT/sbin/init
+}
+
+# create lvm.conf file from dumpconfig. Just use filter options
+create_lvmconf () {
+    echo 'devices {' > $TMPMNT/etc/lvm/lvm.conf
+    lvm dumpconfig | grep 'filter=' >> $TMPMNT/etc/lvm/lvm.conf
+    echo '}' >> $TMPMNT/etc/lvm/lvm.conf
+}
+
+#
+# Main
+#
+
+cmd=`basename $0`
+
+VERSION=`uname -r`
+
+while [ $# -gt 0 ]; do
+   case $1 in
+   -h|--help) usage; exit 0;;
+   -v|--verbose)  VERBOSE="y";;
+   -c|--lvmconf)  LVMCONF=$2; shift;;
+   -m|--modules)  MODULES=$2; shift;;
+   -e|--extra)    EXTRAFILES=$2; shift;;
+   -r|--raid)     RAID=$2; shift;;
+   -R|--raidconf) RAIDCONF=$2; shift;;
+   -M|--makedev)  MAKEDEV=$2; shift;;
+   [2-9].[0-9]*.[0-9]*) VERSION=$1;;
+   *) echo "$cmd -- invalid option '$1'"; usage; exit 0;;
+   esac
+   shift
+done
+
+INITRD=${INITRD:-"/boot/initrd-lvm2-$VERSION.gz"}
+
+echo "$cmd -- make LVM initial ram disk $INITRD"
+echo ""
+
+if [ -n "$RAID" ]; then
+    BINFILES="$BINFILES /sbin/mdadm"
+    RAIDCONF=${RAIDCONF:-"/etc/mdadm/mdadm.conf"}
+    if [ -r $RAIDCONF ]; then
+       EXTRAFILES="$EXTRAFILES $RAIDCONF"
+    else
+        echo "$cmd -- WARNING: No $RAIDCONF! Your RAID device minor numbers must match their superblock values!"
+    fi
+fi
+
+# add modprobe if we declared any modules
+if [ -n "$MODULES" ]; then
+    BINFILES="$BINFILES /sbin/modprobe /sbin/insmod /sbin/rmmod"
+fi
+
+for a in $BINFILES $EXTRAFILES; do
+    if [ ! -r "$a" ] ; then
+       echo "$cmd -- ERROR: you need $a"
+       exit 1;
+    fi;
+done
+
+# Figure out which shared libraries we actually need in our initrd
+echo "$cmd -- finding required shared libraries"
+verbose "BINFILES: `echo $BINFILES`"
+
+# We need to strip certain lines from ldd output.  This is the full output of an example ldd:
+#lvmhost~ # ldd /sbin/lvm /bin/bash
+#/sbin/lvm:
+#        not a dynamic executable
+#/bin/bash:
+#        linux-gate.so.1 =>  (0xbfffe000)
+#        libncurses.so.5 => /lib/libncurses.so.5 (0xb7ee3000)
+#        libdl.so.2 => /lib/libdl.so.2 (0xb7edf000)
+#        libc.so.6 => /lib/libc.so.6 (0xb7dc1000)
+#        /lib/ld-linux.so.2 (0xb7f28000)
+#
+# 1) Lines with a ":" contain the name of the original binary we're examining, and so are unnecessary.
+#    We need to strip them because they contain "/", and can be confused with links with a hardcoded path.
+# 2) The linux-gate library is a virtual dll that does not exist on disk, but is instead loaded automatically
+#    into the process space, and can't be copied to the ramdisk
+#
+# After these lines have been stripped, we're interested in the lines remaining if they
+# 1) Contain "=>" because they are pathless links, and the value following the token is the path on the disk
+# 2) Contain "/" because it's a link with a hardcoded path, and so we're interested in the link itself.
+LIBFILES=`ldd $BINFILES 2>/dev/null |grep -v -E \(linux-gate\|:\) | awk '{if (/=>/) { print $3 } else if (/\//) { print $1 }}' | sort -u`
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR figuring out needed shared libraries"
+   exit 1
+fi
+
+verbose "Shared libraries needed: `echo $LIBFILES`"
+
+INITRDFILES="$BINFILES $LIBFILES $MODULES $EXTRAFILES"
+
+# tack on stuff for modules if we declared any and the files exist
+if [ -n "$MODULES" ]; then
+    if [ -f "/etc/modprobe.conf" ]; then
+       INITRDFILES="$INITRDFILES /etc/modprobe.conf"
+    fi
+    if [ -f "/lib/modules/modprobe.conf" ]; then
+       INITRDFILES="$INITRDFILES /lib/modules/modprobe.conf"
+    fi
+fi
+
+# Calculate the the size of the ramdisk image.
+# Don't forget that inodes take up space too, as does the filesystem metadata.
+echo "$cmd -- calculating initrd filesystem parameters"
+if [ -z "$INITRDSIZE" ]; then
+   echo "$cmd -- calculating loopback file size"
+   verbose "finding size"
+   INITRDSIZE="`du -Lck $INITRDFILES | tail -1 | cut -f 1`"
+   verbose "minimum: $INITRDSIZE kB for files + inodes + filesystem metadata"
+   INITRDSIZE=`expr $INITRDSIZE + 512`  # enough for ext2 fs + a bit
+fi
+
+echo "$cmd -- making loopback file ($INITRDSIZE kB)"
+verbose "using $DEVRAM as a temporary loopback file"
+dd if=/dev/zero of=$DEVRAM count=$INITRDSIZE bs=1024 > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR creating loopback file"
+   cleanup 1
+fi
+
+echo "$cmd -- making ram disk filesystem"
+verbose "mke2fs -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE"
+[ "$VERBOSE" ] && OPT_Q="" || OPT_Q="-q"
+mke2fs $OPT_Q -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR making ram disk filesystem"
+   echo "$cmd -- ERROR you need to use mke2fs >= 1.14 or increase INITRDSIZE"
+   cleanup 1
+fi
+
+verbose "creating mountpoint $TMPMNT"
+mkdir $TMPMNT
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR making $TMPMNT"
+   cleanup 1
+fi
+
+echo "$cmd -- mounting ram disk filesystem"
+verbose "mount -o loop $DEVRAM $TMPMNT"
+mount -oloop $DEVRAM $TMPMNT
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR mounting $DEVRAM on $TMPMNT"
+   cleanup 1
+fi
+
+verbose "creating basic set of directories in $TMPMNT"
+(cd $TMPMNT; mkdir bin dev etc lib proc sbin var)
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR creating directories in $TMPMNT"
+   cleanup 1
+fi
+
+# Add some /dev files. We have to handle different types of MAKEDEV invocations
+# here, so this is rather messy.
+RETCODE=0
+echo "$cmd -- adding required /dev files"
+verbose "BASICDEVICES: `echo $BASICDEVICES`"
+verbose "BLOCKDEVICES: `echo $BLOCKDEVICES`"
+[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q=""
+case "$MAKEDEV" in 
+debian)
+    (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES)
+    RETCODE=$?
+    ;;
+redhat)
+    (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q -d $TMPMNT/dev -m 2)
+    RETCODE=$?
+    ;;
+gentoo)
+    (cd $TMPMNT/dev; /usr/sbin/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES)
+    RETCODE=$?
+    ;;
+*)
+    echo "$cmd -- ERROR: $MAKEDEV is not a known MAKEDEV style."
+    RETCODE=1
+    ;;
+esac
+
+
+if [ $RETCODE -ne 0 ]; then
+   echo "$cmd -- ERROR adding /dev files"
+   cleanup 1
+fi
+
+
+# copy necessary files to ram disk
+echo "$cmd -- copying initrd files to ram disk"
+[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="--quiet"
+verbose "find \$INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT"
+find $INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR cpio to ram disk"
+   cleanup 1
+fi
+
+
+echo "$cmd -- creating symlinks to busybox"
+shopt -s extglob
+[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q=""
+BUSYBOXSYMLINKS=`busybox 2>&1| awk '/^Currently defined functions:$/ {i++;next} i'|tr ',\t\n' ' '`
+for link in ${BUSYBOXSYMLINKS//@(linuxrc|init|busybox)}; do 
+       ln -s $OPT_Q busybox $TMPMNT/bin/$link;
+done
+shopt -u extglob
+
+echo "$cmd -- creating new $TMPMNT/sbin/init"
+create_init
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR creating init"
+   cleanup
+   exit 1
+fi
+
+# copy LVMCONF into place or create a stripped down one from lvm dumpconfig
+mkdir -p $TMPMNT/etc/lvm
+if [ -n "$LVMCONF" ]; then
+    echo "$cmd -- copying $LVMCONF to $TMPMNT/etc/lvm/lvm.conf"
+    if [ -f "$LVMCONF" ]; then
+        cp $LVMCONF $TMPMNT/etc/lvm/lvm.conf
+    else
+        echo "$cmd -- ERROR: $LVMCONF does not exist!"
+       cleanup
+       exit 1
+    fi
+else
+    echo "$cmd -- creating new $TMPMNT/etc/lvm/lvm.conf"
+    create_lvmconf
+fi
+
+if [ -n "$RAID" ]; then
+    RAIDLIST="$TMPMNT/etc/raid_autostart"
+    echo "$cmd -- creating $RAIDLIST file."
+    for device in $RAID; do
+        echo $device >> $RAIDLIST
+    done
+fi
+
+# create modules.dep and /etc/modules files if needed
+if [ -n "$MODULES" ]; then
+    echo "$cmd -- creating $MODDIR/modules.dep file and $TMPMNT/etc/modules"
+    depmod -b $TMPMNT $VERSION
+    for module in $MODULES; do
+        basename $module | sed 's/\.k\{0,1\}o$//' >> $TMPMNT/etc/modules
+    done
+fi
+
+verbose "removing $TMPMNT/lost+found"
+rmdir $TMPMNT/lost+found
+
+echo "$cmd -- ummounting ram disk"
+umount $DEVRAM
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR umounting $DEVRAM"
+   cleanup 1
+fi
+
+echo "$cmd -- creating compressed initrd $INITRD"
+verbose "dd if=$DEVRAM bs=1k count=$INITRDSIZE | gzip -9"
+dd if=$DEVRAM bs=1k count=$INITRDSIZE 2>/dev/null | gzip -9 > $INITRD
+if [ $? -ne 0 ]; then
+   echo "$cmd -- ERROR creating $INITRD"
+   cleanup 1
+fi
+
+
+cat << FINALTXT
+--------------------------------------------------------
+Your initrd is ready in $INITRD
+
+Don't forget to set root=/dev/ram0 in kernel parameters
+Don't forget to set lvm2root=/dev/VG/LV in kernel parameters, where LV is your root volume
+If you use lilo try adding/modifying an entry similar to this one in lilo.conf:
+
+image=/boot/vmlinuz-lvm2-$VERSION
+        label="ramdisk_LVM"
+        initrd=/boot/initrd-lvm2-$VERSION.gz
+        append="root=/dev/ram0 lvm2root=/dev/system/root <other parameters>"
+
+If using grub try adding/modifying an entry similar to this one in menu.lst
+
+title ramdisk LVM
+        kernel /boot/vmlinuz-lvm2-$VERSION root=/dev/ram0 lvm2root=/dev/system/root <other parameters>
+        initrd /boot/initrd-lvm2-$VERSION.gz
+
+You can also pass lvm2rescue to the kernel to get a shell
+--------------------------------------------------------
+FINALTXT
+
+cleanup 0
+
diff --git a/scripts/lvm2create_initrd/lvm2create_initrd.8 b/scripts/lvm2create_initrd/lvm2create_initrd.8
new file mode 100644 (file)
index 0000000..c9dae9f
--- /dev/null
@@ -0,0 +1,281 @@
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  | will give a
+.\" real vertical bar.  \*(C+ will give a nicer C++.  Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available.  \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.\"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "lvm2create_initrd 8"
+.TH lvm2create_initrd 8 "2004-06-05" "lvm2create_initrd" "create LVM2 initrd"
+.SH "NAME"
+lvm2create_initrd \- create initrd image for booting to root\e\-on\e\-LVM2
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBlvm2create_initrd\fR [ \fB\-h|\-\-help\fR ] [ \fB\-v|\-\-verbose\fR ] [ \fB\-c|\-\-lvmconf\fR \fI/path/to/lvm.conf\fR ] [ \fB\-m|\-\-modules\fR "\fImodule1 module2 ...\fR" ] [ \fB\-e|\-\-extra\fR "\fIfile1 file2 ...\fR" ] [ \fB\-r|\-\-raid\fR "\fI/dev/md1 /dev/md2 ...\fR" ]
+[ \fB\-R|\-\-raidconf\fR \fI/path/to/mdadm.conf\fR ] [ \fB\-M|\-\-makedev\fR \fIstyle\fR ]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+lvm2create_initrd creates an initial ramdisk (initrd) image suitable for booting to system that has an \s-1LVM2\s0 volume as its root filesystem.
+.PP
+To boot to such a setup, you'll
+either need a bootloader that understands \s-1LVM2\s0 volumes, or you'll need a
+filesystem on a regular volume to act as a boot partition (typically mounted
+on /boot).
+.PP
+The resulting initrd image is fairly full\-featured. It can harbor and load
+kernel modules, start \s-1MD\s0 devices, and boot to a shell to perform rescue
+operations.
+.Sh "Booting to your initrd Image:"
+.IX Subsection "Booting to your initrd Image:"
+The filesystem image created is an ext2fs filesystem, hence your kernel must have
+ext2fs built into it statically in order to boot to the image.
+.PP
+Once you create your initrd image, you must pass the correct options to the kernel when
+you boot using it. Your kernel command line should look something like this:
+.PP
+\&\fBroot=/dev/ram0 lvm2root=/dev/rootvg/root [ lvm2rescue ]\fR
+.PP
+of course there may be other options.
+.IP "\fBroot=/dev/ram0\fR" 4
+.IX Item "root=/dev/ram0"
+This option is required. It tells the kernel that the root filesystem should initially
+be set to the ramdisk (/dev/ram0).
+.IP "\fBlvm2root=/dev/rootvg/root\fR" 4
+.IX Item "lvm2root=/dev/rootvg/root"
+This option is also required. It tells the initrd image which \s-1LVM2\s0 device the root filesystem is located on.
+.IP "\fBlvm2rescue\fR" 4
+.IX Item "lvm2rescue"
+Causes the initrd image to run a shell prior to mounting the root filesystem. This is
+helpful in disaster situations where your initrd image is accessable, but there is
+a problem with the root filesystem (corrupted image, incorrect device setup, etc.). This
+option is (of course) optional.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+Most of parameters that can be set via command-line options can also be set
+via environment variables. Options specified on the command-line always take
+precedence.
+.IP "\fB\-h|\-\-help\fR" 4
+.IX Item "-h|--help"
+Display short help text and exit. If used, other options are ignored.
+.IP "\fB\-v|\-\-verbose\fR" 4
+.IX Item "-v|--verbose"
+Turn on extra verbosity for debugging, etc.
+.IP "\fB\-c|\-\-lvmconf\fR \fI/path/to/lvm.conf\fR" 4
+.IX Item "-c|--lvmconf /path/to/lvm.conf"
+Specify an lvm.conf file to include in the image. This is useful if you have
+special device filters or other options you wish to use during the initrd
+stage. If this option is not
+included, then a lvm.conf file is created that contains only the current
+device filter from an \fBlvm dumpconfig\fR. This can also be set via the \fB$LVMCONF\fR
+environment variable.
+.ie n .IP "\fB\-m|\-\-modules\fR ""\fI/path/to/module1.ko /path/to/module2.ko ...\fR""" 4
+.el .IP "\fB\-m|\-\-modules\fR ``\fI/path/to/module1.ko /path/to/module2.ko ...\fR''" 4
+.IX Item "-m|--modules ""/path/to/module1.ko /path/to/module2.ko ..."""
+Specify modules to include and plug in during the initrd phase. This option
+takes a quoted, space-separated list of modules. Full pathnames are required.
+These modules are loaded into the kernel early in the initrd phase of the boot
+process. The current modprobe.conf file is also copied to the initrd image
+as well. This can also be specified via the \fB$MODULES\fR environment variable.
+.ie n .IP "\fB\-e|\-\-extra\fR ""\fI/path/to/file1 /path/to/file2 ...\fR""" 4
+.el .IP "\fB\-e|\-\-extra\fR ``\fI/path/to/file1 /path/to/file2 ...\fR''" 4
+.IX Item "-e|--extra ""/path/to/file1 /path/to/file2 ..."""
+Extra files that should be included in the initrd image. These files will be
+copied to the same location in the initrd image that they are in the current
+filesystem. Again full pathnames are required. This can also be specified via
+the \fB$EXTRAFILES\fR environment variable.
+.ie n .IP "\fB\-r|\-\-raid\fR ""\fI/dev/md1 /dev/md2...\fR""" 4
+.el .IP "\fB\-r|\-\-raid\fR ``\fI/dev/md1 /dev/md2...\fR''" 4
+.IX Item "-r|--raid ""/dev/md1 /dev/md2..."""
+\&\s-1RAID\s0 devices to be started prior to scanning for \s-1LVM2\s0 volume groups. If this
+option is used then then \fBmdadm\fR program must be installed. This can also be
+specified via the \fB$RAID\fR environment variable.
+.ie n .IP "\fB\-R|\-\-raidconf\fR ""\fI/path/to/mdadm.conf\fR""" 4
+.el .IP "\fB\-R|\-\-raidconf\fR ``\fI/path/to/mdadm.conf\fR''" 4
+.IX Item "-R|--raidconf ""/path/to/mdadm.conf"""
+Location of a mdadm.conf file to include. If this is not specified, then no
+files are included, and any devices specified with the \fB\-r\fR option above
+must have minor numbers that match their superblock values. This can also be
+specified via the \fB$RAIDCONF\fR environment variable.
+.IP "\fB\-M|\-\-makedev\fR \fIstyle\fR" 4
+.IX Item "-M|--makedev style"
+Set \s-1MAKEDEV\s0 invocation style. The script currently supports 2 styles of
+\&\s-1MAKEDEV\s0 programs \fIdebian\fR and \fIredhat\fR. The default is \fIdebian\fR. Set
+to \fIredhat\fR if using the RedHat/Fedora binary \s-1MAKEDEV\s0 program. Please send
+a bug report to maintainer if your distrib doesn't work with any of the
+current options.
+.SH "ENVIRONMENT VARIABLES"
+.IX Header "ENVIRONMENT VARIABLES"
+Most of the options to this script can be set via environment variables. In
+situations where both are set, then the command-line options take precedence.
+.IP "\fB$LVMCONF\fR" 4
+.IX Item "$LVMCONF"
+Same as \-c option.
+.IP "\fB$MODULES\fR" 4
+.IX Item "$MODULES"
+Same as \-m option.
+.IP "\fB$EXTRAFILES\fR" 4
+.IX Item "$EXTRAFILES"
+Same as \-e option.
+.IP "\fB$RAID\fR" 4
+.IX Item "$RAID"
+Same as \-r option.
+.IP "\fB$RAIDCONF\fR" 4
+.IX Item "$RAIDCONF"
+Same as \-R option.
+.IP "\fB$MAKEDEV\fR" 4
+.IX Item "$MAKEDEV"
+Same as \-M option.
+.IP "\fB$BASICDEVICES\fR" 4
+.IX Item "$BASICDEVICES"
+Overrides the default value of \f(CW$BASICDEVICES\fR in the script (which is \*(L"std consoleonly fd\*(R"). These values are passed to the \fB\s-1MAKEDEV\s0\fR program to create device
+entries in the initrd image.
+.IP "\fB$BLOCKDEVICES\fR" 4
+.IX Item "$BLOCKDEVICES"
+Overrides the default value of \f(CW$BLOCKDEVICES\fR in the script (which is \*(L"md hda hdb hdc hdd sda sdb sdc sdd\*(R"). This value is passed to the \fB\s-1MAKEDEV\s0\fR program to
+create device entries in the initrd image.
+.IP "\fB$BINFILES\fR" 4
+.IX Item "$BINFILES"
+Overrides the default value of \f(CW$BINFILES\fR (which is \*(L"/lib/lvm\-200/lvm /bin/bash /bin/busybox /sbin/pivot_root\*(R"). The difference between using this and adding
+a file to the \f(CW$EXTRAFILES\fR list above is that libraries that these depend upon are also included. You can still use \f(CW$EXTRAFILES\fR to achieve the same effect, but
+you must resolve library dependencies youself.
+.IP "\fB$INITRDSIZE\fR" 4
+.IX Item "$INITRDSIZE"
+Force a particular size for your initrd image. The default is to total up the size of
+the included files and to add 512K as a buffer.
+.SH "BUGS"
+.IX Header "BUGS"
+I don't like having to specify a \-M option to set the \s-1MAKEDEV\s0 style, but I know
+of no way to reliably detect what type of \s-1MAKEDEV\s0 is being used. We'll probably
+have to add other \s-1MAKEDEV\s0 styles in the future as this script is tested on
+other distributions.
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+The script was originally written by Miguel Cabeca, with significant
+improvements by Jeffrey Layton. Comments, bug reports and patches should be
+sent to Jeffrey Layton at \fBjtlayton@poochiereds.net\fR.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fB\s-1MAKEDEV\s0\fR(8), \fBmdadm\fR(8), \fBbusybox\fR(8), \fBlvm.conf\fR(5)
diff --git a/scripts/lvm2create_initrd/lvm2create_initrd.pod b/scripts/lvm2create_initrd/lvm2create_initrd.pod
new file mode 100644 (file)
index 0000000..577930f
--- /dev/null
@@ -0,0 +1,187 @@
+=head1 NAME
+
+lvm2create_initrd - create initrd image for booting to root\-on\-LVM2
+
+=head1 SYNOPSIS
+
+B<lvm2create_initrd> [ B<-h|--help> ] [ B<-v|--verbose> ] [ B<-c|--lvmconf> I</path/to/lvm.conf> ] [ B<-m|--modules> "I<module1 module2 ...>" ] [ B<-e|--extra> "I<file1 file2 ...>" ] [ B<-r|--raid> "I</dev/md1 /dev/md2 ...>" ]
+[ B<-R|--raidconf> I</path/to/mdadm.conf> ] [ B<-M|--makedev> I<style> ]
+
+=head1 DESCRIPTION
+
+lvm2create_initrd creates an initial ramdisk (initrd) image suitable for booting to system that has an LVM2 volume as its root filesystem.
+
+To boot to such a setup, you'll
+either need a bootloader that understands LVM2 volumes, or you'll need a
+filesystem on a regular volume to act as a boot partition (typically mounted
+on /boot).
+
+The resulting initrd image is fairly full-featured. It can harbor and load
+kernel modules, start MD devices, and boot to a shell to perform rescue
+operations.
+
+=head2 Booting to your initrd Image:
+
+The filesystem image created is an ext2fs filesystem, hence your kernel must have
+ext2fs built into it statically in order to boot to the image.
+
+Once you create your initrd image, you must pass the correct options to the kernel when
+you boot using it. Your kernel command line should look something like this:
+
+B<root=/dev/ram0 lvm2root=/dev/rootvg/root [ lvm2rescue ]>
+
+of course there may be other options.
+
+=over
+
+=item B<root=/dev/ram0>
+
+This option is required. It tells the kernel that the root filesystem should initially
+be set to the ramdisk (/dev/ram0).
+
+=item B<lvm2root=/dev/rootvg/root>
+
+This option is also required. It tells the initrd image which LVM2 device the root filesystem is located on.
+
+=item B<lvm2rescue>
+
+Causes the initrd image to run a shell prior to mounting the root filesystem. This is
+helpful in disaster situations where your initrd image is accessable, but there is
+a problem with the root filesystem (corrupted image, incorrect device setup, etc.). This
+option is (of course) optional.
+
+=back
+
+=head1 OPTIONS
+
+Most of parameters that can be set via command-line options can also be set
+via environment variables. Options specified on the command-line always take
+precedence.
+
+=over
+
+=item B<-h|--help>
+
+Display short help text and exit. If used, other options are ignored.
+
+=item B<-v|--verbose>
+
+Turn on extra verbosity for debugging, etc.
+
+=item B<-c|--lvmconf> I</path/to/lvm.conf>
+
+Specify an lvm.conf file to include in the image. This is useful if you have
+special device filters or other options you wish to use during the initrd
+stage. If this option is not
+included, then a lvm.conf file is created that contains only the current
+device filter from an B<lvm dumpconfig>. This can also be set via the B<$LVMCONF>
+environment variable.
+
+=item B<-m|--modules> "I</path/to/module1.ko /path/to/module2.ko ...>"
+
+Specify modules to include and plug in during the initrd phase. This option
+takes a quoted, space-separated list of modules. Full pathnames are required.
+These modules are loaded into the kernel early in the initrd phase of the boot
+process. The current modprobe.conf file is also copied to the initrd image
+as well. This can also be specified via the B<$MODULES> environment variable.
+
+=item B<-e|--extra> "I</path/to/file1 /path/to/file2 ...>"
+
+Extra files that should be included in the initrd image. These files will be
+copied to the same location in the initrd image that they are in the current
+filesystem. Again full pathnames are required. This can also be specified via
+the B<$EXTRAFILES> environment variable.
+
+=item B<-r|--raid> "I</dev/md1 /dev/md2...>"
+
+RAID devices to be started prior to scanning for LVM2 volume groups. If this
+option is used then then B<mdadm> program must be installed. This can also be
+specified via the B<$RAID> environment variable.
+
+=item B<-R|--raidconf> "I</path/to/mdadm.conf>"
+
+Location of a mdadm.conf file to include. If this is not specified, then no
+files are included, and any devices specified with the B<-r> option above
+must have minor numbers that match their superblock values. This can also be
+specified via the B<$RAIDCONF> environment variable.
+
+=item B<-M|--makedev> I<style>
+
+Set MAKEDEV invocation style. The script currently supports 2 styles of
+MAKEDEV programs I<debian> and I<redhat>. The default is I<debian>. Set
+to I<redhat> if using the RedHat/Fedora binary MAKEDEV program. Please send
+a bug report to maintainer if your distrib doesn't work with any of the
+current options.
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+Most of the options to this script can be set via environment variables. In
+situations where both are set, then the command-line options take precedence.
+
+=over
+
+=item B<$LVMCONF>
+
+Same as -c option.
+
+=item B<$MODULES>
+
+Same as -m option.
+
+=item B<$EXTRAFILES>
+
+Same as -e option.
+
+=item B<$RAID>
+
+Same as -r option.
+
+=item B<$RAIDCONF>
+
+Same as -R option.
+
+=item B<$MAKEDEV>
+
+Same as -M option.
+
+=item B<$BASICDEVICES>
+
+Overrides the default value of $BASICDEVICES in the script (which is "std consoleonly fd"). These values are passed to the B<MAKEDEV> program to create device
+entries in the initrd image.
+
+=item B<$BLOCKDEVICES>
+
+Overrides the default value of $BLOCKDEVICES in the script (which is "md hda hdb hdc hdd sda sdb sdc sdd"). This value is passed to the B<MAKEDEV> program to
+create device entries in the initrd image.
+
+=item B<$BINFILES>
+
+Overrides the default value of $BINFILES (which is "/lib/lvm-200/lvm /bin/bash /bin/busybox /sbin/pivot_root"). The difference between using this and adding
+a file to the $EXTRAFILES list above is that libraries that these depend upon are also included. You can still use $EXTRAFILES to achieve the same effect, but
+you must resolve library dependencies youself.
+
+=item B<$INITRDSIZE>
+
+Force a particular size for your initrd image. The default is to total up the size of
+the included files and to add 512K as a buffer.
+
+=back
+
+=head1 BUGS
+
+I don't like having to specify a -M option to set the MAKEDEV style, but I know
+of no way to reliably detect what type of MAKEDEV is being used. We'll probably
+have to add other MAKEDEV styles in the future as this script is tested on
+other distributions.
+
+=head1 AUTHORS
+
+The script was originally written by Miguel Cabeca, with significant
+improvements by Jeffrey Layton. Comments, bug reports and patches should be
+sent to Jeffrey Layton at S<B<jtlayton@poochiereds.net>>.
+
+=head1 SEE ALSO
+
+B<MAKEDEV>(8), B<mdadm>(8), B<busybox>(8), B<lvm.conf>(5)
diff --git a/scripts/lvm2create_initrd/lvm2udev b/scripts/lvm2create_initrd/lvm2udev
new file mode 100644 (file)
index 0000000..573aac1
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# $Id: lvm2udev,v 1.1 2004/06/07 16:20:05 agk Exp $
+
+# simple startup script to create lvm2 devices if /dev is a mountpoint, there
+# are active dm- devices, and an executable /sbin/vgscan.
+
+# this script is licensed under GPLv2.
+# See http://www.gnu.org/licenses/gpl.html
+
+case $1 in
+start)
+    # is /dev a mountpoint?
+    mountpoint -q /dev
+    DEVMNTPOINT=$?
+
+    # check to see if there are active dm entries under /sys
+    ls /sys/block/dm-*/dev 1>/dev/null 2>&1
+    ACTIVEDMDEVS=$?
+
+    # mknodes if conditions are right
+    if [ $DEVMNTPOINT -eq 0 -a $ACTIVEDMDEVS -eq 0 -a -x /sbin/vgscan ]; then
+        /sbin/vgscan --mknodes --ignorelockingfailure
+    fi
+    ;;
+stop)
+    exit 0
+    ;;
+*)
+    echo "usage:"
+    echo "    $0 start|stop"
+    ;;
+esac
diff --git a/scripts/lvmconf.sh b/scripts/lvmconf.sh
new file mode 100644 (file)
index 0000000..5a8e9e8
--- /dev/null
@@ -0,0 +1,262 @@
+#!/bin/bash
+#
+# Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the lvm2 package.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# Edit an lvm.conf file to adjust various properties
+#
+
+function usage
+{
+    echo "usage: $0 <command>"
+    echo ""
+    echo "Commands:"
+    echo "Enable clvm:  --enable-cluster [--lockinglibdir <dir>] [--lockinglib <lib>]"
+    echo "Disable clvm: --disable-cluster"
+    echo "Set locking library: --lockinglibdir <dir> [--lockinglib <lib>]"
+    echo ""
+    echo "Global options:"
+    echo "Config file location: --file <configfile>"
+    echo ""
+}
+
+
+function parse_args
+{
+    while [ -n "$1" ]; do
+        case $1 in
+            --enable-cluster)
+                LOCKING_TYPE=3
+                shift
+                ;;
+            --disable-cluster)
+                LOCKING_TYPE=1
+                shift
+                ;;
+            --lockinglibdir)
+                if [ -n "$2" ]; then
+                    LOCKINGLIBDIR=$2
+                    shift 2
+                else
+                    usage
+                    exit 1
+                fi
+                ;;
+            --lockinglib)
+                if [ -n "$2" ]; then
+                    LOCKINGLIB=$2
+                    shift 2
+                else
+                    usage
+                    exit 1
+                fi
+                ;;
+            --file)
+                if [ -n "$2" ]; then
+                    CONFIGFILE=$2
+                    shift 2
+                else
+                    usage
+                    exit 1
+                fi
+                ;;
+            *)
+                usage
+                exit 1
+        esac
+    done
+}
+
+function validate_args
+{
+    [ -z "$CONFIGFILE" ] && CONFIGFILE="/etc/lvm/lvm.conf"
+
+    if [ ! -f "$CONFIGFILE" ]
+            then
+            echo "$CONFIGFILE does not exist"
+            exit 10
+    fi
+
+    if [ -z "$LOCKING_TYPE" ] && [ -z "$LOCKINGLIBDIR" ]; then
+        usage
+        exit 1
+    fi
+
+    if [ -n "$LOCKINGLIBDIR" ]; then
+
+        if [ "${LOCKINGLIBDIR:0:1}" != "/" ]
+            then
+            echo "Prefix must be an absolute path name (starting with a /)"
+            exit 12
+        fi
+
+        if [ -n "$LOCKINGLIB" ] && [ ! -f "$LOCKINGLIBDIR/$LOCKINGLIB" ]
+            then
+            echo "$LOCKINGLIBDIR/$LOCKINGLIB does not exist, did you do a \"make install\" ?"
+            exit 11
+        fi
+
+    fi
+
+    if [ "$LOCKING_TYPE" = "1" ] && [ -n "$LOCKINGLIBDIR" -o -n "$LOCKINGLIB" ]; then
+       echo "Superfluous locking lib parameter, ignoring"
+    fi
+}
+
+umask 0077
+
+parse_args "$@"
+
+validate_args
+
+
+SCRIPTFILE=/etc/lvm/.lvmconf-script.tmp
+TMPFILE=/etc/lvm/.lvmconf-tmp.tmp
+
+
+# Flags so we know which parts of the file we can replace and which need
+# adding. These are return codes from grep, so zero means it IS present!
+have_type=1
+have_dir=1
+have_library=1
+have_global=1
+
+grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $CONFIGFILE
+have_type=$?
+
+grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $CONFIGFILE
+have_dir=$?
+
+grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $CONFIGFILE
+have_library=$?
+
+# Those options are in section "global {" so we must have one if any are present.
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+then
+
+    # See if we can find it...
+    grep -q '^[[:blank:]]*global[[:blank:]]*{' $CONFIGFILE
+    have_global=$?
+
+    if [ "$have_global" = "1" ] 
+       then
+       echo "global keys but no 'global {' found, can't edit file"
+       exit 13
+    fi
+fi
+
+if [ "$LOCKING_TYPE" = "2" ] && [ -z "$LOCKINGLIBDIR" ] && [ "$have_dir" = "1" ]; then
+       echo "no library_dir specified in $CONFIGFILE"
+       exit 16
+fi
+
+# So if we don't have "global {" we need to create one and 
+# populate it
+
+if [ "$have_global" = "1" ]
+then
+    if [ -z "$LOCKING_TYPE" ]; then
+       LOCKING_TYPE=1
+    fi
+    if [ "$LOCKING_TYPE" = "3" ] || [ "$LOCKING_TYPE" = "2" ]; then
+        cat $CONFIGFILE - <<EOF > $TMPFILE
+global {
+    # Enable locking for cluster LVM
+    locking_type = $LOCKING_TYPE
+    library_dir = "$LOCKINGLIBDIR"
+EOF
+        if [ $? != 0 ]
+        then
+           echo "failed to create temporary config file, $CONFIGFILE not updated"
+           exit 14
+        fi
+       if [ -n "$LOCKINGLIB" ]; then
+           cat - <<EOF >> $TMPFILE
+    locking_library = "$LOCKINGLIB"
+EOF
+            if [ $? != 0 ]
+            then
+               echo "failed to create temporary config file, $CONFIGFILE not updated"
+               exit 16
+            fi
+       fi
+       cat - <<EOF >> $TMPFILE
+}
+EOF
+    fi # if we aren't setting cluster locking, we don't need to create a global section
+
+    if [ $? != 0 ]
+    then
+       echo "failed to create temporary config file, $CONFIGFILE not updated"
+       exit 17
+    fi
+else
+    #
+    # We have a "global {" section, so add or replace the
+    # locking entries as appropriate
+    #
+
+    if [ -n "$LOCKING_TYPE" ]; then
+       if [ "$have_type" = "0" ] 
+       then
+           SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = $LOCKING_TYPE/g"
+       else
+           SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = $LOCKING_TYPE"
+       fi
+    fi
+
+    if [ -n "$LOCKINGLIBDIR" ]; then
+        if [ "$have_dir" = "0" ] 
+            then
+            SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$LOCKINGLIBDIR\"'g"
+        else
+            SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$LOCKINGLIBDIR\""
+        fi
+    fi
+
+    if [ -n "$LOCKINGLIB" ]; then
+        if [ "$have_library" = "0" ]
+            then
+            SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LOCKINGLIB\"/g"
+        else
+            SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LOCKINGLIB\""
+        fi
+    fi
+
+    echo -e $SEDCMD > $SCRIPTFILE
+    sed  <$CONFIGFILE >$TMPFILE -f $SCRIPTFILE
+    if [ $? != 0 ]
+    then
+       echo "sed failed, $CONFIGFILE not updated"
+       exit 15
+    fi
+fi
+
+# Now we have a suitably editted config file in a temp place,
+# backup the original and copy our new one into place.
+
+cp $CONFIGFILE $CONFIGFILE.lvmconfold
+if [ $? != 0 ]
+    then
+    echo "failed to backup old config file, $CONFIGFILE not updated"
+    exit 2
+fi
+
+cp $TMPFILE $CONFIGFILE
+if [ $? != 0 ]
+    then
+    echo "failed to copy new config file into place, check $CONFIGFILE is still OK"
+    exit 3
+fi
+
+rm -f $SCRIPTFILE $TMPFILE
diff --git a/scripts/lvmconf_lockingtype2.sh b/scripts/lvmconf_lockingtype2.sh
new file mode 100644 (file)
index 0000000..b823a43
--- /dev/null
@@ -0,0 +1,259 @@
+#!/bin/bash
+#
+# Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the lvm2-cluster package.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# Edit an lvm.conf file to adjust various properties
+#
+
+function usage
+{
+    echo "usage: $0 <command>"
+    echo ""
+    echo "Commands:"
+    echo "Enable clvm:  --enable-cluster [--lockinglibdir <dir>] [--lockinglib <lib>]"
+    echo "Disable clvm: --disable-cluster"
+    echo "Set locking library: --lockinglibdir <dir> [--lockinglib <lib>]"
+    echo ""
+    echo "Global options:"
+    echo "Config file location: --file <configfile>"
+    echo ""
+}
+
+
+function parse_args
+{
+    while [ -n "$1" ]; do
+        case $1 in
+            --enable-cluster)
+                LOCKING_TYPE=2
+                shift
+                ;;
+            --disable-cluster)
+                LOCKING_TYPE=1
+                shift
+                ;;
+            --lockinglibdir)
+                if [ -n "$2" ]; then
+                    LOCKINGLIBDIR=$2
+                    shift 2
+                else
+                    usage
+                    exit 1
+                fi
+                ;;
+            --lockinglib)
+                if [ -n "$2" ]; then
+                    LOCKINGLIB=$2
+                    shift 2
+                else
+                    usage
+                    exit 1
+                fi
+                ;;
+            --file)
+                if [ -n "$2" ]; then
+                    CONFIGFILE=$2
+                    shift 2
+                else
+                    usage
+                    exit 1
+                fi
+                ;;
+            *)
+                usage
+                exit 1
+        esac
+    done
+}
+
+function validate_args
+{
+    [ -z "$CONFIGFILE" ] && CONFIGFILE="/etc/lvm/lvm.conf"
+
+    if [ ! -f "$CONFIGFILE" ]
+            then
+            echo "$CONFIGFILE does not exist"
+            exit 10
+    fi
+
+    if [ -z "$LOCKING_TYPE" ] && [ -z "$LOCKINGLIBDIR" ]; then
+        usage
+        exit 1
+    fi
+
+    if [ -n "$LOCKINGLIBDIR" ]; then
+
+        [ -z "$LOCKINGLIB" ] && LOCKINGLIB="liblvm2clusterlock.so"
+            
+        if [ "${LOCKINGLIBDIR:0:1}" != "/" ]
+            then
+            echo "Prefix must be an absolute path name (starting with a /)"
+            exit 12
+        fi
+    
+        if [ ! -f "$LOCKINGLIBDIR/$LOCKINGLIB" ]
+            then
+            echo "$LOCKINGLIBDIR/$LOCKINGLIB does not exist, did you do a \"make install\" ?"
+            exit 11
+        fi
+        
+    fi
+
+    if [ "$LOCKING_TYPE" = "1" ] && [ -n "$LOCKINGLIBDIR" -o -n "$LOCKINGLIB" ]; then
+       echo "Superfluous locking lib parameter, ignoring"
+    fi
+}
+
+umask 0077
+
+parse_args "$@"
+
+validate_args
+
+
+SCRIPTFILE=/etc/lvm/.lvmconf-script.tmp
+TMPFILE=/etc/lvm/.lvmconf-tmp.tmp
+
+
+# Flags so we know which parts of the file we can replace and which need
+# adding. These are return codes from grep, so zero means it IS present!
+have_type=1
+have_dir=1
+have_library=1
+have_global=1
+
+grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $CONFIGFILE
+have_type=$?
+
+grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $CONFIGFILE
+have_dir=$?
+
+grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $CONFIGFILE
+have_library=$?
+
+# Those options are in section "global {" so we must have one if any are present.
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+then
+
+    # See if we can find it...
+    grep -q '^[[:blank:]]*global[[:blank:]]*{' $CONFIGFILE
+    have_global=$?
+    
+    if [ "$have_global" = "1" ] 
+       then
+       echo "global keys but no 'global {' found, can't edit file"
+       exit 13
+    fi
+fi
+
+if [ "$LOCKING_TYPE" = "2" ] && [ -z "$LOCKINGLIBDIR" ] && [ "$have_dir" = "1" ]; then
+       echo "no library_dir specified in $CONFIGFILE"
+       exit 16
+fi
+
+# So if we don't have "global {" we need to create one and 
+# populate it
+
+if [ "$have_global" = "1" ]
+then
+    if [ -z "$LOCKING_TYPE" ]; then
+       LOCKING_TYPE=1
+    fi
+    if [ "$LOCKING_TYPE" = "2" ]; then
+        cat $CONFIGFILE - <<EOF > $TMPFILE
+global {
+    # Enable locking for cluster LVM
+    locking_type = $LOCKING_TYPE
+    library_dir = "$LOCKINGLIBDIR"
+    locking_library = "$LOCKINGLIB"
+}
+EOF
+    fi # if we aren't setting cluster locking, we don't need to create a global section
+
+    if [ $? != 0 ]
+    then
+       echo "failed to create temporary config file, $CONFIGFILE not updated"
+       exit 14
+    fi
+else
+    #
+    # We have a "global {" section, so add or replace the
+    # locking entries as appropriate
+    #
+
+    if [ -n "$LOCKING_TYPE" ]; then
+       if [ "$have_type" = "0" ] 
+       then
+           SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = $LOCKING_TYPE/g"
+       else
+           SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = $LOCKING_TYPE"
+       fi
+    fi
+    
+    if [ -n "$LOCKINGLIBDIR" ]; then
+        if [ "$have_dir" = "0" ] 
+            then
+            SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$LOCKINGLIBDIR\"'g"
+        else
+            SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$LOCKINGLIBDIR\""
+        fi
+
+        if [ "$have_library" = "0" ] 
+            then
+            SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LOCKINGLIB\"/g"
+        else
+            SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LOCKINGLIB\""
+        fi
+    fi
+
+    if [ "$LOCKING_TYPE" = "1" ]; then
+        # if we're not using cluster locking, remove the library dir and locking library name
+        if [ "$have_dir" = "0" ] 
+            then
+            SEDCMD="${SEDCMD}\n/^[[:blank:]]*library_dir[[:blank:]]*=.*/d"
+        fi
+
+        if [ "$have_library" = "0" ] 
+            then
+            SEDCMD="${SEDCMD}\n/^[[:blank:]]*locking_library[[:blank:]]*=.*/d"
+        fi
+    fi
+
+    echo -e $SEDCMD > $SCRIPTFILE
+    sed  <$CONFIGFILE >$TMPFILE -f $SCRIPTFILE
+    if [ $? != 0 ]
+    then
+       echo "sed failed, $CONFIGFILE not updated"
+       exit 15
+    fi
+fi
+
+# Now we have a suitably editted config file in a temp place,
+# backup the original and copy our new one into place.
+
+cp $CONFIGFILE $CONFIGFILE.lvmconfold
+if [ $? != 0 ]
+    then
+    echo "failed to backup old config file, $CONFIGFILE not updated"
+    exit 2
+fi
+
+cp $TMPFILE $CONFIGFILE
+if [ $? != 0 ]
+    then
+    echo "failed to copy new config file into place, check $CONFIGFILE is still OK"
+    exit 3
+fi
+
+rm -f $SCRIPTFILE $TMPFILE
diff --git a/scripts/lvmdump.sh b/scripts/lvmdump.sh
new file mode 100755 (executable)
index 0000000..55c477d
--- /dev/null
@@ -0,0 +1,235 @@
+#!/bin/bash
+# We use some bash-isms (getopts?)
+
+# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# lvm_dump: This script is used to collect pertinent information for
+#           the debugging of lvm issues.
+
+# following external commands are used throughout the script
+# echo and test are internal in bash at least
+MKDIR=mkdir # need -p
+TAR=tar # need czf
+RM=rm # need -rf
+CP=cp
+TAIL=tail # we need -n
+LS=ls # need -la
+PS=ps # need alx
+SED=sed
+DD=dd
+CUT=cut
+DATE=date
+BASENAME=basename
+UNAME=uname
+
+# user may override lvm and dmsetup location by setting LVM_BINARY
+# and DMSETUP_BINARY respectively
+LVM=${LVM_BINARY-lvm}
+DMSETUP=${DMSETUP_BINARY-dmsetup}
+
+die() {
+    code=$1; shift
+    echo "$@" 1>&2
+    exit $code
+}
+
+"$LVM" version >& /dev/null || die 2 "Could not run lvm binary '$LVM'"
+"$DMSETUP" version >& /dev/null || DMSETUP=:
+
+function usage {
+       echo "$0 [options]"
+       echo "    -h print this message"
+       echo "    -a advanced collection - warning: if lvm is already hung,"
+       echo "       then this script may hang as well if -a is used"
+       echo "    -m gather LVM metadata from the PVs"
+       echo "    -d <directory> dump into a directory instead of tarball"
+       echo "    -c if running clvmd, gather cluster data as well"
+       echo ""
+       
+       exit 1
+}
+
+advanced=0
+clustered=0
+metadata=0
+while getopts :acd:hm opt; do
+       case $opt in 
+               s)      sysreport=1 ;;
+               a)      advanced=1 ;;
+               c)      clustered=1 ;;
+               d)      userdir=$OPTARG ;;
+               h)      usage ;;
+               m)      metadata=1 ;;
+               :)      echo "$0: $OPTARG requires a value:"; usage ;;
+               \?)     echo "$0: unknown option $OPTARG"; usage ;;
+               *)      usage ;;
+       esac
+done
+
+NOW=`$DATE -u +%G%m%d%k%M%S | /usr/bin/tr -d ' '`
+if test -n "$userdir"; then
+       dir="$userdir"
+else
+       dirbase="lvmdump-$HOSTNAME-$NOW"
+       dir="$HOME/$dirbase"
+fi
+
+test -e $dir && die 3 "Fatal: $dir already exists"
+$MKDIR -p $dir || die 4 "Fatal: could not create $dir"
+
+log="$dir/lvmdump.log"
+
+myecho() {
+       echo "$@"
+       echo "$@" >> "$log"
+}
+
+log() {
+       echo "$@" >> "$log"
+       eval "$@"
+}
+
+warnings() {
+       if test "$UID" != "0" && test "$EUID" != "0"; then
+               myecho "WARNING! Running as non-privileged user, dump is likely incomplete!"
+       elif test "$DMSETUP" = ":"; then
+               myecho "WARNING! Could not run dmsetup, dump is likely incomplete."
+       fi
+}
+
+warnings
+
+myecho "Creating dump directory: $dir"
+echo " "
+
+if (( $advanced )); then
+       myecho "Gathering LVM volume info..."
+
+       myecho "  vgscan..."
+       log "\"$LVM\" vgscan -vvvv > \"$dir/vgscan\" 2>&1"
+
+       myecho "  pvscan..."
+       log "\"$LVM\" pvscan -v >> \"$dir/pvscan\" 2>> \"$log\""
+
+       myecho "  lvs..."
+       log "\"$LVM\" lvs -a -o +devices >> \"$dir/lvs\" 2>> \"$log\""
+
+       myecho "  pvs..."
+       log "\"$LVM\" pvs -a -v > \"$dir/pvs\" 2>> \"$log\""
+
+       myecho "  vgs..."
+       log "\"$LVM\" vgs -v > \"$dir/vgs\" 2>> \"$log\""
+fi
+
+if (( $clustered )); then
+       myecho "Gathering cluster info..."
+
+       {
+       for i in nodes status services; do
+               cap_i=$(echo $i|tr a-z A-Z)
+               printf "$cap_i:\n----------------------------------\n"
+               log "cman_tool $i 2>> \"$log\""
+               echo
+       done
+
+       echo "LOCKS:"
+       echo "----------------------------------"
+       if [ -f /proc/cluster/dlm_locks ]
+       then
+               echo clvmd > /proc/cluster/dlm_locks
+               cat /proc/cluster/dlm_locks
+               echo
+               echo "RESOURCE DIR:"
+               cat /proc/cluster/dlm_dir
+               echo
+               echo "DEBUG LOG:"
+               cat /proc/cluster/dlm_debug
+               echo
+       fi
+       if [ -f /debug/dlm/clvmd ]
+       then
+               cat /debug/dlm/clvmd
+               echo
+               echo "WAITERS:"
+               cat /debug/dlm/clvmd_waiters
+               echo
+               echo "MASTER:"
+               cat /debug/dlm/clvmd_master
+       fi
+       } > $dir/cluster_info
+fi
+
+myecho "Gathering LVM & device-mapper version info..."
+echo "LVM VERSION:" > "$dir/versions"
+"$LVM" lvs --version >> "$dir/versions" 2>> "$log"
+echo "DEVICE MAPPER VERSION:" >> "$dir/versions"
+"$DMSETUP" --version >> "$dir/versions" 2>> "$log"
+echo "KERNEL VERSION:" >> "$dir/versions"
+"$UNAME" -a >> "$dir/versions" 2>> "$log"
+echo "DM TARGETS VERSIONS:" >> "$dir/versions"
+"$DMSETUP" targets >> "$dir/versions" 2>> "$log"
+
+myecho "Gathering dmsetup info..."
+log "\"$DMSETUP\" info -c > \"$dir/dmsetup_info\" 2>> \"$log\""
+log "\"$DMSETUP\" table > \"$dir/dmsetup_table\" 2>> \"$log\""
+log "\"$DMSETUP\" status > \"$dir/dmsetup_status\" 2>> \"$log\""
+
+myecho "Gathering process info..."
+log "$PS alx > \"$dir/ps_info\" 2>> \"$log\""
+
+myecho "Gathering console messages..."
+log "$TAIL -n 75 /var/log/messages > \"$dir/messages\" 2>> \"$log\""
+
+myecho "Gathering /etc/lvm info..."
+log "$CP -a /etc/lvm \"$dir/lvm\" 2>> \"$log\""
+
+myecho "Gathering /dev listing..."
+log "$LS -laR /dev > \"$dir/dev_listing\" 2>> \"$log\""
+
+myecho "Gathering /sys/block listing..."
+log "$LS -laR /sys/block > \"$dir/sysblock_listing\"  2>> \"$log\""
+log "$LS -laR /sys/devices/virtual/block >> \"$dir/sysblock_listing\"  2>> \"$log\""
+
+if (( $metadata )); then
+       myecho "Gathering LVM metadata from Physical Volumes..."
+
+       log "$MKDIR -p \"$dir/metadata\""
+
+       pvs="$("$LVM" pvs --separator , --noheadings --units s --nosuffix -o \
+           name,pe_start 2>> "$log" | $SED -e 's/^ *//')"
+       for line in $pvs
+       do
+               test -z "$line" && continue
+               pv="$(echo $line | $CUT -d, -f1)"
+               pe_start="$(echo $line | $CUT -d, -f2)"
+               name="$($BASENAME "$pv")"
+               myecho "  $pv"
+               log "$DD if=$pv \"of=$dir/metadata/$name\" bs=512 count=$pe_start 2>> \"$log\""
+       done
+fi
+
+if test -z "$userdir"; then
+       lvm_dump="$dirbase.tgz"
+       myecho "Creating report tarball in $HOME/$lvm_dump..."
+fi
+
+warnings
+
+if test -z "$userdir"; then
+       cd "$HOME"
+       "$TAR" czf "$lvm_dump" "$dirbase" 2>/dev/null
+       "$RM" -rf "$dir"
+fi
+
+exit 0
+
diff --git a/scripts/relpath.awk b/scripts/relpath.awk
new file mode 100755 (executable)
index 0000000..9195ed8
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/awk -f
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# relpath.awk: Script is used to calculate relative path
+#             between two real absolute paths.
+#
+# echo /a/b/c/d  /a/b/e/f | relpath.awk
+# -> ../../e/f/
+
+{
+       length_from = split($1, from, "/");
+       length_to = split($2, to, "/") ;
+       l = 1;
+       while (l <= length_from && l <= length_to && from[l] == to[l])
+               l++;
+       for (i = l; i <= length_from && length(from[i]); i++) {
+               if (i > l)
+                       p = sprintf("%s/", p);
+               p = sprintf("%s..", p);
+       }
+       for (i = l; i <= length_to && length(to[i]); i++) {
+               if (length(p) > 0)
+                       p = sprintf("%s/", p);
+               p = sprintf("%s%s", p, to[i]);
+       }
+       if (length(p))
+               p = sprintf("%s/", p);
+       print p
+}
diff --git a/scripts/vg_convert b/scripts/vg_convert
new file mode 100755 (executable)
index 0000000..6559941
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh -x
+
+# Original script used to convert a VG from LVM1 to LVM2 metadata format.
+# Superceded by 'vgconvert', but left here to show how to do it step-by-step.
+
+# Takes vgname as parameter.  No error checking.  Uses temp file 'lvmbackup'.
+
+echo "Please use the 'vgconvert' tool instead"
+exit 1
+
+./vgcfgbackup $1 || exit 1
+./vgcfgbackup --file lvmbackup $1 || exit 1
+
+CMDS=`./pvscan -u | sed -ne "s/.*PV \(.*\) with UUID \(.*\) VG $1 .*/.\/pvcreate -ff -y -M lvm2 --restorefile lvmbackup -u \2 \1 ; /p"`
+
+sh -x -c "$CMDS" || exit 1
+
+./vgcfgrestore --file lvmbackup -M lvm2 $1 || exit 1
diff --git a/scripts/vgimportclone.sh b/scripts/vgimportclone.sh
new file mode 100755 (executable)
index 0000000..731b860
--- /dev/null
@@ -0,0 +1,366 @@
+#!/bin/bash
+
+# Copyright (C) 2009 Chris Procter All rights reserved.
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+#
+# vgimportclone: This script is used to rename the VG and change the associated
+#                VG and PV UUIDs (primary application being HW snapshot restore)
+
+# following external commands are used throughout the script
+# echo and test are internal in bash at least
+RM=rm
+BASENAME=basename
+MKTEMP=mktemp
+AWK=awk
+CUT=cut
+TR=tr
+READLINK=readlink
+GREP=grep
+GETOPT=getopt
+
+# user may override lvm location by setting LVM_BINARY
+LVM="${LVM_BINARY:-lvm}"
+
+die() {
+    code=$1; shift
+    echo "Fatal: $@" 1>&2
+    exit $code
+}
+
+"$LVM" version >& /dev/null || die 2 "Could not run lvm binary '$LVM'"
+
+
+function getvgname {
+### get a unique vg name
+###        $1 = list of exists VGs
+###        $2 = the name we want
+    VGLIST=$1
+    VG=$2
+    NEWVG=$3
+
+    BNAME="${NEWVG:-${VG}}"
+    NAME="${BNAME}"
+    I=0
+
+    while [[ "${VGLIST}" =~ "${NAME}" ]]
+    do
+        I=$(($I+1))
+        NAME="${BNAME}$I"
+    done
+    echo "${NAME}"
+}
+
+
+function checkvalue {
+### check return value and error if non zero
+    if [ $1 -ne 0 ]
+    then
+        die $1 "$2, error: $1"
+    fi
+}
+
+
+function usage {
+### display usage message
+    echo "Usage: ${SCRIPTNAME} [options] PhysicalVolume [PhysicalVolume...]"
+    echo "    -n|--basevgname - Base name for the new volume group(s)"
+    echo "    -i|--import     - Import any exported volume groups found"
+    echo "    -t|--test       - Run in test mode"
+    echo "    --quiet         - Suppress output"
+    echo "    -v|--verbose    - Set verbose level"
+    echo "    -d|--debug      - Set debug level"
+    echo "    --version       - Display version information"
+    echo "    -h|--help       - Display this help message"
+    echo ""
+    exit 1
+}
+
+
+function cleanup {
+    #set to use old lvm.conf
+    LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
+
+    if [ $KEEP_TMP_LVM_SYSTEM_DIR -eq 1 ]; then
+        echo "${SCRIPTNAME}: LVM_SYSTEM_DIR (${TMP_LVM_SYSTEM_DIR}) must be cleaned up manually."
+    else
+        "$RM" -rf -- "${TMP_LVM_SYSTEM_DIR}"
+    fi
+}
+
+SCRIPTNAME=`"$BASENAME" $0`
+
+
+if [ "$UID" != "0" -a "$EUID" != "0" ]
+then
+    die 3 "${SCRIPTNAME} must be run as root."
+fi
+
+LVM_OPTS=""
+TEST_OPT=""
+DISKS=""
+# for compatibility: using mktemp -t rather than --tmpdir
+TMP_LVM_SYSTEM_DIR=`"$MKTEMP" -d -t snap.XXXXXXXX`
+KEEP_TMP_LVM_SYSTEM_DIR=0
+CHANGES_MADE=0
+IMPORT=0
+DEBUG=""
+VERBOSE=""
+VERBOSE_COUNT=0
+DEVNO=0
+
+if [ -n "${LVM_SYSTEM_DIR}" ]; then
+    export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}"
+fi
+
+trap  cleanup 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
+
+#####################################################################
+### Get and check arguments
+#####################################################################
+OPTIONS=`"$GETOPT" -o n:dhitv \
+    -l basevgname:,debug,help,import,quiet,test,verbose,version \
+    -n "${SCRIPTNAME}" -- "$@"`
+[ $? -ne 0 ] && usage
+eval set -- "$OPTIONS"
+
+while true
+do
+    case $1 in
+        -n|--basevgname)
+            NEWVG="$2"; shift; shift
+            ;;
+        -i|--import)
+            IMPORT=1; shift
+            ;;
+        -t|--test)
+            TEST_OPT="-t"
+            shift
+            ;;
+        --quiet)
+            LVM_OPTS="--quiet ${LVM_OPTS}"
+            shift
+            ;;
+        -v|--verbose)
+            let VERBOSE_COUNT=VERBOSE_COUNT+1
+            if [ -z "$VERBOSE" ]
+            then
+                VERBOSE="-v"
+            else
+                VERBOSE="${VERBOSE}v"
+            fi
+            shift
+            ;;
+        -d|--debug)
+            if [ -z "$DEBUG" ]
+            then
+                DEBUG="-d"
+                set -x
+            else
+                DEBUG="${DEBUG}d"
+            fi
+            shift
+            ;;
+        --version)
+            "$LVM" version
+            shift
+            exit 0
+            ;;
+        -h|--help)
+            usage; shift
+            ;;
+        --)
+            shift; break
+            ;;
+        *)
+            usage
+            ;;
+    esac
+done
+
+# turn on DEBUG (special case associated with -v use)
+if [ -z "$DEBUG" -a $VERBOSE_COUNT -gt 3 ]; then
+    DEBUG="-d"
+    set -x
+fi
+
+# setup LVM_OPTS
+if [ -n "${DEBUG}" -o -n "${VERBOSE}" ]
+then
+    LVM_OPTS="${LVM_OPTS} ${DEBUG} ${VERBOSE}"
+fi
+
+# process remaining arguments (which should be disks)
+for ARG
+do
+    if [ -b "$ARG" ]
+    then
+        PVS_OUT=`"${LVM}" pvs ${LVM_OPTS} --noheadings -o vg_name "$ARG" 2>/dev/null`
+        checkvalue $? "$ARG is not a PV."
+        PV_VGNAME=$(echo $PVS_OUT | $GREP -v '[[:space:]]+$')
+        [ -z "$PV_VGNAME" ] && die 3 "$ARG is not in a VG."
+
+        ln -s "$ARG" ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}
+        DISKS="${DISKS} ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}"
+        DEVNO=$((${DEVNO}+1))
+    else
+        die 3 "$ARG is not a block device."
+    fi
+done
+
+### check we have suitable values for important variables
+if [ -z "${DISKS}" ]
+then
+    usage
+fi
+
+#####################################################################
+### Get the existing state so we can use it later
+#####################################################################
+
+OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings 2>/dev/null`
+checkvalue $? "Current VG names could not be collected without errors"
+
+#####################################################################
+### Prepare the temporary lvm environment
+#####################################################################
+
+for BLOCK in ${DISKS}
+do
+    FILTER="\"a|^${BLOCK}$|\", ${FILTER}"
+done
+export FILTER="filter=[ ${FILTER} \"r|.*|\" ]"
+
+LVMCONF=${TMP_LVM_SYSTEM_DIR}/lvm.conf
+
+"$LVM" dumpconfig ${LVM_OPTS} | \
+"$AWK" -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/.cache \
+    -v CACHE_DIR=${TMP_LVM_SYSTEM_DIR}/cache \
+    '/^[[:space:]]*filter[[:space:]]*=/{print ENVIRON["FILTER"];next} \
+     /^[[:space:]]*scan[[:space:]]*=/{print "scan = [ \"" DEV "\" ]";next} \
+     /^[[:space:]]*cache[[:space:]]*=/{print "cache = \"" CACHE "\"";next} \
+     /^[[:space:]]*cache_dir[[:space:]]*=/{print "cache_dir = \"" CACHE_DIR "\"";next} \
+     {print $0}' > ${LVMCONF}
+
+checkvalue $? "Failed to generate ${LVMCONF}"
+# Only keep TMP_LVM_SYSTEM_DIR if it contains something worth keeping
+[ -n "${DEBUG}" ] && KEEP_TMP_LVM_SYSTEM_DIR=1
+
+# verify the config contains the filter, scan and cache_dir (or cache) config keywords
+"$GREP" -q '^[[:space:]]*filter[[:space:]]*=' ${LVMCONF} || \
+    die 5 "Temporary lvm.conf must contain 'filter' config."
+"$GREP" -q '^[[:space:]]*scan[[:space:]]*=' ${LVMCONF} || \
+    die 6 "Temporary lvm.conf must contain 'scan' config."
+
+# check for either 'cache' or 'cache_dir' config values
+"$GREP" -q '[[:space:]]*cache[[:space:]]*=' ${LVMCONF}
+CACHE_RET=$?
+"$GREP" -q '^[[:space:]]*cache_dir' ${LVMCONF}
+CACHE_DIR_RET=$?
+[ $CACHE_RET -eq 0 -o $CACHE_DIR_RET -eq 0 ] || \
+    die 7 "Temporary lvm.conf must contain 'cache' or 'cache_dir' config."
+
+### set to use new lvm.conf
+export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR}
+
+
+#####################################################################
+### Rename the VG(s) and change the VG and PV UUIDs.
+#####################################################################
+
+PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator : 2>/dev/null`
+checkvalue $? "PV info could not be collected without errors"
+
+# output VG info so each line looks like: name:exported?:disk1,disk2,...
+VGINFO=`echo "${PVINFO}" | \
+    "$AWK" -F : '{{sub(/^[[:space:]]*/,"")} \
+    {sub(/unknown device/,"unknown_device")} \
+    {vg[$2]=$1","vg[$2]} if($3 ~ /^..x/){x[$2]="x"}} \
+    END{for(k in vg){printf("%s:%s:%s\n", k, x[k], vg[k])}}'`
+checkvalue $? "PV info could not be parsed without errors"
+
+for VG in ${VGINFO}
+do
+    VGNAME=`echo "${VG}" | "$CUT" -d: -f1`
+    EXPORTED=`echo "${VG}" | "$CUT" -d: -f2`
+    PVLIST=`echo "${VG}" | "$CUT" -d: -f3- | "$TR" , ' '`
+
+    if [ -z "${VGNAME}" ]
+    then
+        FOLLOWLIST=""
+        for DEV in $PVLIST; do
+            FOLLOW=`"$READLINK" $DEV`
+            FOLLOWLIST="$FOLLOW $FOLLOWLIST"
+        done
+        die 8 "Specified PV(s) ($FOLLOWLIST) don't belong to a VG."
+    fi
+
+    if [ -n "${EXPORTED}" ]
+    then
+        if [ ${IMPORT} -eq 1 ]
+        then
+            "$LVM" vgimport ${LVM_OPTS} ${TEST_OPT} "${VGNAME}"
+            checkvalue $? "Volume Group ${VGNAME} could not be imported"
+        else
+            echo "Volume Group ${VGNAME} exported, skipping."
+            continue
+        fi
+    fi
+
+    ### change the pv uuids
+    if [[ "${PVLIST}" =~ "unknown" ]]
+    then
+        echo "Volume Group ${VGNAME} has unknown PV(s), skipping."
+        echo "- Were all associated PV(s) supplied as arguments?"
+        continue
+    fi
+
+    for BLOCKDEV in ${PVLIST}
+    do
+        "$LVM" pvchange ${LVM_OPTS} ${TEST_OPT} --uuid ${BLOCKDEV} --config 'global{activation=0}'
+        checkvalue $? "Unable to change PV uuid for ${BLOCKDEV}"
+    done
+
+    NEWVGNAME=`getvgname "${OLDVGS}" "${VGNAME}" "${NEWVG}"`
+
+    "$LVM" vgchange ${LVM_OPTS} ${TEST_OPT} --uuid "${VGNAME}" --config 'global{activation=0}'
+    checkvalue $? "Unable to change VG uuid for ${VGNAME}"
+
+    ## if the name isn't going to get changed dont even try.
+    if [ "${VGNAME}" != "${NEWVGNAME}" ]
+    then
+        "$LVM" vgrename ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" "${NEWVGNAME}"
+        checkvalue $? "Unable to rename ${VGNAME} to ${NEWVGNAME}"
+    fi
+
+    CHANGES_MADE=1
+done
+
+#####################################################################
+### Restore the old environment
+#####################################################################
+### set to use old lvm.conf
+if [ -z "${ORIG_LVM_SYS_DIR}" ]
+then
+    unset LVM_SYSTEM_DIR
+else
+    LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
+fi
+
+### update the device cache and make sure all
+### the device nodes we need are straight
+if [ ${CHANGES_MADE} -eq 1 ]
+then
+    "$LVM" vgscan ${LVM_OPTS} --mknodes
+fi
+
+exit 0
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644 (file)
index 0000000..2351bfc
--- /dev/null
@@ -0,0 +1,4 @@
+.bin-dir-stamp
+Makefile
+bin
+init.sh
diff --git a/test/Makefile.in b/test/Makefile.in
new file mode 100644 (file)
index 0000000..86542c1
--- /dev/null
@@ -0,0 +1,130 @@
+# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
+RM ?= rm -f
+
+subdir := $(shell pwd|sed 's,.*/,,')
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+abs_srcdir = @abs_srcdir@
+abs_builddir = @abs_builddir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+
+T ?= .
+S ?= @ # never match anything by default
+VERBOSE ?= 0
+RUN = $(shell find $(srcdir) -regextype posix-egrep \( -name t-\*.sh -or -path */api/\*.sh \) -and -regex "$(srcdir)/.*($(T)).*" -and -not -regex "$(srcdir)/.*($(S)).*" | sort)
+RUN_BASE = $(shell echo $(RUN) | xargs -n 1 echo | sed -e s,^$(srcdir)/,,)
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+SUPPORT := $(srcdir)/test-utils.sh \
+           $(srcdir)/lvm-utils.sh
+
+ifeq ("@UDEV_SYNC@", "yes")
+dm_udev_synchronisation = 1
+endif
+
+all check: init.sh
+       make -C api tests
+       @echo Testing with locking_type 1
+       VERBOSE=$(VERBOSE) ./bin/harness $(RUN_BASE)
+       @echo Testing with locking_type 3
+       VERBOSE=$(VERBOSE) LVM_TEST_LOCKING=3 ./bin/harness $(RUN_BASE)
+
+check_cluster: init.sh
+       make -C api tests
+       @echo Testing with locking_type 3
+       VERBOSE=$(VERBOSE) LVM_TEST_LOCKING=3 ./bin/harness $(RUN_BASE)
+
+check_local: init.sh
+       make -C api tests
+       @echo Testing with locking_type 1
+       VERBOSE=$(VERBOSE) LVM_TEST_LOCKING=1 ./bin/harness $(RUN_BASE)
+
+bin/not: $(srcdir)/not.c .bin-dir-stamp
+       $(CC) -o bin/not $<
+       ln -sf not bin/should
+
+bin/harness: $(srcdir)/harness.c .bin-dir-stamp
+       $(CC) -o bin/harness $<
+
+bin/check: $(srcdir)/check.sh .bin-dir-stamp
+       cp $< bin/check
+       chmod +x bin/check
+
+init.sh: $(srcdir)/Makefile.in .bin-dir-stamp bin/not bin/check bin/harness $(RUN) $(SUPPORT) $(UNIT)
+       rm -f $@-t $@
+       echo 'top_srcdir=$(top_srcdir)' >> $@-t
+       echo 'abs_top_builddir=$(abs_top_builddir)' >> $@-t
+       echo 'abs_top_srcdir=$(abs_top_builddir)' >> $@-t
+       echo 'PATH=$$abs_top_builddir/test/bin:$$PATH' >> $@-t
+       LDLPATH="\$$abs_top_builddir/libdm"; \
+       LDLPATH="$$LDLPATH:\$$abs_top_builddir/tools"; \
+       LDLPATH="$$LDLPATH:\$$abs_top_builddir/liblvm"; \
+       LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd"; \
+       LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd/plugins/lvm2"; \
+       LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd/plugins/mirror"; \
+       LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd/plugins/snapshot"; \
+       echo "export LD_LIBRARY_PATH=\"$$LDLPATH\"" >> $@-t
+       echo 'top_srcdir=$(top_srcdir)' >> $@-t
+       echo 'abs_srcdir=$(abs_srcdir)' >> $@-t
+       echo 'abs_builddir=$(abs_builddir)' >> $@-t
+       echo 'export PATH' >> $@-t
+       echo 'export DM_UDEV_SYNCHRONISATION=$(dm_udev_synchronisation)' >> $@-t
+       chmod a-w $@-t
+       mv $@-t $@
+       @if test "$(srcdir)" != . ; then \
+           echo "Copying tests to builddir."; \
+           cp $(SUPPORT) .; \
+           for f in $(RUN); do cp $$f `echo $$f | sed -e s,^$(srcdir)/,,`; done; \
+       fi
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+.bin-dir-stamp: lvm-wrapper
+       rm -rf bin
+       mkdir bin
+       for i in lvm $$(cat ../tools/.commands); do \
+         ln -s ../lvm-wrapper bin/$$i; \
+       done
+       ln -s "$(abs_top_builddir)/tools/dmsetup" bin/dmsetup
+       ln -s "$(abs_top_builddir)/daemons/clvmd/clvmd" bin/clvmd
+       ln -s "$(abs_top_builddir)/daemons/dmeventd/dmeventd" bin/dmeventd
+       touch $@
+
+lvm-wrapper: Makefile
+       rm -f $@-t $@
+       echo '#!/bin/sh'                                          >  $@-t
+       echo 'cmd=$$(echo ./$$0|sed "s,.*/,,")'                   >> $@-t
+       echo 'test "$$cmd" = lvm &&'                              >> $@-t
+       echo 'exec "$(abs_top_builddir)/tools/lvm" "$$@"'         >> $@-t
+       echo 'exec "$(abs_top_builddir)/tools/lvm" "$$cmd" "$$@"' >> $@-t
+       chmod a-w,a+x $@-t
+       mv $@-t $@
+
+clean:
+       rm -rf init.sh lvm-wrapper bin .bin-dir-stamp
+       if test "$(srcdir)" != . ; then rm -f $(subst $(srcdir)/, ,$(RUN)) lvm2app.sh ; fi
+
+distclean: clean
+       rm -f Makefile
+
+.NOTPARALLEL:
diff --git a/test/api/Makefile.in b/test/api/Makefile.in
new file mode 100644 (file)
index 0000000..b3fb751
--- /dev/null
@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+ifeq ("@DEBUG@", "yes")
+       DEFS += -DDEBUG
+endif
+
+TARGETS = 
+test_SOURCES = test.c
+wrapper_SOURCES = test.c
+INCLUDES += -I../../include
+
+UNIT = vgtest.t percent.t
+
+LVMLIBS = @LVM2APP_LIB@ -ldevmapper
+DEPLIBS = $(top_builddir)/liblvm/liblvm2app.so $(top_builddir)/libdm/libdevmapper.so
+
+DEFS += -D_REENTRANT
+
+include $(top_builddir)/make.tmpl
+
+LDFLAGS = -L$(top_builddir)/libdm -L$(top_builddir)/liblvm
+
+ifeq ("@DMEVENTD@", "yes")
+       LVMLIBS += -ldevmapper-event
+       LDFLAGS += -L$(top_builddir)/daemons/dmeventd
+endif
+
+test_OBJECTS = $(test_SOURCES:.c=.o)
+wrapper_OBJECTS = $(wrapper_SOURCES:.c=.o)
+OBJECTS = $(test_OBJECTS)
+
+all: tests test
+
+tests: $(UNIT)
+
+test: $(test_OBJECTS) $(DEPLIBS)
+       $(CC) -o test $(test_OBJECTS) $(CFLAGS) $(LDFLAGS) $(LVMLIBS) $(LIBS) $(READLINE_LIBS)
+
+%.t: %.o $(DEPLIBS)
+       $(CC) -o $@ $(<) $(CFLAGS) $(LDFLAGS) $(LVMLIBS) $(LIBS)
+
+wrapper: $(wrapper_OBJECTS) $(DEPLIBS)
+       $(CC) -o wrapper $(wrapper_OBJECTS) $(CFLAGS) $(LDFLAGS) $(LVMLIBS) $(LIBS) -lreadline
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       cd $(top_builddir) && $(SHELL) ./config.status test/api/Makefile
diff --git a/test/api/percent.c b/test/api/percent.c
new file mode 100644 (file)
index 0000000..a3be1eb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lvm2app.h"
+
+#define assert(x) do { if (!(x)) goto bad; } while (0)
+
+int main(int argc, char *argv[])
+{
+       lvm_t handle;
+       vg_t vg = NULL;
+       lv_t lv;
+       struct lvm_property_value v;
+
+       handle = lvm_init(NULL);
+        assert(handle);
+
+       vg = lvm_vg_open(handle, argv[1], "r", 0);
+        assert(vg);
+
+       lv = lvm_lv_from_name(vg, "snap");
+        assert(lv);
+
+        v = lvm_lv_get_property(lv, "snap_percent");
+        assert(v.is_valid);
+        assert(v.value.integer == PERCENT_0);
+
+        lv = lvm_lv_from_name(vg, "mirr");
+        assert(lv);
+
+        v = lvm_lv_get_property(lv, "copy_percent");
+        assert(v.is_valid);
+        assert(v.value.integer == PERCENT_100);
+
+        lv = lvm_lv_from_name(vg, "snap2");
+        assert(lv);
+
+        v = lvm_lv_get_property(lv, "snap_percent");
+        assert(v.is_valid);
+        assert(v.value.integer == 50 * PERCENT_1);
+
+        lvm_vg_close(vg);
+        return 0;
+
+bad:
+       if (handle && lvm_errno(handle))
+               fprintf(stderr, "LVM Error: %s\n", lvm_errmsg(handle));
+       if (vg)
+               lvm_vg_close(vg);
+       if (handle)
+               lvm_quit(handle);
+       return 1;
+}
diff --git a/test/api/percent.sh b/test/api/percent.sh
new file mode 100644 (file)
index 0000000..4362eb2
--- /dev/null
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+aux prepare_devs 2
+vgcreate -c n -s 4k $vg $devs
+lvcreate -n foo $vg -l 5
+lvcreate -s -n snap $vg/foo -l 2 -c 4k
+lvcreate -s -n snap2 $vg/foo -l 6 -c 4k
+dd if=/dev/urandom of=$DM_DEV_DIR/$vg/snap2 count=1 bs=1024
+lvcreate -m 1 -n mirr $vg -l 1 --mirrorlog core
+lvs
+apitest percent $vg
diff --git a/test/api/test.c b/test/api/test.c
new file mode 100644 (file)
index 0000000..434baea
--- /dev/null
@@ -0,0 +1,1091 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <readline/readline.h>
+
+#include "lvm2app.h"
+
+#define MAX_ARGS 64
+
+static int lvm_split(char *str, int *argc, char **argv, int max)
+{
+       char *b = str, *e;
+       *argc = 0;
+
+       while (*b) {
+               while (*b && isspace(*b))
+                       b++;
+
+               if ((!*b) || ((*argc == 0)&&(*b == '#')))
+                       break;
+
+               e = b;
+               while (*e && !isspace(*e))
+                       e++;
+
+               argv[(*argc)++] = b;
+               if (!*e)
+                       break;
+               *e++ = '\0';
+               b = e;
+               if (*argc == max)
+                       break;
+       }
+
+       return *argc;
+}
+
+static void _show_help(void)
+{
+       printf("'lv_activate vgname lvname: "
+              "Activate an LV\n");
+       printf("'lv_deactivate vgname lvname: "
+              "Deactivate an LV\n");
+       printf("'vg_remove_lv vgname lvname': "
+              "Remove a LV\n");
+       printf("'vg_create_lv_linear vgname lvname size_in_bytes': "
+              "Create a linear LV\n");
+       printf("'scan_vgs': "
+              "Scan the system for LVM metadata\n");
+       printf("'list_vg_names': "
+              "List the names of the VGs that exist in the system\n");
+       printf("'list_vg_ids': "
+              "List the uuids of the VGs that exist in the system\n");
+       printf("'vg_list_pvs vgname': "
+              "List the PVs that exist in VG vgname\n");
+       printf("'pv_list_pvsegs pvname': "
+              "List the PV segments that exist in PV pvname\n");
+       printf("'vg_list_lvs vgname': "
+              "List the LVs that exist in VG vgname\n");
+       printf("'lv_list_lvsegs vgname lvname': "
+              "List the LV segments that exist in LV vgname/lvname\n");
+       printf("'vgs_open': "
+              "List the VGs that are currently open\n");
+       printf("'vgs': "
+              "List all VGs known to the system\n");
+       printf("'vg_extend vgname device: "
+              "Issue a lvm_vg_extend() API call on VG 'vgname'\n");
+       printf("'vg_reduce vgname device: "
+              "Issue a lvm_vg_reduce() API call on VG 'vgname'\n");
+       printf("'vg_open vgname ['r' | 'w']': "
+              "Issue a lvm_vg_open() API call on VG 'vgname'\n");
+       printf("'vg_close vgname': "
+              "Issue a lvm_vg_close() API call on VG 'vgname'\n");
+       printf("'vg_create vgname: "
+              "Issue a lvm_vg_create() to create VG 'vgname'\n");
+       printf("'vg_remove vgname: "
+              "Issue a lvm_vg_remove() to remove VG 'vgname'\n");
+       printf("'config_reload': "
+              "Issue a lvm_config_reload() API to reload LVM config\n");
+       printf("'config_override' device: "
+              "Issue a lvm_config_override() with accept device filter\n");
+       printf("'vg_get_tags vgname': "
+              "List the tags of a VG\n");
+       printf("'lv_get_property vgname lvname property_name': "
+              "Display the value of LV property\n");
+       printf("'vg_get_property vgname property_name': "
+              "Display the value of VG property\n");
+       printf("'pv_get_property pvname property_name': "
+              "Display the value of PV property\n");
+       printf("'vg_set_property vgname property_name': "
+              "Set the value of VG property\n");
+       printf("'lv_get_tags vgname lvname': "
+              "List the tags of a LV\n");
+       printf("'vg_{add|remove}_tag vgname tag': "
+              "Add/remove a tag from a VG\n");
+       printf("'lv_{add|remove}_tag vgname lvname tag': "
+              "Add/remove a tag from a LV\n");
+       printf("'vgname_from_devname device': "
+              "Lookup a vgname from a device name\n");
+       printf("'vgname_from_pvid pvid': "
+              "Lookup a vgname from a pvid\n");
+       printf("'lv_from_uuid vgname lvuuid': "
+              "Lookup an LV from an LV uuid\n");
+       printf("'lv_from_name vgname lvname': "
+              "Lookup an LV from an LV name\n");
+       printf("'pv_from_uuid vgname pvuuid': "
+              "Lookup an LV from an LV uuid\n");
+       printf("'pv_from_name vgname pvname': "
+              "Lookup an LV from an LV name\n");
+       printf("'quit': exit the program\n");
+}
+
+static struct dm_hash_table *_vgid_hash = NULL;
+static struct dm_hash_table *_vgname_hash = NULL;
+static struct dm_hash_table *_pvname_hash = NULL;
+static struct dm_hash_table *_lvname_hash = NULL;
+
+static void _hash_destroy_single(struct dm_hash_table **htable)
+{
+       if (htable && *htable) {
+               dm_hash_destroy(*htable);
+               *htable = NULL;
+       }
+}
+
+static void _hash_destroy(void)
+{
+       _hash_destroy_single(&_vgname_hash);
+       _hash_destroy_single(&_vgid_hash);
+       _hash_destroy_single(&_pvname_hash);
+       _hash_destroy_single(&_lvname_hash);
+}
+
+static int _hash_create(void)
+{
+       if (!(_vgname_hash = dm_hash_create(128)))
+               return 0;
+       if (!(_pvname_hash = dm_hash_create(128))) {
+               _hash_destroy_single(&_vgname_hash);
+               return 0;
+       }
+       if (!(_lvname_hash = dm_hash_create(128))) {
+               _hash_destroy_single(&_vgname_hash);
+               _hash_destroy_single(&_pvname_hash);
+               return 0;
+       }
+       if (!(_vgid_hash = dm_hash_create(128))) {
+               _hash_destroy_single(&_vgname_hash);
+               _hash_destroy_single(&_pvname_hash);
+               _hash_destroy_single(&_lvname_hash);
+               return 0;
+       }
+       return 1;
+}
+
+/* FIXME: this should be per vg */
+static lv_t _lookup_lv_by_name(const char *name)
+{
+       lv_t lv;
+
+       if (!name) {
+               printf ("Invalid LV name\n");
+               return NULL;
+       }
+       if (!(lv = dm_hash_lookup(_lvname_hash, name))) {
+               printf ("Can't find %s in LVs - run vg_create_lv first\n",
+                       name);
+               return NULL;
+       }
+       return lv;
+}
+
+static vg_t _lookup_vg_by_name(char **argv, int argc)
+{
+       vg_t vg;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return NULL;
+       }
+       if (!(vg = dm_hash_lookup(_vgid_hash, argv[1])) &&
+           !(vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+               printf ("Can't find %s in open VGs - run vg_open first\n",
+                       argv[1]);
+               return NULL;
+       }
+       return vg;
+}
+
+static pv_t _lookup_pv_by_name(const char *name)
+{
+       pv_t pv;
+
+       if (!(pv = dm_hash_lookup(_pvname_hash, name))) {
+               printf ("Can't find %s in open PVs - run vg_open first\n",
+                       name);
+               return NULL;
+       }
+       return pv;
+}
+
+static void _add_lvs_to_lvname_hash(struct dm_list *lvs)
+{
+       struct lvm_lv_list *lvl;
+       dm_list_iterate_items(lvl, lvs) {
+               /* Concatenate VG name with LV name */
+               dm_hash_insert(_lvname_hash, lvm_lv_get_name(lvl->lv), lvl->lv);
+       }
+}
+
+static void _add_pvs_to_pvname_hash(struct dm_list *pvs)
+{
+       struct lvm_pv_list *pvl;
+       dm_list_iterate_items(pvl, pvs) {
+               dm_hash_insert(_pvname_hash, lvm_pv_get_name(pvl->pv), pvl->pv);
+       }
+}
+
+static void _remove_device_from_pvname_hash(struct dm_list *pvs, const char *name)
+{
+       struct lvm_pv_list *pvl;
+       dm_list_iterate_items(pvl, pvs) {
+               if (!strncmp(lvm_pv_get_name(pvl->pv), name, strlen(name)))
+                       dm_hash_remove(_pvname_hash, name);
+       }
+}
+static void _add_device_to_pvname_hash(struct dm_list *pvs, const char *name)
+{
+       struct lvm_pv_list *pvl;
+       dm_list_iterate_items(pvl, pvs) {
+               if (!strncmp(lvm_pv_get_name(pvl->pv), name, strlen(name)))
+                       dm_hash_insert(_pvname_hash, name, pvl->pv);
+       }
+}
+
+static void _vg_reduce(char **argv, int argc, lvm_t libh)
+{
+       vg_t vg;
+       struct dm_list *pvs;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       if (!(vg = dm_hash_lookup(_vgid_hash, argv[1])) &&
+           !(vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+               printf ("VG not open\n");
+               return;
+       }
+       if (lvm_vg_reduce(vg, argv[2])) {
+               printf("Error reducing %s by %s\n", argv[1], argv[2]);
+               return;
+       }
+
+       printf("Success reducing vg %s by %s\n", argv[1], argv[2]);
+
+       /*
+        * Add the device into the hashes for lookups
+        */
+       pvs = lvm_vg_list_pvs(vg);
+       if (pvs && !dm_list_empty(pvs))
+               _remove_device_from_pvname_hash(pvs, argv[2]);
+}
+
+/* Print "Error" or "Success" depending on lvm status */
+static int _lvm_status_to_pass_fail(int rc)
+{
+       if (rc)
+               printf("Error ");
+       else
+               printf("Success ");
+       return rc;
+}
+static void _config_override(char **argv, int argc, lvm_t libh)
+{
+       int rc;
+       char tmp[64];
+
+       if (argc < 2) {
+               printf ("Please enter device\n");
+               return;
+       }
+       snprintf(tmp, 63, "devices{filter=[\"a|%s|\", \"r|.*|\"]}", argv[1]);
+       rc = lvm_config_override(libh, tmp);
+       _lvm_status_to_pass_fail(rc);
+       printf("overriding LVM configuration\n");
+}
+
+static void _config_reload(char **argv, int argc, lvm_t libh)
+{
+       int rc;
+       rc = lvm_config_reload(libh);
+       _lvm_status_to_pass_fail(rc);
+       printf("reloading LVM configuration\n");
+}
+
+static void _vg_extend(char **argv, int argc, lvm_t libh)
+{
+       vg_t vg;
+       struct dm_list *pvs;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       if (!(vg = dm_hash_lookup(_vgid_hash, argv[1])) &&
+           !(vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+               printf ("VG not open\n");
+               return;
+       }
+       if (lvm_vg_extend(vg, argv[2])) {
+               printf("Error extending %s with %s\n", argv[1], argv[2]);
+               return;
+       }
+
+       printf("Success extending vg %s with %s\n", argv[1], argv[2]);
+
+       /*
+        * Add the device into the hashes for lookups
+        */
+       pvs = lvm_vg_list_pvs(vg);
+       if (pvs && !dm_list_empty(pvs))
+               _add_device_to_pvname_hash(pvs, argv[2]);
+}
+
+static void _vg_open(char **argv, int argc, lvm_t libh)
+{
+       vg_t vg;
+       struct dm_list *lvs;
+       struct dm_list *pvs;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       if ((vg = dm_hash_lookup(_vgid_hash, argv[1])) ||
+           (vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+               printf ("VG already open\n");
+               return;
+       }
+       if (argc < 3)
+               vg = lvm_vg_open(libh, argv[1], "r", 0);
+       else
+               vg = lvm_vg_open(libh, argv[1], argv[2], 0);
+       if (!vg || !lvm_vg_get_name(vg)) {
+               printf("Error opening %s\n", argv[1]);
+               return;
+       }
+
+       printf("Success opening vg %s\n", argv[1]);
+       dm_hash_insert(_vgname_hash, lvm_vg_get_name(vg), vg);
+       dm_hash_insert(_vgid_hash, lvm_vg_get_uuid(vg), vg);
+
+       /*
+        * Add the LVs and PVs into the hashes for lookups
+        */
+       lvs = lvm_vg_list_lvs(vg);
+       if (lvs && !dm_list_empty(lvs))
+               _add_lvs_to_lvname_hash(lvs);
+       pvs = lvm_vg_list_pvs(vg);
+       if (pvs && !dm_list_empty(pvs))
+               _add_pvs_to_pvname_hash(pvs);
+}
+/* Lookup the vg and remove it from the vgname and vgid hashes */
+static vg_t _lookup_and_remove_vg(const char *vgname)
+{
+       vg_t vg=NULL;
+
+       if ((vg = dm_hash_lookup(_vgname_hash, vgname))) {
+               dm_hash_remove(_vgid_hash, lvm_vg_get_uuid(vg));
+               dm_hash_remove(_vgname_hash, lvm_vg_get_name(vg));
+       }
+       if (!vg && (vg = dm_hash_lookup(_vgid_hash, vgname))) {
+               dm_hash_remove(_vgid_hash, lvm_vg_get_uuid(vg));
+               dm_hash_remove(_vgname_hash, lvm_vg_get_name(vg));
+       }
+       return vg;
+}
+
+static void _vg_write(char **argv, int argc)
+{
+       vg_t vg;
+       int rc = 0;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       vg = _lookup_vg_by_name(argv, argc);
+       if (!vg) {
+               printf("Can't find vg_name %s\n", argv[1]);
+               return;
+       }
+       rc = lvm_vg_write(vg);
+       _lvm_status_to_pass_fail(rc);
+       printf("writing VG %s\n", lvm_vg_get_name(vg));
+}
+
+static void _vg_create(char **argv, int argc, lvm_t libh)
+{
+       vg_t vg;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       vg = lvm_vg_create(libh, argv[1]);
+       if (!vg || !lvm_vg_get_name(vg)) {
+               printf("Error creating %s\n", argv[1]);
+               return;
+       }
+
+       printf("Success creating vg %s\n", argv[1]);
+       dm_hash_insert(_vgname_hash, lvm_vg_get_name(vg), vg);
+       dm_hash_insert(_vgid_hash, lvm_vg_get_uuid(vg), vg);
+}
+
+static void _vg_remove(char **argv, int argc)
+{
+       vg_t vg;
+       int rc = 0;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       vg = _lookup_vg_by_name(argv, argc);
+       if (!vg) {
+               printf("Can't find vg_name %s\n", argv[1]);
+               return;
+       }
+       rc = lvm_vg_remove(vg);
+       _lvm_status_to_pass_fail(rc);
+       printf("removing VG\n");
+}
+
+static void _vg_close(char **argv, int argc)
+{
+       vg_t vg;
+       int rc = 0;
+
+       if (argc < 2) {
+               printf ("Please enter vg_name\n");
+               return;
+       }
+       vg = _lookup_and_remove_vg(argv[1]);
+       if (!vg) {
+               printf("Can't find vg_name %s\n", argv[1]);
+               return;
+       }
+       rc = lvm_vg_close(vg);
+       _lvm_status_to_pass_fail(rc);
+       printf("closing VG\n");
+}
+
+static void _show_one_vg(vg_t vg)
+{
+       printf("%s (%s): sz=%"PRIu64", free=%"PRIu64", #pv=%"PRIu64
+               ", seq#=%"PRIu64"\n",
+               lvm_vg_get_name(vg), lvm_vg_get_uuid(vg),
+               lvm_vg_get_size(vg), lvm_vg_get_free_size(vg),
+               lvm_vg_get_pv_count(vg), lvm_vg_get_seqno(vg));
+}
+
+static void _print_pv(pv_t pv)
+{
+       if (!pv)
+               return;
+       printf("%s (%s): size=%"PRIu64", free=%"PRIu64
+              ", dev_size=%"PRIu64", mda_count=%"PRIu64"\n",
+              lvm_pv_get_name(pv), lvm_pv_get_uuid(pv),
+              lvm_pv_get_size(pv), lvm_pv_get_free(pv),
+              lvm_pv_get_dev_size(pv),
+              lvm_pv_get_mda_count(pv));
+}
+
+static void _print_lv(vg_t vg, lv_t lv)
+{
+       if (!lv)
+               return;
+       printf("%s/%s (%s): size=%"PRIu64", %sACTIVE / %sSUSPENDED\n",
+              lvm_vg_get_name(vg),
+              lvm_lv_get_name(lv), lvm_lv_get_uuid(lv),
+              lvm_lv_get_size(lv),
+              lvm_lv_is_active(lv) ? "" : "IN",
+              lvm_lv_is_suspended(lv) ? "" : "NOT ");
+}
+
+static void _list_open_vgs(void)
+{
+       dm_hash_iter(_vgid_hash, (dm_hash_iterate_fn) _show_one_vg);
+}
+
+static void _pvs_in_vg(char **argv, int argc)
+{
+       struct dm_list *pvs;
+       struct lvm_pv_list *pvl;
+       vg_t vg;
+
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       pvs = lvm_vg_list_pvs(vg);
+       if (!pvs || dm_list_empty(pvs)) {
+               printf("No PVs in VG %s\n", lvm_vg_get_name(vg));
+               return;
+       }
+       printf("PVs in VG %s:\n", lvm_vg_get_name(vg));
+       dm_list_iterate_items(pvl, pvs) {
+               _print_pv(pvl->pv);
+       }
+}
+
+static void _print_property_value(const char *name,
+                                 struct lvm_property_value v)
+{
+       if (!v.is_valid)
+               printf("%s = INVALID\n", name);
+       else if (v.is_string)
+               printf("%s = %s\n", name, v.value.string);
+       else
+               printf("%s = %"PRIu64"\n", name, v.value.integer);
+}
+
+static void _pvsegs_in_pv(char **argv, int argc)
+{
+       struct dm_list *pvsegs;
+       struct lvm_pvseg_list *pvl;
+       pv_t pv;
+
+       if (!(pv = _lookup_pv_by_name(argv[1])))
+               return;
+       pvsegs = lvm_pv_list_pvsegs(pv);
+       if (!pvsegs || dm_list_empty(pvsegs)) {
+               printf("No PV segments in pv %s\n", argv[1]);
+               return;
+       }
+       printf("PV segments in pv %s:\n", argv[1]);
+       dm_list_iterate_items(pvl, pvsegs) {
+               struct lvm_property_value v;
+               v = lvm_pvseg_get_property(pvl->pvseg, "pvseg_start");
+               _print_property_value("pvseg_start", v);
+               v = lvm_pvseg_get_property(pvl->pvseg, "pvseg_size");
+               _print_property_value("pvseg_size", v);
+       }
+}
+
+static void _scan_vgs(lvm_t libh)
+{
+       lvm_scan(libh);
+}
+
+static void _list_vg_names(lvm_t libh)
+{
+       struct dm_list *list;
+       struct lvm_str_list *strl;
+
+       list = lvm_list_vg_names(libh);
+       printf("VG names:\n");
+       dm_list_iterate_items(strl, list) {
+               printf("%s\n", strl->str);
+       }
+}
+
+static void _list_vg_ids(lvm_t libh)
+{
+       struct dm_list *list;
+       struct lvm_str_list *strl;
+
+       list = lvm_list_vg_uuids(libh);
+       printf("VG uuids:\n");
+       dm_list_iterate_items(strl, list) {
+               printf("%s\n", strl->str);
+       }
+}
+
+static void _display_tags(struct dm_list *list)
+{
+       struct lvm_str_list *strl;
+       if (dm_list_empty(list)) {
+               printf("No tags exist\n");
+               return;
+       } else if (!list) {
+               printf("Error obtaining tags\n");
+               return;
+       }
+       dm_list_iterate_items(strl, list) {
+               printf("%s\n", strl->str);
+       }
+}
+
+static void _vg_get_tags(char **argv, int argc)
+{
+       vg_t vg;
+
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       printf("VG tags:\n");
+       _display_tags(lvm_vg_get_tags(vg));
+}
+
+static void _vg_tag(char **argv, int argc, int add)
+{
+       vg_t vg;
+
+       if (argc < 3) {
+               printf("Please enter vgname, tag\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       if (add && lvm_vg_add_tag(vg, argv[2]))
+               printf("Error ");
+       else if (!add && lvm_vg_remove_tag(vg, argv[2])){
+               printf("Error ");
+       } else {
+               printf("Success ");
+       }
+       printf("%s tag %s to VG %s\n",
+              add ? "adding":"removing", argv[2], argv[1]);
+}
+
+static void _pv_get_property(char **argv, int argc)
+{
+       pv_t pv;
+       struct lvm_property_value v;
+
+       if (argc < 3) {
+               printf("Please enter pvname, field_id\n");
+               return;
+       }
+       if (!(pv = _lookup_pv_by_name(argv[1])))
+               return;
+       v = lvm_pv_get_property(pv, argv[2]);
+       _print_property_value(argv[2], v);
+}
+
+static void _vg_get_property(char **argv, int argc)
+{
+       vg_t vg;
+       struct lvm_property_value v;
+
+       if (argc < 3) {
+               printf("Please enter vgname, field_id\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       v =  lvm_vg_get_property(vg, argv[2]);
+       _print_property_value(argv[2], v);
+}
+
+static void _lv_get_property(char **argv, int argc)
+{
+       lv_t lv;
+       struct lvm_property_value v;
+
+       if (argc < 4) {
+               printf("Please enter vgname, lvname, field_id\n");
+               return;
+       }
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       v = lvm_lv_get_property(lv, argv[3]);
+       _print_property_value(argv[3], v);
+}
+
+static void _vg_set_property(char **argv, int argc)
+{
+       vg_t vg;
+       struct lvm_property_value value;
+       int rc;
+
+       if (argc < 4) {
+               printf("Please enter vgname, field_id, value\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       value = lvm_vg_get_property(vg, argv[2]);
+       if (!value.is_valid) {
+               printf("Error obtaining property value\n");
+               return;
+       }
+       if (value.is_string)
+               value.value.string = argv[3];
+       else
+               value.value.integer = atoi(argv[3]);
+       rc = lvm_vg_set_property(vg, argv[2], &value);
+       if (rc)
+               printf("Error ");
+       else
+               printf("Success ");
+       printf("setting value of property %s in VG %s\n",
+              argv[2], argv[1]);
+}
+
+static void _lv_get_tags(char **argv, int argc)
+{
+       lv_t lv;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lvname\n");
+               return;
+       }
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       printf("LV tags:\n");
+       _display_tags(lvm_lv_get_tags(lv));
+}
+
+static void _lv_tag(char **argv, int argc, int add)
+{
+       lv_t lv;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lvname\n");
+               return;
+       }
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       if (add && lvm_lv_add_tag(lv, argv[3]))
+               printf("Error ");
+       else if (!add && lvm_lv_remove_tag(lv, argv[3])){
+               printf("Error ");
+       } else {
+               printf("Success ");
+       }
+       printf("%s tag %s to LV %s\n",
+              add ? "adding":"removing", argv[3], argv[2]);
+}
+
+static void _lv_from_uuid(char **argv, int argc)
+{
+       vg_t vg;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lv_uuid\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       _print_lv(vg, lvm_lv_from_uuid(vg, argv[2]));
+}
+
+static void _lv_from_name(char **argv, int argc)
+{
+       vg_t vg;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lv_uuid\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       _print_lv(vg, lvm_lv_from_name(vg, argv[2]));
+}
+
+static void _pv_from_uuid(char **argv, int argc)
+{
+       vg_t vg;
+
+       if (argc < 3) {
+               printf("Please enter vgname, pv_uuid\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       _print_pv(lvm_pv_from_uuid(vg, argv[2]));
+}
+
+static void _pv_from_name(char **argv, int argc)
+{
+       vg_t vg;
+
+       if (argc < 3) {
+               printf("Please enter vgname, pv_uuid\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       _print_pv(lvm_pv_from_name(vg, argv[2]));
+}
+
+static void _vgname_from_pvid(char **argv, int argc, lvm_t libh)
+{
+       const char *vgname;
+
+       if (argc < 1) {
+               printf("Please enter pvid\n");
+               return;
+       }
+       if (!(vgname = lvm_vgname_from_pvid(libh, argv[1]))) {
+               printf("Error ");
+       } else {
+               printf("Success ");
+       }
+       printf("looking up vgname=%s from PVID=%s\n",
+              vgname, argv[1]);
+}
+static void _vgname_from_devname(char **argv, int argc, lvm_t libh)
+{
+       const char *vgname;
+
+       if (argc < 1) {
+               printf("Please enter device\n");
+               return;
+       }
+       if (!(vgname = lvm_vgname_from_device(libh, argv[1]))) {
+               printf("Error ");
+       } else {
+               printf("Success ");
+       }
+       printf("looking up vgname=%s from device name=%s\n",
+              vgname, argv[1]);
+}
+static void _lvs_in_vg(char **argv, int argc)
+{
+       struct dm_list *lvs;
+       struct lvm_lv_list *lvl;
+       vg_t vg;
+
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       lvs = lvm_vg_list_lvs(vg);
+       if (!lvs || dm_list_empty(lvs)) {
+               printf("No LVs in VG %s\n", lvm_vg_get_name(vg));
+               return;
+       }
+       printf("LVs in VG %s:\n", lvm_vg_get_name(vg));
+       dm_list_iterate_items(lvl, lvs) {
+               _print_lv(vg, lvl->lv);
+       }
+}
+
+static void _lvsegs_in_lv(char **argv, int argc)
+{
+       struct dm_list *lvsegs;
+       struct lvm_lvseg_list *lvl;
+       lv_t lv;
+
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       lvsegs = lvm_lv_list_lvsegs(lv);
+       if (!lvsegs || dm_list_empty(lvsegs)) {
+               printf("No LV segments in lv %s\n", lvm_lv_get_name(lv));
+               return;
+       }
+       printf("LV segments in lv %s:\n", lvm_lv_get_name(lv));
+       dm_list_iterate_items(lvl, lvsegs) {
+               struct lvm_property_value v;
+               v = lvm_lvseg_get_property(lvl->lvseg, "segtype");
+               _print_property_value("segtype", v);
+               v = lvm_lvseg_get_property(lvl->lvseg, "seg_start_pe");
+               _print_property_value("seg_start_pe", v);
+               v = lvm_lvseg_get_property(lvl->lvseg, "seg_size");
+               _print_property_value("seg_size", v);
+       }
+}
+
+static void _lv_deactivate(char **argv, int argc)
+{
+       lv_t lv;
+       int rc=0;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lvname\n");
+               return;
+       }
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       rc = lvm_lv_deactivate(lv);
+       _lvm_status_to_pass_fail(rc);
+       printf("De-activating LV %s in VG %s\n",
+               argv[2], argv[1]);
+}
+static void _lv_activate(char **argv, int argc)
+{
+       lv_t lv;
+       int rc=0;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lvname\n");
+               return;
+       }
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       rc = lvm_lv_activate(lv);
+       _lvm_status_to_pass_fail(rc);
+       printf("activating LV %s in VG %s\n",
+               argv[2], argv[1]);
+}
+
+static void _vg_remove_lv(char **argv, int argc)
+{
+       lv_t lv;
+
+       if (argc < 3) {
+               printf("Please enter vgname, lvname\n");
+               return;
+       }
+       if (!(lv = _lookup_lv_by_name(argv[2])))
+               return;
+       if (lvm_vg_remove_lv(lv))
+               printf("Error ");
+       else {
+               printf("Success ");
+               dm_hash_remove(_lvname_hash, argv[2]);
+       }
+       printf("removing LV %s in VG %s\n",
+               argv[2], argv[1]);
+}
+
+static void _vg_create_lv_linear(char **argv, int argc)
+{
+       vg_t vg;
+       lv_t lv;
+
+       if (argc < 4) {
+               printf("Please enter vgname, lvname, and size\n");
+               return;
+       }
+       if (!(vg = _lookup_vg_by_name(argv, argc)))
+               return;
+       lv = lvm_vg_create_lv_linear(vg, argv[2], atol(argv[3]));
+       if (!lv)
+               printf("Error ");
+       else {
+               printf("Success ");
+               dm_hash_insert(_lvname_hash, argv[2], lv);
+       }
+       printf("creating LV %s in VG %s\n",
+               argv[2], argv[1]);
+}
+
+static int lvmapi_test_shell(lvm_t libh)
+{
+       int argc;
+       char *input = NULL, *args[MAX_ARGS], **argv;
+
+       _hash_create();
+       argc=0;
+       while (1) {
+               free(input);
+               input = readline("liblvm> ");
+
+               /* EOF */
+               if (!input) {
+                       printf("\n");
+                       break;
+               }
+
+               /* empty line */
+               if (!*input)
+                       continue;
+
+               argv = args;
+
+               if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+                       printf("Too many arguments, sorry.");
+                       continue;
+               }
+
+               if (!strcmp(argv[0], "lvm")) {
+                       argv++;
+                       argc--;
+               }
+
+               if (!argc)
+                       continue;
+
+               if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
+                       printf("Exiting.\n");
+                       break;
+               } else if (!strcmp(argv[0], "?") || !strcmp(argv[0], "help")) {
+                       _show_help();
+               } else if (!strcmp(argv[0], "config_reload")) {
+                       _config_reload(argv, argc, libh);
+               } else if (!strcmp(argv[0], "config_override")) {
+                       _config_override(argv, argc, libh);
+               } else if (!strcmp(argv[0], "vg_extend")) {
+                       _vg_extend(argv, argc, libh);
+               } else if (!strcmp(argv[0], "vg_reduce")) {
+                       _vg_reduce(argv, argc, libh);
+               } else if (!strcmp(argv[0], "vg_write")) {
+                       _vg_write(argv, argc);
+               } else if (!strcmp(argv[0], "vg_open")) {
+                       _vg_open(argv, argc, libh);
+               } else if (!strcmp(argv[0], "vg_close")) {
+                       _vg_close(argv, argc);
+               } else if (!strcmp(argv[0], "vg_create")) {
+                       _vg_create(argv, argc, libh);
+               } else if (!strcmp(argv[0], "vg_remove")) {
+                       _vg_remove(argv, argc);
+               } else if (!strcmp(argv[0], "lv_activate")) {
+                       _lv_activate(argv, argc);
+               } else if (!strcmp(argv[0], "lv_deactivate")) {
+                       _lv_deactivate(argv, argc);
+               } else if (!strcmp(argv[0], "vg_remove_lv")) {
+                       _vg_remove_lv(argv, argc);
+               } else if (!strcmp(argv[0], "vgs_open")) {
+                       _list_open_vgs();
+               } else if (!strcmp(argv[0], "vg_list_pvs")) {
+                       _pvs_in_vg(argv, argc);
+               } else if (!strcmp(argv[0], "pv_list_pvsegs")) {
+                       _pvsegs_in_pv(argv, argc);
+               } else if (!strcmp(argv[0], "vg_list_lvs")) {
+                       _lvs_in_vg(argv, argc);
+               } else if (!strcmp(argv[0], "lv_list_lvsegs")) {
+                       _lvsegs_in_lv(argv, argc);
+               } else if (!strcmp(argv[0], "list_vg_names")) {
+                       _list_vg_names(libh);
+               } else if (!strcmp(argv[0], "list_vg_ids")) {
+                       _list_vg_ids(libh);
+               } else if (!strcmp(argv[0], "scan_vgs")) {
+                       _scan_vgs(libh);
+               } else if (!strcmp(argv[0], "vg_create_lv_linear")) {
+                       _vg_create_lv_linear(argv, argc);
+               } else if (!strcmp(argv[0], "vg_add_tag")) {
+                       _vg_tag(argv, argc, 1);
+               } else if (!strcmp(argv[0], "vg_remove_tag")) {
+                       _vg_tag(argv, argc, 0);
+               } else if (!strcmp(argv[0], "vg_get_tags")) {
+                       _vg_get_tags(argv, argc);
+               } else if (!strcmp(argv[0], "lv_get_property")) {
+                       _lv_get_property(argv, argc);
+               } else if (!strcmp(argv[0], "vg_get_property")) {
+                       _vg_get_property(argv, argc);
+               } else if (!strcmp(argv[0], "pv_get_property")) {
+                       _pv_get_property(argv, argc);
+               } else if (!strcmp(argv[0], "vg_set_property")) {
+                       _vg_set_property(argv, argc);
+               } else if (!strcmp(argv[0], "lv_add_tag")) {
+                       _lv_tag(argv, argc, 1);
+               } else if (!strcmp(argv[0], "lv_remove_tag")) {
+                       _lv_tag(argv, argc, 0);
+               } else if (!strcmp(argv[0], "lv_get_tags")) {
+                       _lv_get_tags(argv, argc);
+               } else if (!strcmp(argv[0], "vgname_from_devname")) {
+                       _vgname_from_devname(argv, argc, libh);
+               } else if (!strcmp(argv[0], "vgname_from_pvid")) {
+                       _vgname_from_pvid(argv, argc, libh);
+               } else if (!strcmp(argv[0], "lv_from_uuid")) {
+                       _lv_from_uuid(argv, argc);
+               } else if (!strcmp(argv[0], "lv_from_name")) {
+                       _lv_from_name(argv, argc);
+               } else if (!strcmp(argv[0], "pv_from_uuid")) {
+                       _pv_from_uuid(argv, argc);
+               } else if (!strcmp(argv[0], "pv_from_name")) {
+                       _pv_from_name(argv, argc);
+               } else {
+                       printf ("Unrecognized command %s\n", argv[0]);
+               }
+       }
+
+       dm_hash_iter(_vgname_hash, (dm_hash_iterate_fn) lvm_vg_close);
+       _hash_destroy();
+       free(input);
+       return 0;
+}
+
+int main (int argc, char *argv[])
+{
+       lvm_t libh;
+
+       libh = lvm_init(NULL);
+       if (!libh) {
+               printf("Unable to open lvm library instance\n");
+               return 1;
+       }
+
+       printf("Library version: %s\n", lvm_library_get_version());
+       lvmapi_test_shell(libh);
+
+       lvm_quit(libh);
+       return 0;
+}
+
diff --git a/test/api/vgtest.c b/test/api/vgtest.c
new file mode 100644 (file)
index 0000000..cb35da3
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+/*
+ * Unit test case for vgcreate and related APIs.
+ * # gcc -g vgcreate.c -I../../liblvm -I../../include -L../../liblvm \
+ *   -L../../libdm -ldevmapper -llvm2app
+ * # export LD_LIBRARY_PATH=`pwd`/../../libdm:`pwd`/../../liblvm
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "lvm2app.h"
+
+lvm_t handle;
+vg_t vg;
+const char *vg_name;
+#define MAX_DEVICES 16
+const char *device[MAX_DEVICES];
+uint64_t size = 1024;
+
+#define vg_create(vg_name) \
+       printf("Creating VG %s\n", vg_name); \
+       vg = lvm_vg_create(handle, vg_name); \
+       if (!vg) { \
+               fprintf(stderr, "Error creating volume group %s\n", vg_name); \
+               goto bad; \
+       }
+#define vg_extend(vg, dev) \
+       printf("Extending VG %s by %s\n", vg_name, dev); \
+       status = lvm_vg_extend(vg, dev); \
+       if (status) { \
+               fprintf(stderr, "Error extending volume group %s " \
+                       "with device %s\n", vg_name, dev); \
+               goto bad; \
+       }
+#define vg_commit(vg) \
+       printf("Committing VG %s to disk\n", vg_name); \
+       status = lvm_vg_write(vg); \
+       if (status) { \
+               fprintf(stderr, "Commit of volume group '%s' failed\n", \
+                       lvm_vg_get_name(vg)); \
+               goto bad; \
+       }
+#define vg_open(vg_name, mode) \
+       printf("Opening VG %s %s\n", vg_name, mode); \
+       vg = lvm_vg_open(handle, vg_name, mode, 0); \
+       if (!vg) { \
+               fprintf(stderr, "Error opening volume group %s\n", vg_name); \
+               goto bad; \
+       }
+#define vg_close(vg) \
+       printf("Closing VG %s\n", vg_name); \
+       if (lvm_vg_close(vg)) { \
+               fprintf(stderr, "Error closing volume group %s\n", vg_name); \
+               goto bad; \
+       }
+#define vg_reduce(vg, dev) \
+       printf("Reducing VG %s by %s\n", vg_name, dev); \
+       status = lvm_vg_reduce(vg, dev); \
+       if (status) { \
+               fprintf(stderr, "Error reducing volume group %s " \
+                       "by device %s\n", vg_name, dev); \
+               goto bad; \
+       }
+#define vg_remove(vg) \
+       printf("Removing VG %s from system\n", vg_name); \
+       status = lvm_vg_remove(vg); \
+       if (status) { \
+               fprintf(stderr, "Revmoval of volume group '%s' failed\n", \
+                       vg_name); \
+               goto bad; \
+       }
+
+static int init_vgtest(int argc, char *argv[])
+{
+       int i;
+
+       if (argc < 4) {
+               fprintf(stderr, "Usage: %s <vgname> <pv1> <pv2> [... <pvN> ]",
+                       argv[0]);
+               return -1;
+       }
+       vg_name = argv[1];
+       for(i=2; i<MAX_DEVICES && i < argc; i++) {
+               device[i-2] = argv[i];
+       }
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int status;
+
+       if (init_vgtest(argc, argv) < 0)
+               goto bad;
+
+       /* FIXME: make the below messages verbose-only and print PASS/FAIL*/
+       printf("Opening LVM\n");
+       handle = lvm_init(NULL);
+       if (!handle) {
+               fprintf(stderr, "Unable to lvm_init\n");
+               goto bad;
+       }
+
+       printf("Library version: %s\n", lvm_library_get_version());
+       vg_create(vg_name);
+       vg_extend(vg, device[0]);
+
+       printf("Setting VG %s extent_size to %"PRIu64"\n", vg_name, size);
+       status = lvm_vg_set_extent_size(vg, size);
+       if (status) {
+               fprintf(stderr, "Can not set physical extent "
+                       "size '%"PRIu64"' for '%s'\n",
+                       size, vg_name);
+               goto bad;
+       }
+
+       vg_commit(vg);
+       vg_close(vg);
+
+       vg_open(vg_name, "r");
+       vg_close(vg);
+
+       vg_open(vg_name, "w");
+       vg_extend(vg, device[1]);
+       vg_reduce(vg, device[0]);
+       vg_commit(vg);
+       vg_close(vg);
+
+       vg_open(vg_name, "w");
+       vg_extend(vg, device[0]);
+       vg_commit(vg);
+       vg_close(vg);
+
+       vg_open(vg_name, "w");
+       vg_remove(vg);
+       vg_commit(vg);
+       vg_close(vg);
+
+       lvm_quit(handle);
+       printf("liblvm vgcreate unit test PASS\n");
+       _exit(0);
+bad:
+       printf("liblvm vgcreate unit test FAIL\n");
+       if (handle && lvm_errno(handle))
+               fprintf(stderr, "LVM Error: %s\n", lvm_errmsg(handle));
+       if (vg)
+               lvm_vg_close(vg);
+       if (handle)
+               lvm_quit(handle);
+       _exit(-1);
+}
diff --git a/test/api/vgtest.sh b/test/api/vgtest.sh
new file mode 100644 (file)
index 0000000..35daa44
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# tests lvm2app library
+#
+
+. ./test-utils.sh
+aux prepare_devs 2
+pvcreate $dev1 $dev2
+apitest vgtest $vg1 $dev1 $dev2
diff --git a/test/check.sh b/test/check.sh
new file mode 100644 (file)
index 0000000..843ce86
--- /dev/null
@@ -0,0 +1,198 @@
+#!/bin/bash
+
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# check.sh: assert various things about volumes
+
+# USAGE
+#  check linear VG LV
+#  check lv_on VG LV PV
+
+#  check mirror VG LV [LOGDEV|core]
+#  check mirror_nonredundant VG LV
+#  check mirror_legs VG LV N
+#  check mirror_images_on VG LV DEV [DEV...]
+
+# ...
+
+set -e -o pipefail
+
+lvl() {
+       lvs -a --noheadings "$@"
+}
+
+lvdevices() {
+       lvl -odevices "$@" | sed 's/([^)]*)//g; s/,/ /g'
+}
+
+mirror_images_redundant()
+{
+  vg=$1
+  lv=$vg/$2
+
+  lvs -a $vg -o+devices
+  for i in `lvdevices $lv`; do
+         echo "# $i:"
+         lvdevices $vg/$i | sort | uniq
+  done > check.tmp.all
+
+  (grep -v ^# check.tmp.all || true) | sort | uniq -d > check.tmp
+
+  test "`cat check.tmp | wc -l`" -eq 0 || {
+         echo "mirror images of $lv expected redundant, but are not:"
+         cat check.tmp.all
+         exit 1
+  }
+}
+
+mirror_images_on() {
+       vg=$1
+       lv=$2
+
+       shift 2
+
+       for i in `lvdevices $lv`; do
+               lv_on $vg $lv $1
+               shift
+       done
+}
+
+lv_on()
+{
+       lv="$1/$2"
+       lvdevices $lv | grep -F "$3" || {
+               echo "LV $lv expected on $3 but is not:" >&2
+               lvdevices $lv >&2
+               exit 1
+       }
+       test `lvdevices $lv | grep -vF "$3" | wc -l` -eq 0 || {
+               echo "LV $lv contains unexpected devices:" >&2
+               lvdevices $lv >&2
+               exit 1
+       }
+}
+
+mirror_log_on()
+{
+       vg="$1"
+       lv="$2"
+       where="$3"
+       if test "$where" = "core"; then
+               lvl -omirror_log "$vg/$lv" | not grep mlog
+       else
+               lv_on $vg "${lv}_mlog" "$where"
+       fi
+}
+
+lv_is_contiguous()
+{
+       test `lvl --segments $1 | wc -l` -eq 1 || {
+               echo "LV $1 expected to be contiguous, but is not:"
+               lvl --segments $1
+               exit 1
+       }
+}
+
+lv_is_clung()
+{
+       test `lvdevices $1 | sort | uniq | wc -l` -eq 1 || {
+               echo "LV $1 expected to be clung, but is not:"
+               lvdevices $! | sort | uniq
+               exit 1
+       }
+}
+
+mirror_images_contiguous()
+{
+       for i in `lvdevices $1/$2`; do
+               lv_is_contiguous $1/$i
+       done
+}
+
+mirror_images_clung()
+{
+       for i in `lvdevices $1/$2`; do
+               lv_is_clung $1/$i
+       done
+}
+
+mirror() {
+       mirror_nonredundant "$@"
+       mirror_images_redundant "$1" "$2"
+}
+
+mirror_nonredundant() {
+       lv="$1/$2"
+       lvs -oattr "$lv" | grep -q "^ *m.....$" || {
+               echo "$lv expected a mirror, but is not:"
+               lvs -a $lv
+               exit 1
+       }
+       if test -n "$3"; then mirror_log_on "$1" "$2" "$3"; fi
+}
+
+mirror_legs() {
+       lv="$1/$2"
+       expect="$3"
+       lvdevices "$lv"
+       real=`lvdevices "$lv" | wc -w`
+       test "$expect" = "$real"
+}
+
+mirror_no_temporaries()
+{
+       vg=$1
+       lv=$2
+       lvl -oname $vg | grep $lv | not grep "tmp" || {
+               echo "$lv has temporary mirror images unexpectedly:"
+               lvl $vg | grep $lv
+               exit 1
+       }
+}
+
+linear() {
+       lv="$1/$2"
+       lvl -ostripes "$lv" | grep -q "1" || {
+               echo "$lv expected linear, but is not:"
+               lvl "$lv" -o+devices
+               exit 1
+       }
+}
+
+active() {
+       lv="$1/$2"
+       lvl -oattr "$lv" 2> /dev/null | grep -q "^ *....a.$" || {
+               echo "$lv expected active, but lvs says it's not:"
+               lvl "$lv" -o+devices 2>/dev/null
+               exit 1
+       }
+       dmsetup table | egrep -q "$1-$2: *[^ ]+" || {
+               echo "$lv expected active, lvs thinks it is but there are no mappings!"
+               dmsetup table | grep $1-$2:
+               exit 1
+       }
+}
+
+inactive() {
+       lv="$1/$2"
+       lvl -oattr "$lv" 2> /dev/null | grep -q '^ *....[-isd].$' || {
+               echo "$lv expected inactive, but lvs says it's not:"
+               lvl "$lv" -o+devices 2>/dev/null
+               exit 1
+       }
+       dmsetup table | not egrep -q "$1-$2: *[^ ]+" || {
+               echo "$lv expected inactive, lvs thinks it is but there are mappings!"
+               dmsetup table | grep $1-$2:
+               exit 1
+       }
+}
+
+"$@"
diff --git a/test/harness.c b/test/harness.c
new file mode 100644 (file)
index 0000000..0162278
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+pid_t pid;
+int fds[2];
+
+#define MAX 1024
+
+struct stats {
+       int nfailed;
+       int nskipped;
+       int npassed;
+       int nwarned;
+       int status[MAX];
+};
+
+struct stats s;
+
+char *readbuf = NULL;
+int readbuf_sz = 0, readbuf_used = 0;
+
+int die = 0;
+int verbose = 0;
+
+#define PASSED 0
+#define SKIPPED 1
+#define FAILED 2
+#define WARNED 3
+
+void handler( int s ) {
+       signal( s, SIG_DFL );
+       kill( pid, s );
+       die = s;
+}
+
+void dump() {
+       fwrite(readbuf, 1, readbuf_used, stdout);
+}
+
+void clear() {
+       readbuf_used = 0;
+}
+
+void drain() {
+       int sz;
+       char buf[2048];
+       while (1) {
+               sz = read(fds[1], buf, 2048);
+               if (verbose)
+                       write(1, buf, sz);
+               if (sz <= 0)
+                       return;
+               if (readbuf_used + sz >= readbuf_sz) {
+                       readbuf_sz = readbuf_sz ? 2 * readbuf_sz : 4096;
+                       readbuf = realloc(readbuf, readbuf_sz);
+               }
+               if (!readbuf)
+                       exit(205);
+               memcpy(readbuf + readbuf_used, buf, sz);
+               readbuf_used += sz;
+               readbuf[readbuf_used] = 0;
+       }
+}
+
+void passed(int i, char *f) {
+       if (strstr(readbuf, "TEST WARNING")) {
+               ++s.nwarned;
+               s.status[i] = WARNED;
+               printf("warnings\n");
+       } else {
+               ++ s.npassed;
+               s.status[i] = PASSED;
+               printf("passed.\n");
+       }
+}
+
+void skipped(int i, char *f) {
+       ++ s.nskipped;
+       s.status[i] = SKIPPED;
+       printf("skipped.\n");
+}
+
+void failed(int i, char *f, int st) {
+       ++ s.nfailed;
+       s.status[i] = FAILED;
+       if(die == 2) {
+               printf("interrupted.\n");
+               return;
+       }
+       printf("FAILED.\n");
+       printf("-- FAILED %s ------------------------------------\n", f);
+       dump();
+       printf("-- FAILED %s (end) ------------------------------\n", f);
+}
+
+void run(int i, char *f) {
+       pid = fork();
+       if (pid < 0) {
+               perror("Fork failed.");
+               exit(201);
+       } else if (pid == 0) {
+               close(0);
+               dup2(fds[0], 1);
+               dup2(fds[0], 2);
+               execlp("bash", "bash", f, NULL);
+               perror("execlp");
+               fflush(stderr);
+               _exit(202);
+       } else {
+               char buf[128];
+               snprintf(buf, 128, "%s ...", f);
+               buf[127] = 0;
+               printf("Running %-40s ", buf);
+               fflush(stdout);
+               int st, w;
+               while ((w = waitpid(pid, &st, WNOHANG)) == 0) {
+                       drain();
+                       usleep(20000);
+               }
+               if (w != pid) {
+                       perror("waitpid");
+                       exit(206);
+               }
+               drain();
+               if (WIFEXITED(st)) {
+                       if (WEXITSTATUS(st) == 0) {
+                               passed(i, f);
+                       } else if (WEXITSTATUS(st) == 200) {
+                               skipped(i, f);
+                       } else {
+                               failed(i, f, st);
+                       }
+               } else {
+                       failed(i, f, st);
+               }
+               clear();
+       }
+}
+
+int main(int argc, char **argv) {
+       int i;
+
+       if (argc >= MAX) {
+               fprintf(stderr, "Sorry, my head exploded. Please increase MAX.\n");
+               exit(1);
+       }
+
+       s.nwarned = s.nfailed = s.npassed = s.nskipped = 0;
+
+       char *config = getenv("LVM_TEST_CONFIG"),
+               *config_debug,
+               *be_verbose = getenv("VERBOSE");
+       if (be_verbose && atoi(be_verbose))
+               verbose = 1; // XXX
+       config = config ? config : "";
+       asprintf(&config_debug, "%s\n%s\n", config, "log { verbose=4 }");
+
+       if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) {
+               perror("socketpair");
+               return 201;
+       }
+
+        if ( fcntl( fds[1], F_SETFL, O_NONBLOCK ) == -1 ) {
+               perror("fcntl on socket");
+               return 202;
+       }
+
+       /* set up signal handlers */
+        for (i = 0; i <= 32; ++i) {
+            if (i == SIGCHLD || i == SIGWINCH || i == SIGURG)
+                continue;
+            signal(i, handler);
+        }
+
+       /* run the tests */
+       for (i = 1; i < argc; ++ i) {
+               run(i, argv[i]);
+               if (die)
+                       break;
+       }
+
+       printf("\n## %d tests: %d OK, %d warnings, %d failures; %d skipped\n",
+              s.nwarned + s.npassed + s.nfailed + s.nskipped,
+              s.npassed, s.nwarned, s.nfailed, s.nskipped);
+
+       /* print out a summary */
+       if (s.nfailed || s.nskipped) {
+               for (i = 1; i < argc; ++ i) {
+                       switch (s.status[i]) {
+                       case FAILED:
+                               printf("FAILED: %s\n", argv[i]);
+                               break;
+                       case SKIPPED:
+                               printf("skipped: %s\n", argv[i]);
+                               break;
+                       }
+               }
+               printf("\n");
+               return s.nfailed > 0 || die;
+       }
+       return die;
+}
diff --git a/test/harness.sh b/test/harness.sh
new file mode 100644 (file)
index 0000000..b4bdd69
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+tests="$@"
+test -z "$tests" && tests=`echo t-*.sh`
+
+for t in $tests; do
+    printf "Running %-40s" "$t ..."
+    out=`bash ./$t 2>&1`
+    ret=$?
+    if test $ret = 0; then
+       echo " passed."
+    elif test $ret = 200; then
+        skipped="$skipped $t"
+       echo " skipped."
+    else
+       echo " FAILED!"
+       len=`echo $t | wc -c`
+       # fancy formatting...
+       printf -- "--- Output: $t -"
+       for i in `seq $(($len + 14)) 78`; do echo -n "-"; done; echo
+       printf "%s\n" "$out"
+       printf -- "--- End: $t ----"
+       for i in `seq $(($len + 14)) 78`; do echo -n "-"; done; echo
+       failed="$failed $t"
+    fi
+done
+
+if test -n "$failed"; then
+    echo "Tests skipped:"
+    for t in $skipped; do
+       printf "\t%s\n" $t
+    done
+    echo "TESTS FAILED:"
+    for t in $failed; do
+       printf "\t%s\n" $t
+    done
+    exit 1
+else
+    echo "All tests passed."
+fi
diff --git a/test/lvm-utils.sh b/test/lvm-utils.sh
new file mode 100644 (file)
index 0000000..fec4e2c
--- /dev/null
@@ -0,0 +1,153 @@
+# Put lvm-related utilities here.
+# This file is sourced from test-lib.sh.
+
+# Copyright (C) 2007, 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+export LVM_SUPPRESS_FD_WARNINGS=1
+
+ME=$(basename "$0")
+warn() { echo >&2 "$ME: $@"; }
+
+trim()
+{
+    trimmed=${1%% }
+    trimmed=${trimmed## }
+
+    echo "$trimmed"
+}
+
+compare_two_fields_()
+{
+    local cmd1=$1;
+    local obj1=$2;
+    local field1=$3;
+    local cmd2=$4;
+    local obj2=$5;
+    local field2=$6;
+    local val1;
+    local val2;
+
+    val1=$($cmd1 --noheadings -o $field1 $obj1)
+    val2=$($cmd2 --noheadings -o $field2 $obj2)
+if test "$verbose" = "t"
+then
+  echo "compare_two_fields_ $obj1($field1): $val1 $obj2($field2): $val2"
+fi
+  test "$val1" = "$val2"
+}
+
+compare_vg_field_()
+{
+    local vg1=$1;
+    local vg2=$2;
+    local field=$3;
+    local val1;
+    local val2;
+
+    val1=$(vgs --noheadings -o $field $vg1)
+    val2=$(vgs --noheadings -o $field $vg2)
+if test "$verbose" = "t"
+then
+  echo "compare_vg_field_ VG1: $val1 VG2: $val2"
+fi
+  test "$val1" = "$val2"
+}
+
+
+get_pv_field() {
+       local pv=$1
+       local field=$2
+       local value
+       pvs --noheading -o $field $pv | sed 's/^ *//'
+}
+
+get_vg_field() {
+       local vg=$1
+       local field=$2
+       local value
+       vgs --noheading -o $field $vg | sed 's/^ *//'
+}
+
+get_lv_field() {
+       local lv=$1
+       local field=$2
+       local value
+       lvs --noheading -o $field $lv | sed 's/^ *//'
+}
+
+check_vg_field_()
+{
+    local vg=$1;
+    local field=$2;
+    local expected=$3;
+    local actual;
+
+    actual=$(trim $(vgs --noheadings -o $field $vg))
+if test "$verbose" = "t"
+then
+  echo "check_vg_field_ VG=$vg, field=$field, actual=$actual, expected=$expected"
+fi
+  test "$actual" = "$expected"
+}
+
+check_pv_field_()
+{
+    local pv=$1;
+    local field=$2;
+    local expected=$3;
+    local pvs_args=$4; # optional
+    local actual;
+
+    actual=$(trim $(pvs --noheadings $pvs_args -o $field $pv))
+if test "$verbose" = "t"
+then
+  echo "check_pv_field_ PV=$pv, field=$field, actual=$actual, expected=$expected"
+fi
+    test "$actual" = "$expected"
+}
+
+check_lv_field_()
+{
+    local lv=$1;
+    local field=$2;
+    local expected=$3;
+    local actual;
+
+    actual=$(trim $(lvs --noheadings -o $field $lv))
+if test "$verbose" = "t"
+then
+  echo "check_lv_field_ LV=$lv, field=$field, actual=$actual, expected=$expected"
+fi
+  test "$actual" = "$expected"
+}
+
+vg_validate_pvlv_counts_()
+{
+       local local_vg=$1
+       local num_pvs=$2
+       local num_lvs=$3
+       local num_snaps=$4
+
+       lvs -a -o+devices $local_vg
+
+       check_vg_field_ $local_vg pv_count $num_pvs && \
+         check_vg_field_ $local_vg lv_count $num_lvs && \
+         check_vg_field_ $local_vg snap_count $num_snaps
+}
+
+dmsetup_has_dm_devdir_support_()
+{
+  # Detect support for the envvar.  If it's supported, the
+  # following command will fail with the expected diagnostic.
+  out=$(DM_DEV_DIR=j dmsetup version 2>&1)
+  test "$?:$out" = "1:Invalid DM_DEV_DIR envvar value." -o \
+       "$?:$out" = "1:Invalid DM_DEV_DIR environment variable value."
+}
diff --git a/test/mkdtemp b/test/mkdtemp
new file mode 100755 (executable)
index 0000000..89be8ac
--- /dev/null
@@ -0,0 +1,120 @@
+#!/bin/sh
+# Create a temporary directory, sort of like mktemp -d does.
+
+# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Written by Jim Meyering.
+
+# Usage: mkdtemp /tmp phoey.XXXXXXXXXX
+
+# First, try to use the mktemp program.
+# Failing that, we'll roll our own mktemp-like function:
+#  - try to get random bytes from /dev/urandom
+#  - failing that, generate output from a combination of quickly-varying
+#      sources and gzip.  Ignore non-varying gzip header, and extract
+#      "random" bits from there.
+#  - given those bits, map to file-name bytes using tr, and try to create
+#      the desired directory.
+#  - make only $MAX_TRIES attempts
+
+ME=$(basename "$0")
+die() { echo >&2 "$ME: $@"; exit 1; }
+
+MAX_TRIES=4
+
+rand_bytes()
+{
+  n=$1
+
+  chars=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
+
+  dev_rand=/dev/urandom
+  if test -r "$dev_rand"; then
+    # Note: 256-length($chars) == 194; 3 copies of $chars is 186 + 8 = 194.
+    head -c$n "$dev_rand" | tr -c $chars 01234567$chars$chars$chars
+    return
+  fi
+
+  cmds='date; date +%N; free; who -a; w; ps auxww; ps ef; netstat -n'
+  data=$( (eval "$cmds") 2>&1 | gzip )
+
+  n_plus_50=$(expr $n + 50)
+
+  # Ensure that $data has length at least 50+$n
+  while :; do
+    len=$(echo "$data"|wc -c)
+    test $n_plus_50 -le $len && break;
+    data=$( (echo "$data"; eval "$cmds") 2>&1 | gzip )
+  done
+
+  echo "$data" \
+    | dd bs=1 skip=50 count=$n 2>/dev/null \
+    | tr -c $chars 01234567$chars$chars$chars
+}
+
+mkdtemp()
+{
+  case $# in
+  2);;
+  *) die "Usage: $ME DIR TEMPLATE";;
+  esac
+
+  destdir=$1
+  template=$2
+
+  case $template in
+  *XXXX) ;;
+  *) die "invalid template: $template (must have a suffix of at least 4 X's)";;
+  esac
+
+  fail=0
+
+  # First, try to use mktemp.
+  d=$(env -u TMPDIR mktemp -d -t -p "$destdir" "$template" 2>/dev/null) \
+    || fail=1
+
+  # The resulting name must be in the specified directory.
+  case $d in "$destdir"*);; *) fail=1;; esac
+
+  # It must have created the directory.
+  test -d "$d" || fail=1
+
+  # It must have 0700 permissions.
+  perms=$(ls -dgo "$d" 2>/dev/null) || fail=1
+  case $perms in drwx------*) ;; *) fail=1;; esac
+
+  test $fail = 0 && {
+    echo "$d"
+    return
+  }
+
+  # If we reach this point, we'll have to create a directory manually.
+
+  # Get a copy of the template without its suffix of X's.
+  base_template=$(echo "$template"|sed 's/XX*$//')
+
+  # Calculate how many X's we've just removed.
+  nx=$(expr length "$template" - length "$base_template")
+
+  err=
+  i=1
+  while :; do
+    X=$(rand_bytes $nx)
+    candidate_dir="$destdir/$base_template$X"
+    err=$(mkdir -m 0700 "$candidate_dir" 2>&1) \
+      && { echo "$candidate_dir"; return; }
+    test $MAX_TRIES -le $i && break;
+    i=$(expr $i + 1)
+  done
+  die "$err"
+}
+
+mkdtemp "$@"
diff --git a/test/not.c b/test/not.c
new file mode 100644 (file)
index 0000000..534a356
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int finished(const char *cmd, int status) {
+       if (!strcmp(cmd, "not"))
+               return !status;
+       if (!strcmp(cmd, "should")) {
+               if (status)
+                       fprintf(stderr, "TEST WARNING: Ignoring command failure.\n");
+               return 0;
+       }
+       return 6;
+}
+
+int main(int args, char **argv) {
+       pid_t pid;
+       int status;
+       int FAILURE = 6;
+
+       if (args < 2) {
+               fprintf(stderr, "Need args\n");
+               return FAILURE;
+       }
+
+       pid = fork();
+       if (pid == -1) {
+               fprintf(stderr, "Could not fork\n");
+               return FAILURE;
+       } else if (pid == 0) {  /* child */
+               execvp(argv[1], &argv[1]);
+               /* should not be accessible */
+               return FAILURE;
+       } else {                /* parent */
+               waitpid(pid, &status, 0);
+               if (!WIFEXITED(status)) {
+                       if (WIFSIGNALED(status))
+                               fprintf(stderr,
+                                       "Process %d died of signal %d.\n",
+                                       pid, WTERMSIG(status));
+                       /* did not exit correctly */
+                       return FAILURE;
+               }
+
+               return finished(argv[0], WEXITSTATUS(status));
+       }
+       /* not accessible */
+       return FAILURE;
+}
diff --git a/test/t-000-basic.sh b/test/t-000-basic.sh
new file mode 100755 (executable)
index 0000000..ed76a6f
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+lvm version
+
+v=$abs_top_srcdir/lib/misc/lvm-version.h
+sed -n "/#define LVM_VERSION ./s///p" "$v" | sed "s/ .*//" > expected
+
+lvm pvmove --version|sed -n "1s/.*: *\([0-9][^ ]*\) .*/\1/p" > actual
+
+# ensure they are the same
+diff -u actual expected
+
+mknod $DM_DEV_DIR/null c 1 3 || \
+  error "Can't create nodes on filesystem"
+echo >$DM_DEV_DIR/null || \
+  error "Filesystem for tests does not allow using device nodes (check nodev)"
+
+# ensure we can create devices (uses dmsetup, etc)
+aux prepare_devs 5
+
diff --git a/test/t-activate-missing.sh b/test/t-activate-missing.sh
new file mode 100644 (file)
index 0000000..4242f21
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Test activation behaviour with devices missing.
+# - snapshots and their origins are only activated together; if one fails, both
+#   fail
+# - partial mirrors are not activated (but maybe they should? maybe we should
+#   instead lvconvert --repair them?)
+# - linear LVs with bits missing are not activated
+
+. ./test-utils.sh
+
+prepare_vg 4
+
+lvcreate -l1 -n linear1 $vg $dev1
+lvcreate -l1 -n linear2 $vg $dev2
+lvcreate -l2 -n linear12 $vg $dev1:4 $dev2:4
+
+lvcreate -l1 -n origin1 $vg $dev1
+lvcreate -s $vg/origin1 -l1 -n s_napshot2 $dev2
+
+lvcreate -l1 -m1 -n mirror12 --mirrorlog core $vg $dev1 $dev2
+lvcreate -l1 -m1 -n mirror123 $vg $dev1 $dev2 $dev3
+
+vgchange -a n $vg
+disable_dev $dev1
+not vgchange -a y $vg
+not vgck $vg
+
+check inactive $vg linear1
+check active $vg linear2
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg linear12
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev1
+disable_dev $dev2
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg linear1
+check inactive $vg linear2
+check inactive $vg linear12
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev2
+disable_dev $dev3
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check inactive $vg mirror123
+check active $vg mirror12
+
+vgchange -a n $vg
+enable_dev $dev3
+disable_dev $dev4
+vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check active $vg mirror12
+check active $vg mirror123
diff --git a/test/t-activate-partial.sh b/test/t-activate-partial.sh
new file mode 100644 (file)
index 0000000..b6bf9cb
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_vg 3
+
+lvcreate -m 1 -l 1 -n mirror $vg
+lvchange -a n $vg/mirror
+disable_dev $dev1
+
+not vgreduce --removemissing $vg
+not lvchange -v -a y $vg/mirror
+lvchange -v --partial -a y $vg/mirror
+not lvchange -v --refresh $vg/mirror
+lvchange -v --refresh --partial $vg/mirror
+
+# also check that vgchange works
+vgchange -a n --partial $vg
+vgchange -a y --partial $vg
+
+# check vgremove
+vgremove -f $vg
diff --git a/test/t-covercmd.sh b/test/t-covercmd.sh
new file mode 100755 (executable)
index 0000000..358a93e
--- /dev/null
@@ -0,0 +1,82 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# tests basic functionality of read-ahead and ra regressions
+#
+
+. ./test-utils.sh
+
+TEST_UUID="aaaaaa-aaaa-aaaa-aaaa-aaaa-aaaa-aaaaaa"
+
+get_lvs_()
+{
+  case $(lvs --units s --nosuffix --noheadings -o $1_read_ahead "$vg"/"$lv") in
+    *$2) true ;;
+    *) false ;;
+  esac
+}
+
+aux prepare_devs 5
+
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 0 $dev3
+pvcreate $dev4
+pvcreate --norestorefile -u $TEST_UUID --metadatacopies 0 $dev5
+vgcreate -c n $vg $devs
+lvcreate -n $lv -l 5 -i5 -I256 $vg
+
+# test *scan and *display tools
+pvscan
+vgscan
+lvscan
+lvmdiskscan
+vgdisplay --units k
+lvdisplay --units g
+for i in h b s k m g t p e H B S K M G T P E ; do
+    pvdisplay --units "$i" "$dev1"
+done
+
+# test vgexport vgimport tools
+vgchange -an $vg
+vgexport $vg
+vgimport $vg
+vgchange -ay $vg
+
+# "-persistent y --major 254 --minor 20"
+# "-persistent n"
+# test various lvm utils
+for i in dumpconfig formats segtypes; do
+    lvm "$i"
+done
+
+for i in pr "p rw" an ay "-monitor y" "-monitor n" \
+        -resync -refresh "-addtag MYTAG" "-deltag MYETAG"; do
+    lvchange -$i "$vg"/"$lv"
+done
+
+pvck "$dev1"
+vgck "$vg"
+lvrename "$vg" "$lv" "$lv-rename"
+vgcfgbackup -f "$(pwd)/backup.$$" "$vg"
+vgchange -an "$vg"
+vgcfgrestore  -f "$(pwd)/backup.$$" "$vg"
+pvremove -y -ff $dev5
+not vgcfgrestore  -f "$(pwd)/backup.$$" "$vg"
+pvcreate -u $TEST_UUID --restorefile  "$(pwd)/backup.$$" $dev5
+vgremove -f "$vg"
+pvresize --setphysicalvolumesize 10M "$dev1"
+
+# test various errors and obsoleted tools
+not lvmchange
+not lvrename "$vg"
+not lvrename "$vg-xxx"
+not lvrename "$vg"  "$vg"/"$lv-rename" "$vg"/"$lv"
diff --git a/test/t-dmeventd-restart.sh b/test/t-dmeventd-restart.sh
new file mode 100644 (file)
index 0000000..6368d77
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_vg 5
+prepare_dmeventd
+
+which mkfs.ext2 || exit 200
+
+lvcreate -m 3 --ig -L 1 -n 4way $vg
+lvchange --monitor y $vg/4way
+lvcreate -m 2 --ig -L 1 -n 3way $vg
+lvchange --monitor y $vg/3way
+
+dmeventd -R -f &
+LOCAL_DMEVENTD="$!"
+
+sleep 1 # wait a bit, so we talk to the new dmeventd later
+
+lvchange --monitor y --verbose $vg/3way 2>&1 | tee lvchange.out
+grep 'already monitored' lvchange.out
+lvchange --monitor y --verbose $vg/4way 2>&1 | tee lvchange.out
+grep 'already monitored' lvchange.out
diff --git a/test/t-fsadm.sh b/test/t-fsadm.sh
new file mode 100644 (file)
index 0000000..17d2090
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/bash
+# Copyright (C) 2008-2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description='Exercise fsadm filesystem resize'
+exit 200
+
+. ./test-utils.sh
+
+aux prepare_vg 1 100
+
+# set to "skip" to avoid testing given fs and test warning result
+# i.e. check_reiserfs=skip
+check_ext3=
+check_xfs=
+check_reiserfs=
+
+which mkfs.ext3 || check_ext3=${check_ext3:=mkfs.ext3}
+which fsck.ext3 || check_ext3=${check_ext3:=fsck.ext3}
+which mkfs.xfs || check_xfs=${check_xfs:=mkfs.xfs}
+which xfs_check || check_xfs=${check_xfs:=xfs_check}
+which mkfs.reiserfs || check_reiserfs=${check_reiserfs:=mkfs.reiserfs}
+which reiserfsck || check_reiserfs=${check_reiserfs:=reiserfsck}
+
+vg_lv="$vg/$lv1"
+dev_vg_lv="$DM_DEV_DIR/$vg_lv"
+mount_dir="$TESTDIR/mnt"
+
+test ! -d $mount_dir && mkdir $mount_dir
+
+cleanup_mounted_and_teardown()
+{
+       umount $mount_dir || true
+       teardown
+}
+
+fscheck_ext3()
+{
+       fsck.ext3 -p -F -f $dev_vg_lv
+}
+
+fscheck_xfs()
+{
+       xfs_check $dev_vg_lv
+}
+
+fscheck_reiserfs()
+{
+       reiserfsck --check -p -f $dev_vg_lv </dev/null
+}
+
+check_missing()
+{
+       eval local t=$\check_$1
+       test -z "$t" && return 0
+       test "$t" = skip && return 1
+       # trick for warning test
+       echo "TEST ""WARNING: fsadm skips $1 tests, $t tool is missing"
+       return 1
+}
+
+# Test for block sizes != 1024 (rhbz #480022)
+lvcreate -n $lv1 -L20M $vg
+trap 'aux cleanup_mounted_and_teardown' EXIT
+
+if check_missing ext3; then
+       mkfs.ext3 -b4096 -j $dev_vg_lv
+
+       fsadm --lvresize resize $vg_lv 30M
+       # Fails - not enough space for 4M fs
+       not fsadm --lvresize resize $dev_vg_lv 4M
+       lvresize -L+10M -r $vg_lv
+       lvreduce -L10M -r $vg_lv
+
+       fscheck_ext3
+       mount $dev_vg_lv $mount_dir
+       not fsadm --lvresize resize $vg_lv 9M
+       lvresize -L+20M -r -n $vg_lv
+       umount $mount_dir
+       fscheck_ext3
+
+       lvresize -f -L20M $vg_lv
+fi
+
+if check_missing xfs; then
+       mkfs.xfs -l internal,size=1000b -f $dev_vg_lv
+
+       fsadm --lvresize resize $vg_lv 30M
+       # Fails - not enough space for 4M fs
+       lvresize -L+10M -r $vg_lv
+       not lvreduce -L10M -r $vg_lv
+
+       fscheck_xfs
+       mount $dev_vg_lv $mount_dir
+       lvresize -L+10M -r -n $vg_lv
+       umount $mount_dir
+       fscheck_xfs
+
+       lvresize -f -L20M $vg_lv
+fi
+
+if check_missing reiserfs; then
+       mkfs.reiserfs -s 513 -f $dev_vg_lv
+
+       fsadm --lvresize resize $vg_lv 30M
+       lvresize -L+10M -r $vg_lv
+       fsadm --lvresize -y resize $vg_lv 10M
+
+       fscheck_reiserfs
+       mount $dev_vg_lv $mount_dir
+
+       not fsadm -y --lvresize resize $vg_lv 20M
+       umount $mount_dir
+
+       lvresize -f -L20M $vg_lv
+fi
diff --git a/test/t-inconsistent-metadata.sh b/test/t-inconsistent-metadata.sh
new file mode 100644 (file)
index 0000000..310dde5
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_vg 3
+
+lvcreate -m 1 -l 1 -n mirror $vg
+lvcreate -l 1 -n resized $vg
+lvchange -a n $vg/mirror
+
+backup_dev $devs
+
+init() {
+       restore_dev $devs
+       lvs -o lv_name,lv_size --units k $vg | tee lvs.out
+       grep resized lvs.out | not grep 8192
+       lvresize -L 8192K $vg/resized
+       restore_dev $dev1
+}
+
+check() {
+       lvs -o lv_name,lv_size --units k $vg | tee lvs.out
+       grep resized lvs.out | grep 8192
+}
+
+# vgscan fixes up metadata
+init
+vgscan 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgscan 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+# vgdisplay fixes
+init
+vgdisplay 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgdisplay 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+# lvs fixes up
+init
+lvs 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgdisplay 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+# vgs fixes up as well
+init
+vgs 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgs 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+echo Check auto-repair of failed vgextend - metadata written to original pv but not new pv
+vgremove -f $vg
+pvremove -ff $devs
+pvcreate $devs
+backup_dev $dev2
+vgcreate $vg $dev1
+vgextend $vg $dev2
+restore_dev $dev2
+should compare_two_fields_ vgs $vg vg_mda_count pvs $dev2 vg_mda_count
diff --git a/test/t-listings.sh b/test/t-listings.sh
new file mode 100644 (file)
index 0000000..9ea10b6
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# tests functionality of lvs, pvs, vgs, *display tools
+#
+
+. ./test-utils.sh
+
+get_lvs_()
+{
+  case $(lvs --units s --nosuffix --noheadings -o $1_read_ahead "$vg"/"$lv") in
+    *$2) true ;;
+    *) false ;;
+  esac
+}
+
+aux prepare_devs 5
+
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 0 $dev3
+pvcreate $dev4
+pvcreate --metadatacopies 0 $dev5
+
+#COMM bz195276 -- pvs doesn't show PVs until a VG is created
+pvs --noheadings|tee out
+test $(wc -l <out) -eq 5
+
+#COMM pvs with segment attributes works even for orphans
+pvs --noheadings -o  seg_all,pv_all,lv_all,vg_all | tee out
+test $(wc -l <out) -eq 5
+
+vgcreate -c n $vg $devs
+
+#COMM pvs and vgs report mda_count, mda_free (bz202886, bz247444)
+pvs -o +pv_mda_count,pv_mda_free $devs
+for I in $dev2 $dev3 $dev5; do
+       aux check_pv_field_ $I pv_mda_count 0
+       aux check_pv_field_ $I pv_mda_free 0
+done
+vgs -o +vg_mda_count,vg_mda_free $vg
+aux check_vg_field_ $vg vg_mda_count 2
+
+#COMM pvs doesn't display --metadatacopies 0 PVs as orphans (bz409061)
+pvdisplay $dev2|grep "VG Name.*$vg"
+test $(pvs -o vg_name --noheadings $dev2) = $vg
+
+#COMM lvs displays snapshots (bz171215)
+lvcreate -l4 -n $lv1 $vg
+lvcreate -l4 -s -n $lv2 $vg/$lv1
+lvs $vg --noheadings|tee out
+test $(wc -l <out) -eq 2
+lvs -a --noheadings|tee out
+# should lvs -a display cow && real devices? (it doesn't)
+test $(wc -l <out) -eq 2
+dmsetup ls|grep $PREFIX|grep -v "LVMTEST.*pv."
+lvremove -f $vg/$lv2
+
+#COMM lvs -a displays mirror legs and log
+lvcreate -l4  -m2 -n$lv3 $vg
+lvs $vg --noheadings|tee out
+test $(wc -l <out) -eq 2
+lvs -a --noheadings|tee out
+test $(wc -l <out) -eq 6
+dmsetup ls|grep $PREFIX|grep -v "LVMTEST.*pv."
+
+#COMM vgs with options from pvs still treats arguments as VGs (bz193543)
+vgs -o pv_name,vg_name $vg
+# would complain if not
+
+#COMM pvdisplay --maps feature (bz149814)
+pvdisplay $devs >out
+pvdisplay --maps $devs >out2
+not diff out out2
+
diff --git a/test/t-lock-blocking.sh b/test/t-lock-blocking.sh
new file mode 100644 (file)
index 0000000..166340c
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description='test some blocking / non-blocking multi-vg operations'
+
+. ./test-utils.sh
+
+aux prepare_devs 3
+test -n "$LOCAL_CLVMD" && exit 200
+pvcreate $dev1 $dev2
+vgcreate $vg $dev1 $dev2
+
+# if wait_for_locks set, vgremove should wait for orphan lock
+# flock process should have exited by the time first vgremove completes
+flock -w 5 $TESTDIR/var/lock/lvm/P_orphans -c "sleep 10" &
+flock_pid=`jobs -p`
+vgremove --config 'global { wait_for_locks = 1 }' $vg
+not vgremove --config 'global { wait_for_locks = 1 }' $vg
+not ps $flock_pid # finished
+
+# if wait_for_locks not set, vgremove should fail on non-blocking lock
+# we must wait for flock process at the end - vgremove won't wait
+vgcreate $vg $dev1 $dev2
+flock -w 5 $TESTDIR/var/lock/lvm/P_orphans -c "sleep 10" &
+flock_pid=`jobs -p`
+not vgremove --config 'global { wait_for_locks = 0 }' $vg
+ps $flock_pid # still running
+kill $flock_pid
diff --git a/test/t-lvchange-mirror.sh b/test/t-lvchange-mirror.sh
new file mode 100644 (file)
index 0000000..7c915be
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+aux prepare_vg 3
+
+# force resync 2-way active mirror
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+check mirror $vg $lv1 $dev3
+echo y | lvchange --resync $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# force resync 2-way inactive mirror
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvchange --resync $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
diff --git a/test/t-lvconvert-mirror-basic-0.sh b/test/t-lvconvert-mirror-basic-0.sh
new file mode 100644 (file)
index 0000000..eec6d5c
--- /dev/null
@@ -0,0 +1,12 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 0
diff --git a/test/t-lvconvert-mirror-basic-1.sh b/test/t-lvconvert-mirror-basic-1.sh
new file mode 100644 (file)
index 0000000..7019dbb
--- /dev/null
@@ -0,0 +1,12 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 1
diff --git a/test/t-lvconvert-mirror-basic-2.sh b/test/t-lvconvert-mirror-basic-2.sh
new file mode 100644 (file)
index 0000000..85d54c9
--- /dev/null
@@ -0,0 +1,12 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 2
diff --git a/test/t-lvconvert-mirror-basic-3.sh b/test/t-lvconvert-mirror-basic-3.sh
new file mode 100644 (file)
index 0000000..0c57580
--- /dev/null
@@ -0,0 +1,12 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 3
diff --git a/test/t-lvconvert-mirror-basic.sh b/test/t-lvconvert-mirror-basic.sh
new file mode 100644 (file)
index 0000000..880bd09
--- /dev/null
@@ -0,0 +1,148 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+log_name_to_count()
+{
+       if [ "$1" = "mirrored" ]; then
+               echo 2
+       elif [ "$1" = "disk" ]; then
+               echo 1
+       else
+               echo 0
+       fi
+}
+
+# FIXME: For test_[up|down]convert, I'd still like to be able
+# to specifiy devices - especially if I can do partial PV
+# specification for down-converts.  It may even be wise to
+# do one round through these tests without specifying the PVs
+# to use and one round where we do.
+
+# test_lvconvert
+#   start_mirror_count:  The '-m' argument to create with
+#   start_log_type: core|disk|mirrored
+#   final_mirror_count: The '-m' argument to convert to
+#   final_log_type: core|disk|mirrored
+#   active: Whether the LV should be active when the convert happens
+#
+# Exmaple: Convert 3-way disk-log mirror to
+#          2-way disk-log mirror while not active
+# -> test_lvconvert 2 disk 3 disk 0
+
+test_lvconvert()
+{
+       local start_count=$1
+       local start_count_p1=$(($start_count + 1))
+       local start_log_type=$2
+       local finish_count=$3
+       local finish_count_p1=$(($finish_count + 1))
+       local finish_log_type=$4
+       local dev_array=($dev1 $dev2 $dev3 $dev4 $dev5)
+       local start_log_count
+       local finish_log_count
+       local max_log_count
+       local alloc=""
+       local active=true
+       local i
+
+       if [ "$start_log_type" = "disk" ] &&
+               [ "$finish_log_type" = "mirrored" ]; then
+               echo "FIXME:  disk -> mirrored log conversion not yet supported by LVM"
+               return 0
+       fi
+
+       test "$5" = "active" && active=false
+       #test $finish_count -gt $start_count && up=true
+
+       # Do we have enough devices for the mirror images?
+       if [ $start_count_p1 -gt ${#dev_array[@]} ]; then
+               echo "Action requires too many devices"
+               return 1
+       fi
+
+       # Do we have enough devices for the mirror images?
+       if [ $finish_count_p1 -gt ${#dev_array[@]} ]; then
+               echo "Action requires too many devices"
+               return 1
+       fi
+
+       start_log_count=`log_name_to_count $start_log_type`
+       finish_log_count=`log_name_to_count $finish_log_type`
+       if [ $finish_log_count -gt $start_log_count ]; then
+               max_log_count=$finish_log_count
+       else
+               max_log_count=$start_log_count
+       fi
+
+       prepare_vg 5
+
+       if [ $start_count -gt 0 ]; then
+               # Are there extra devices for the log or do we overlap
+               if [ $(($start_count_p1 + $start_log_count)) -gt ${#dev_array[@]} ]; then
+                       alloc="--alloc anywhere"
+               fi
+
+               lvcreate -l2 -m $start_count --mirrorlog $start_log_type \
+                       -n $lv1 $vg $alloc
+               check mirror_legs $vg $lv1 $start_count_p1
+               # FIXME: check mirror log
+       else
+               lvcreate -l2 -n $lv1 $vg
+       fi
+
+       lvs -a -o name,copy_percent,devices $vg
+       if ! $active; then
+               lvchange -an $vg/$lv1
+       fi
+
+       # Are there extra devices for the log or do we overlap
+       if [ $(($finish_count_p1 + $finish_log_count)) -gt ${#dev_array[@]} ]; then
+               alloc="--alloc anywhere"
+       fi
+
+       echo y | lvconvert -m $finish_count --mirrorlog $finish_log_type \
+               $vg/$lv1 $alloc
+
+       if ! $active; then
+               lvchange -ay $vg/$lv1
+       fi
+
+       check mirror_no_temporaries $vg $lv1
+       if [ "$finish_count_p1" -eq 1 ]; then
+               check linear $vg $lv1
+       else
+               if test -n "$alloc"; then
+                       check mirror_nonredundant $vg $lv1
+               else
+                       check mirror $vg $lv1
+               fi
+               check mirror_legs $vg $lv1 $finish_count_p1
+       fi
+}
+
+aux prepare_vg 5
+
+test_many() {
+       i=$1
+       for j in $(seq 0 3); do
+               for k in core disk mirrored; do
+                       for l in core disk mirrored; do
+                               if test "$i" -eq "$j" && test "$k" = "$l"; then continue; fi
+                               : ----------------------------------------------------
+                               : "Testing mirror conversion -m$i/$k -> -m$j/$l"
+                               : ----------------------------------------------------
+                               test_lvconvert $i $k $j $l 0
+                               test_lvconvert $i $k $j $l 1
+                       done
+               done
+       done
+}
diff --git a/test/t-lvconvert-mirror.sh b/test/t-lvconvert-mirror.sh
new file mode 100644 (file)
index 0000000..c33196c
--- /dev/null
@@ -0,0 +1,242 @@
+#!/bin/sh
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+# convert from linear to 2-way mirror
+aux prepare_vg 5
+lvcreate -l2 -n $lv1 $vg $dev1
+lvconvert -i1 -m+1 $vg/$lv1 $dev2 $dev3:0-1
+check mirror $vg $lv1 $dev3
+
+# convert from 2-way mirror to linear
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvconvert -m-1 $vg/$lv1
+check linear $vg $lv1
+lvremove -ff $vg
+# and now try removing a specific leg (bz453643)
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvconvert -m0 $vg/$lv1 $dev2
+check lv_on $vg $lv1 $dev1
+lvremove -ff $vg
+
+# convert from disklog to corelog, active
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvconvert -f --mirrorlog core $vg/$lv1
+check mirror $vg $lv1 core
+lvremove -ff $vg
+
+# convert from corelog to disklog, active
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+lvconvert --mirrorlog disk $vg/$lv1 $dev3:0-1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# bz192865: lvconvert log of an inactive mirror lv
+# convert from disklog to corelog, inactive
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+echo y | lvconvert -f --mirrorlog core $vg/$lv1
+check mirror $vg $lv1 core
+lvremove -ff $vg
+
+# convert from corelog to disklog, inactive
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+lvchange -an $vg/$lv1
+lvconvert --mirrorlog disk $vg/$lv1 $dev3:0-1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# convert linear to 2-way mirror with 1 PV
+aux prepare_vg 5
+lvcreate -l2 -n $lv1 $vg $dev1
+not lvconvert -m+1 --mirrorlog core $vg/$lv1 $dev1
+lvremove -ff $vg
+
+# Start w/ 3-way mirror
+# Test pulling primary image before mirror in-sync (should fail)
+# Test pulling primary image after mirror in-sync (should work)
+# Test that the correct devices remain in the mirror
+lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev4 $dev3:0
+# FIXME:
+#  This is somewhat timing dependent - sync /could/ finish before
+#  we get a chance to have this command fail
+should not lvconvert -m-1 $vg/$lv1 $dev1
+
+lvconvert $vg/$lv1 # wait
+lvconvert -m2 $vg/$lv1 $dev1 $dev2 $dev4 $dev3:0 # If the above "should" failed...
+
+lvconvert -m-1 $vg/$lv1 $dev1
+check mirror_images_on $lv1 $dev2 $dev4
+lvconvert -m-1 $vg/$lv1 $dev2
+check linear $vg $lv1
+check lv_on $vg $lv1 $dev4
+
+# No parallel lvconverts on a single LV please
+
+aux prepare_vg 5
+lvcreate -l5 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+check mirror $vg $lv1
+check mirror_legs $vg $lv1 2
+lvconvert -m+1 -b $vg/$lv1 $dev4
+
+# Next convert should fail b/c we can't have 2 at once
+should not lvconvert -m+1 $vg/$lv1 $dev5
+lvconvert $vg/$lv1 # wait
+lvconvert -m2 $vg/$lv1 # In case the above "should" actually failed
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# add 1 mirror to core log mirror, but
+#  implicitly keep log as 'core'
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+lvconvert -m +1 -i1 $vg/$lv1
+
+check mirror $vg $lv1 core
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# remove 1 mirror from corelog'ed mirror; should retain 'core' log type
+aux prepare_vg 5
+lvcreate -l2 -m2 --corelog -n $lv1 $vg
+lvconvert -m -1 -i1 $vg/$lv1
+
+check mirror $vg $lv1 core
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# add 1 mirror then add 1 more mirror during conversion
+# FIXME this has been explicitly forbidden?
+#aux prepare_vg 5
+#lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+#lvconvert -m+1 -b $vg/$lv1 $dev4
+#lvconvert -m+1 $vg/$lv1 $dev5
+#
+#check mirror $vg $lv1 $dev3
+#check mirror_no_temporaries $vg $lv1
+#check mirror_legs $vg $lv1 4
+
+# Linear to mirror with mirrored log using --alloc anywhere
+aux prepare_vg 5
+lvcreate -l2 -n $lv1 $vg $dev1
+lvconvert -m +1 --mirrorlog mirrored $vg/$lv1 $dev1 $dev2 --alloc anywhere
+should check mirror $vg $lv1
+
+# convert inactive mirror and start polling
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvchange -an $vg/$lv1
+lvconvert -m+1 $vg/$lv1 $dev4
+lvchange -ay $vg/$lv1
+lvconvert $vg/$lv1 # wait
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+
+# ---------------------------------------------------------------------
+# removal during conversion
+
+# "remove newly added mirror"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvconvert -m+1 -b $vg/$lv1 $dev4
+lvconvert -m-1 $vg/$lv1 $dev4
+lvconvert $vg/$lv1 # wait
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# "remove one of newly added mirrors"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvconvert -m+2 -b $vg/$lv1 $dev4 $dev5
+lvconvert -m-1 $vg/$lv1 $dev4
+lvconvert $vg/$lv1 # wait
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# "remove from original mirror (the original is still mirror)"
+aux prepare_vg 5
+lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev5 $dev3:0
+lvconvert -m+1 -b $vg/$lv1 $dev4 
+lvconvert -m-1 $vg/$lv1 $dev2 
+lvconvert $vg/$lv1 
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# "remove from original mirror (the original becomes linear)"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvconvert -m+1 -b $vg/$lv1 $dev4 
+lvconvert -m-1 $vg/$lv1 $dev2 
+lvconvert $vg/$lv1 
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# ---------------------------------------------------------------------
+
+# "rhbz440405: lvconvert -m0 incorrectly fails if all PEs allocated"
+aux prepare_vg 5
+lvcreate -l`pvs --noheadings -ope_count $dev1` -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+while [ `lvs --noheadings -o copy_percent $vg/$lv1` != "100.00" ]; do sleep 1; done
+lvconvert -m0 $vg/$lv1 $dev1
+check linear $vg $lv1
+
+# "rhbz264241: lvm mirror doesn't lose it's "M" --nosync attribute after being down and the up converted"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n$lv1 --nosync $vg 
+lvconvert -m0 $vg/$lv1
+lvconvert -m1 $vg/$lv1
+lvs --noheadings -o attr $vg/$lv1 | grep '^ *m'
+
+# lvconvert from linear (on multiple PVs) to mirror
+aux prepare_vg 5
+lvcreate -l 8 -n $lv1 $vg $dev1:0-3 $dev2:0-3
+lvconvert -m1 $vg/$lv1
+
+should check mirror $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# BZ 463272: disk log mirror convert option is lost if downconvert option is also given
+aux prepare_vg 5
+lvcreate -l1 -m2 --corelog -n $lv1 $vg $dev1 $dev2 $dev3
+while [ `lvs --noheadings -o copy_percent $vg/$lv1` != "100.00" ]; do sleep 1; done
+lvconvert -m1 --mirrorlog disk $vg/$lv1
+check mirror $vg $lv1
+not check mirror $vg $lv1 core
+
+# ---
+# add mirror and disk log
+
+# "add 1 mirror and disk log" 
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+
+# FIXME on next line, specifying $dev3:0 $dev4 (i.e log device first) fails (!)
+lvconvert -m+1 --mirrorlog disk -i1 $vg/$lv1 $dev4 $dev3:0
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
diff --git a/test/t-lvconvert-repair-dmeventd.sh b/test/t-lvconvert-repair-dmeventd.sh
new file mode 100644 (file)
index 0000000..f80a410
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_vg 5
+prepare_dmeventd
+
+which mkfs.ext2 || exit 200
+
+lvcreate -m 3 --ig -L 1 -n 4way $vg
+lvchange --monitor y $vg/4way
+disable_dev $dev2 $dev4
+mkfs.ext2 $DM_DEV_DIR/$vg/4way
+sleep 10 # FIXME: need a "poll" utility, akin to "check"
+enable_dev $dev2 $dev4
+check mirror $vg 4way
+check mirror_legs $vg 4way 2
diff --git a/test/t-lvconvert-repair-policy.sh b/test/t-lvconvert-repair-policy.sh
new file mode 100644 (file)
index 0000000..1f31215
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_vg 4
+
+# Clean-up and create a 2-way mirror, where the the
+# leg devices are always on $dev[12] and the log
+# is always on $dev3.  ($dev4 behaves as a spare)
+cleanup() {
+       vgreduce --removemissing $vg
+       for d in "$@"; do enable_dev $d; done
+       for d in "$@"; do vgextend $vg $d; done
+       lvremove -ff $vg/mirror
+       lvcreate -m 1 --ig -l 2 -n mirror $vg $dev1 $dev2 $dev3:0
+}
+
+repair() {
+       lvconvert --repair --use-policies --config "$1" $vg/mirror
+}
+
+lvcreate -m 1 -L 1 -n mirror $vg
+lvchange -a n $vg/mirror
+
+# Fail a leg of a mirror.
+aux disable_dev $dev1
+lvchange --partial -a y $vg/mirror
+repair 'activation { mirror_image_fault_policy = "remove" }'
+check linear $vg mirror
+aux cleanup $dev1
+
+# Fail a leg of a mirror.
+# Expected result: Mirror (leg replaced)
+aux disable_dev $dev1
+repair 'activation { mirror_image_fault_policy = "replace" }'
+check mirror $vg mirror
+lvs | grep mirror_mlog
+aux cleanup $dev1
+
+# Fail a leg of a mirror (use old name for policy specification)
+# Expected result: Mirror (leg replaced)
+aux disable_dev $dev1
+repair 'activation { mirror_device_fault_policy = "replace" }'
+check mirror $vg mirror
+lvs | grep mirror_mlog
+aux cleanup $dev1
+
+# Fail a leg of a mirror w/ no available spare
+# Expected result: 2-way with corelog
+aux disable_dev $dev2 $dev4
+repair 'activation { mirror_image_fault_policy = "replace" }'
+check mirror $vg mirror
+lvs | not grep mirror_mlog
+aux cleanup $dev2 $dev4
+
+# Fail the log device of a mirror w/ no available spare
+# Expected result: mirror w/ corelog
+aux disable_dev $dev3 $dev4
+repair 'activation { mirror_image_fault_policy = "replace" }' $vg/mirror
+check mirror $vg mirror
+lvs | not grep mirror_mlog
+aux cleanup $dev3 $dev4
+
+# Fail the log device with a remove policy
+# Expected result: mirror w/ corelog
+lvchange -a y $vg/mirror
+aux disable_dev $dev3 $dev4
+repair 'activation { mirror_log_fault_policy = "remove" }'
+check mirror $vg mirror core
+lvs | not grep mirror_mlog
+cleanup $dev3 $dev4
diff --git a/test/t-lvconvert-repair-replace.sh b/test/t-lvconvert-repair-replace.sh
new file mode 100644 (file)
index 0000000..7315a0e
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_vg 6
+
+# multiple failures, full replace
+lvcreate --mirrorlog disk -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4:0-1
+disable_dev $dev1 $dev2
+echo y | lvconvert --repair $vg/3way 2>&1 | tee 3way.out
+lvs -a -o +devices | not grep unknown
+not grep "WARNING: Failed" 3way.out
+vgreduce --removemissing $vg
+check mirror $vg 3way
+enable_dev $dev1 $dev2
+
+vgremove -ff $vg; vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4 $dev5
+
+# multiple failures, partial replace
+lvcreate --mirrorlog disk -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4
+disable_dev $dev1 $dev2
+echo y | lvconvert --repair $vg/3way 2>&1 | tee 3way.out
+grep "WARNING: Failed" 3way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+check mirror $vg 3way
+enable_dev $dev1 $dev2
+lvchange -a n $vg/3way
+
+vgremove -ff $vg; vgcreate -c n $vg $dev1 $dev2 $dev3
+
+lvcreate --mirrorlog disk -m 1 --ig -L 1 -n 2way $vg $dev1 $dev2 $dev3
+disable_dev $dev1
+echo y | lvconvert --repair $vg/2way 2>&1 | tee 2way.out
+grep "WARNING: Failed" 2way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+check mirror $vg 2way
+enable_dev $dev1 $dev2
+lvchange -a n $vg/2way
+
+vgremove -ff $vg; vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4
+
+# Test repair of inactive mirror with log failure
+#  Replacement should fail, but covert should succeed (switch to corelog)
+lvcreate -m 2 --ig -l 2 -n mirror2 $vg $dev1 $dev2 $dev3 $dev4:0
+vgchange -a n $vg
+pvremove -ff -y $dev4
+echo 'y' | lvconvert -y --repair $vg/mirror2
+check mirror $vg mirror2
+vgs
+
diff --git a/test/t-lvconvert-repair-transient.sh b/test/t-lvconvert-repair-transient.sh
new file mode 100644 (file)
index 0000000..b4e1a06
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_vg 5
+
+exit 200 # this breaks upstream .33 and RHEL6 kernel :(
+
+# fail multiple devices
+
+lvcreate -m 3 --ig -L 1 -n 4way $vg
+disable_dev $dev2 $dev4
+mkfs.ext3 $DM_DEV_DIR/$vg/4way
+enable_dev $dev2 $dev4
+echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+check mirror $vg 4way
+lvchange -a n $vg/4way
diff --git a/test/t-lvconvert-repair.sh b/test/t-lvconvert-repair.sh
new file mode 100644 (file)
index 0000000..bce8b53
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/bash
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+
+# fail multiple devices
+
+aux prepare_vg 5
+lvcreate -m 3 --ig -L 1 -n 4way $vg $dev1 $dev2 $dev3 $dev4 $dev5:0
+disable_dev $dev2 $dev4
+echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+enable_dev $dev2 $dev4
+check mirror $vg 4way $dev5
+
+aux prepare_vg 5
+lvcreate -m 2 --ig -L 1 -n 3way $vg
+disable_dev $dev1 $dev2
+echo n | lvconvert --repair $vg/3way
+check linear $vg 3way
+lvs -a -o +devices | not grep unknown
+lvs -a -o +devices | not grep mlog
+dmsetup ls | grep $PREFIX | not grep mlog
+vgreduce --removemissing $vg
+enable_dev $dev1 $dev2
+check linear $vg 3way
+
+# fail just log and get it removed
+
+aux prepare_vg 5
+lvcreate -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4:0
+disable_dev $dev4
+echo n | lvconvert --repair $vg/3way
+check mirror $vg 3way core
+lvs -a -o +devices | not grep unknown
+lvs -a -o +devices | not grep mlog
+dmsetup ls | grep $PREFIX | not grep mlog
+vgreduce --removemissing $vg
+enable_dev $dev4
+
+aux prepare_vg 5
+lvcreate -m 1 --ig -L 1 -n 2way $vg $dev1 $dev2 $dev3:0
+disable_dev $dev3
+echo n | lvconvert --repair $vg/2way
+check mirror $vg 2way core
+lvs -a -o +devices | not grep unknown
+lvs -a -o +devices | not grep mlog
+vgreduce --removemissing $vg
+enable_dev $dev3
+
+# fail single devices
+
+aux prepare_vg 5
+vgreduce $vg $dev4
+
+lvcreate -m 1 --ig -L 1 -n mirror $vg
+lvchange -a n $vg/mirror
+vgextend $vg $dev4
+disable_dev $dev1
+lvchange --partial -a y $vg/mirror
+
+not vgreduce -v --removemissing $vg
+lvconvert -y --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev1
+vgextend $vg $dev1
+disable_dev $dev2
+lvconvert -y --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev2
+vgextend $vg $dev2
+disable_dev $dev3
+lvconvert -y --repair $vg/mirror
+vgreduce --removemissing $vg
+enable_dev $dev3
+vgextend $vg $dev3
+lvremove -ff $vg
diff --git a/test/t-lvconvert-twostep.sh b/test/t-lvconvert-twostep.sh
new file mode 100644 (file)
index 0000000..c499fdd
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_vg 4
+lvcreate -m 1 --mirrorlog disk --ig -L 1 -n mirror $vg
+not lvconvert -m 2 --mirrorlog core $vg/mirror $dev3 2>&1 | tee errs
+grep "two steps" errs
+lvconvert -m 2 $vg/mirror $dev3
+lvconvert --mirrorlog core $vg/mirror
+not lvconvert -m 1 --mirrorlog disk $vg/mirror $dev3 2>&1 | tee errs
+grep "two steps" errs
diff --git a/test/t-lvcreate-mirror.sh b/test/t-lvcreate-mirror.sh
new file mode 100644 (file)
index 0000000..ae3fceb
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+aux prepare_vg 5 80
+
+# 2-way mirror with corelog, 2 PVs
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+check mirror_images_redundant $vg $lv1
+lvremove -ff $vg
+
+# 2-way mirror with disklog, 3 PVs
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+check mirror_images_redundant $vg $lv1
+check mirror_log_on $vg $lv1 $dev3
+lvremove -ff $vg
+
+# 3-way mirror with disklog, 4 PVs
+lvcreate -l2 -m2 --mirrorlog disk -n $lv1 $vg $dev1 $dev2 $dev4 $dev3:0-1
+check mirror_images_redundant $vg $lv1
+check mirror_log_on $vg $lv1 $dev3
+lvremove -ff $vg
+
+# lvcreate --nosync is in 100% sync after creation (bz429342)
+lvcreate -l2 -m1 --nosync -n $lv1 $vg $dev1 $dev2 $dev3:0-1 2>out
+grep "New mirror won't be synchronised." out
+lvs -o copy_percent --noheadings $vg/$lv1 | grep 100.00
+lvremove -ff $vg
+
+# creating 2-way mirror with disklog from 2 PVs fails
+not lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2
diff --git a/test/t-lvcreate-operation.sh b/test/t-lvcreate-operation.sh
new file mode 100644 (file)
index 0000000..8a31759
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Exercise some lvcreate diagnostics'
+
+. ./test-utils.sh
+
+cleanup_lvs() {
+       lvremove -ff $vg
+       if dmsetup table|grep $vg; then
+               echo "ERROR: lvremove did leave some some mappings in DM behind!"
+               return 1
+       fi
+}
+
+prepare_pvs 2
+aux pvcreate --metadatacopies 0 $dev1
+aux vgcreate -c n $vg $devs
+
+# ---
+# Create snapshots of LVs on --metadatacopies 0 PV (bz450651)
+lvcreate -n$lv1 -l4 $vg $dev1
+lvcreate -n$lv2 -l4 -s $vg/$lv1
+cleanup_lvs
+
+# ---
+# Create mirror on two devices with mirrored log using --alloc anywhere
+lvcreate -m 1 -l4 -n $lv1 --mirrorlog mirrored $vg --alloc anywhere $dev1 $dev2
+cleanup_lvs
+
+# --
+# Create mirror on one dev with mirrored log using --alloc anywhere, should fail
+not lvcreate -m 1 -l4 -n $lv1 --mirrorlog mirrored $vg --alloc anywhere $dev1
+cleanup_lvs
diff --git a/test/t-lvcreate-pvtags.sh b/test/t-lvcreate-pvtags.sh
new file mode 100755 (executable)
index 0000000..b9a4380
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_pvs 3
+# not required, just testing
+aux pvcreate --metadatacopies 0 $dev1
+
+vgcreate -c n $vg $devs
+pvchange --addtag fast $devs
+
+# 3 stripes with 3 PVs (selected by tag, @fast) is fine
+lvcreate -l3 -i3 $vg @fast
+
+# too many stripes(4) for 3 PVs
+not lvcreate -l4 -i4 $vg @fast
+
+# 2 stripes is too many with just one PV
+not lvcreate -l2 -i2 $vg $DM_DEV_DIR/mapper/pv1
+
+# lvcreate mirror
+lvcreate -l1 -m1 $vg @fast
+
+# lvcreate mirror w/corelog
+lvcreate -l1 -m2 --corelog $vg @fast
+
+# lvcreate mirror w/no free PVs
+not lvcreate -l1 -m2 $vg @fast
+
+# lvcreate mirror (corelog, w/no free PVs)
+not lvcreate -l1 -m3 --corelog $vg @fast
+
+# lvcreate mirror with a single PV arg
+not lvcreate -l1 -m1 --corelog $vg $dev1
diff --git a/test/t-lvcreate-small-snap.sh b/test/t-lvcreate-small-snap.sh
new file mode 100644 (file)
index 0000000..789c099
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_pvs 3
+
+vgcreate -c n -s 1k $vg $devs
+
+lvcreate -n one -l 10 $vg
+lvcreate -s -l 8 -n snapA $vg/one
+lvcreate -s -c 4k -l 8 -n snapX1 $vg/one
+lvcreate -s -c 8k -l 16 -n snapX2 $vg/one
+
+# Check that snapshots that are too small are caught with correct error.
+not lvcreate -s -c 8k -l 8 -n snapX3 $vg/one 2>&1 | tee lvcreate.out
+not grep "suspend origin one" lvcreate.out
+grep "Unable to create a snapshot" lvcreate.out
+
+not lvcreate -s -l 4 -n snapB $vg/one 2>&1 | tee lvcreate.out
+not grep "suspend origin one" lvcreate.out
+grep "Unable to create a snapshot" lvcreate.out
diff --git a/test/t-lvcreate-usage.sh b/test/t-lvcreate-usage.sh
new file mode 100755 (executable)
index 0000000..0bb06ab
--- /dev/null
@@ -0,0 +1,141 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Exercise some lvcreate diagnostics'
+
+. ./test-utils.sh
+
+aux prepare_pvs 4
+aux pvcreate --metadatacopies 0 $dev1
+vgcreate -cn $vg $devs
+
+# "lvcreate rejects repeated invocation (run 2 times) (bz178216)" 
+lvcreate -n $lv -l 4 $vg 
+not lvcreate -n $lv -l 4 $vg
+lvremove -ff $vg/$lv
+# try to remove it again - should fail (but not segfault)
+not lvremove -ff $vg/$lv
+
+# "lvcreate rejects a negative stripe_size"
+not lvcreate -L 64m -n $lv -i2 --stripesize -4 $vg 2>err;
+grep "^  Negative stripesize is invalid\$" err
+
+# 'lvcreate rejects a too-large stripesize'
+not lvcreate -L 64m -n $lv -i2 --stripesize 4294967291 $vg 2>err
+grep "^  Stripe size cannot be larger than" err
+
+# 'lvcreate w/single stripe succeeds with diagnostics to stdout' 
+lvcreate -L 64m -n $lv -i1 --stripesize 4 $vg 2> err | tee out
+grep "^  Ignoring stripesize argument with single stripe\$" out 
+lvdisplay $vg 
+lvremove -ff $vg
+
+# 'lvcreate w/default (64KB) stripe size succeeds with diagnostics to stdout'
+lvcreate -L 64m -n $lv -i2 $vg > out
+grep "^  Using default stripesize" out 
+lvdisplay $vg 
+check_lv_field_ $vg/$lv stripesize "64.00k"
+lvremove -ff $vg
+
+# 'lvcreate rejects an invalid number of stripes' 
+not lvcreate -L 64m -n $lv -i129 $vg 2>err
+grep "^  Number of stripes (129) must be between 1 and 128\$" err
+
+# The case on lvdisplay output is to verify that the LV was not created.
+# 'lvcreate rejects an invalid stripe size'
+not lvcreate -L 64m -n $lv -i2 --stripesize 3 $vg 2>err
+grep "^  Invalid stripe size" err
+case $(lvdisplay $vg) in "") true ;; *) false ;; esac
+
+# Setting max_lv works. (bz490298)
+lvremove -ff $vg
+vgchange -l 3 $vg
+lvcreate -l1 -n $lv1 $vg
+lvcreate -l1 -s -n $lv2 $vg/$lv1
+lvcreate -l1 -n $lv3 $vg
+not lvcreate -l1 -n $lv4 $vg
+
+lvremove -ff $vg/$lv3
+lvcreate -l1 -s -n $lv3 $vg/$lv1
+not lvcreate -l1 -n $lv4 $vg
+not lvcreate -l1 -m1 -n $lv4 $vg
+
+lvremove -ff $vg/$lv3
+lvcreate -l1 -m1 -n $lv3 $vg
+lvs
+vgs -o +max_lv
+not lvcreate -l1 -n $lv4 $vg
+not lvcreate -l1 -m1 -n $lv4 $vg
+
+lvconvert -m0 $vg/$lv3
+lvconvert -m2 -i 1 $vg/$lv3
+lvconvert -m1 $vg/$lv3
+
+not vgchange -l 2
+vgchange -l 4
+vgs $vg
+
+lvremove -ff $vg
+vgchange -l 0 $vg
+
+# lvcreate rejects invalid chunksize, accepts between 4K and 512K
+# validate origin_size
+vgremove -ff $vg
+vgcreate -cn $vg $devs
+lvcreate -L 32m -n $lv1 $vg
+not lvcreate -L 8m -n $lv2 -s --chunksize 3k $vg/$lv1
+not lvcreate -L 8m -n $lv2 -s --chunksize 1024k $vg/$lv1
+lvcreate -L 8m -n $lv2 -s --chunksize 4k $vg/$lv1
+check_lv_field_ $vg/$lv2 chunk_size 4.00k
+check_lv_field_ $vg/$lv2 origin_size 32.00m
+lvcreate -L 8m -n $lv3 -s --chunksize 512k $vg/$lv1
+check_lv_field_ $vg/$lv3 chunk_size 512.00k
+check_lv_field_ $vg/$lv3 origin_size 32.00m
+lvremove -ff $vg
+vgchange -l 0 $vg
+
+# regionsize must be
+# - nonzero (bz186013)
+# - a power of 2 and a multiple of page size
+# - <= size of LV
+not lvcreate -L 32m -n $lv -R0 $vg 2>err
+grep "Non-zero region size must be supplied." err
+not lvcreate -L 32m -n $lv -R 11k $vg
+not lvcreate -L 32m -n $lv -R 1k $vg
+lvcreate -L 32m -n $lv --regionsize 128m  -m 1 $vg
+check_lv_field_ $vg/$lv regionsize "32.00m"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --regionsize 4m -m 1 $vg
+check_lv_field_ $vg/$lv regionsize "4.00m"
+lvremove -ff $vg
+
+# snapshot with virtual origin works
+lvcreate -s --virtualoriginsize 64m -L 32m -n $lv1 $vg
+lvrename $vg/$lv1 $vg/$lv2
+lvcreate -s --virtualoriginsize 64m -L 32m -n $lv1 $vg
+lvchange -a n $vg/$lv1
+lvremove $vg/$lv1
+lvremove -ff $vg
+
+# readahead default (auto), none, #, auto
+lvcreate -L 32m -n $lv $vg
+check_lv_field_ $vg/$lv lv_read_ahead "auto"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --readahead none $vg
+check_lv_field_ $vg/$lv lv_read_ahead "0"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --readahead 8k $vg
+check_lv_field_ $vg/$lv lv_read_ahead "8.00k"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --readahead auto $vg
+check_lv_field_ $vg/$lv lv_read_ahead "auto"
+lvremove -ff $vg
+
diff --git a/test/t-lvextend-percent-extents.sh b/test/t-lvextend-percent-extents.sh
new file mode 100755 (executable)
index 0000000..d020dec
--- /dev/null
@@ -0,0 +1,101 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Check extents percentage arguments'
+
+. ./test-utils.sh
+
+aux prepare_vg 2 128
+
+lvcreate -L 64m -n $lv $vg
+
+# 'lvextend rejects both size and extents without PVs'
+not lvextend -l 10 -L 64m $vg/$lv 2>err
+grep "^  Please specify either size or extents but not both.\$" err
+
+# 'lvextend rejects both size and extents with PVs'
+not lvextend -l 10 -L 64m $vg/$lv $dev1 2>err
+grep "^  Please specify either size or extents but not both.\$" err
+
+# 'lvextend accepts no size or extents but one PV - bz154691'
+lvextend $vg/$lv $dev1 >out
+grep "^  Logical volume $lv successfully resized\$" out
+check_pv_field_ $dev1 pv_free "0"
+
+lvremove -f $vg/$lv 
+
+# 'lvextend computes necessary free space correctly - bz213552'
+vgsize=$(vgs -o vg_extent_count --noheadings)
+lvcreate -l $vgsize  -n $lv $vg
+lvreduce -f -l $(( $vgsize / 2 )) $vg/$lv
+lvextend -l $vgsize $vg/$lv
+
+# 'Reset LV to original size' 
+lvremove -f $vg/$lv 
+lvcreate -L 64m -n $lv $vg
+
+# 'lvextend accepts no size but extents 100%PVS and two PVs - bz154691'
+lvextend -l +100%PVS $vg/$lv $dev1 $dev2 >out
+grep "^  Logical volume $lv successfully resized\$" out 
+check_pv_field_ $dev1 pv_free "0" 
+check_pv_field_ $dev2 pv_free "0"
+
+# Exercise the range overlap code.  Allocate every 2 extents.
+#
+#      Physical Extents
+#          1         2
+#012345678901234567890123
+#
+#aaXXaaXXaaXXaaXXaaXXaaXX - (a)llocated
+#rrrXXXrrrXXXrrrXXXrrrXXX - (r)ange on cmdline
+#ooXXXXXXoXXXooXXXXXXoXXX - (o)verlap of range and allocated
+#
+# Key: a - allocated
+#      F - free
+#      r - part of a range on the cmdline
+#      N - not on cmdline
+#
+# Create the LV with 12 extents, allocated every other 2 extents.
+# Then extend it, with a range of PVs on the cmdline of every other 3 extents.
+# Total number of extents should be 12 + overlap = 12 + 6 = 18.
+# Thus, total size for the LV should be 18 * 4M = 72M
+#
+# 'Reset LV to 12 extents, allocate every other 2 extents' 
+create_pvs=`for i in $(seq 0 4 20); do echo -n "\$dev1:$i-$(($i + 1)) "; done` 
+lvremove -f $vg/$lv
+lvcreate -l 12 -n $lv $vg $create_pvs
+check_lv_field_ $vg/$lv lv_size "48.00m"
+
+# 'lvextend with partially allocated PVs and extents 100%PVS with PE ranges' 
+extend_pvs=`for i in $(seq 0 6 18); do echo -n "\$dev1:$i-$(($i + 2)) "; done` 
+lvextend -l +100%PVS $vg/$lv $extend_pvs >out
+grep "^  Logical volume $lv successfully resized\$" out 
+check_lv_field_ $vg/$lv lv_size "72.00m"
+
+# Simple seg_count validation; initially create the LV with half the # of
+# extents (should be 1 lv segment), extend it (should go to 2 segments),
+# then reduce (should be back to 1)
+# FIXME: test other segment fields such as seg_size, pvseg_start, pvseg_size
+lvremove -f $vg/$lv
+pe_count=$(pvs -o pv_pe_count --noheadings $dev1)
+pe1=$(( $pe_count / 2 ))
+lvcreate -l $pe1 -n $lv $vg
+pesize=$(lvs -ovg_extent_size --units b --nosuffix --noheadings $vg/$lv)
+segsize=$(( $pe1 * $pesize / 1024 / 1024 ))m
+check_lv_field_ $vg/$lv seg_count 1
+check_lv_field_ $vg/$lv seg_start 0
+check_lv_field_ $vg/$lv seg_start_pe 0
+#check_lv_field_ $vg/$lv seg_size $segsize
+lvextend -l +$(( $pe_count * 1 )) $vg/$lv
+check_lv_field_ $vg/$lv seg_count 2
+lvreduce -f -l -$(( $pe_count * 1 )) $vg/$lv
+check_lv_field_ $vg/$lv seg_count 1
+
diff --git a/test/t-lvextend-snapshot-dmeventd.sh b/test/t-lvextend-snapshot-dmeventd.sh
new file mode 100644 (file)
index 0000000..f1ed72f
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+extend() {
+       lvextend --use-policies --config "activation { snapshot_extend_threshold = $1 }" $vg/snap
+}
+
+write() {
+       mount $DM_DEV_DIR/$vg/snap mnt
+       dd if=/dev/zero of=mnt/file$1 bs=1k count=$2
+       umount mnt
+}
+
+percent() {
+       lvs $vg/snap -o snap_percent --noheadings | cut -c4- | cut -d. -f1
+}
+
+which mkfs.ext2 || exit 200
+
+aux prepare_vg 2
+aux prepare_dmeventd
+
+lvcreate -l 8 -n base $vg
+mkfs.ext2 $DM_DEV_DIR/$vg/base
+
+lvcreate -s -l 4 -n snap $vg/base
+lvchange --monitor y $vg/snap
+
+mkdir mnt
+
+write 1 4096
+pre=`percent`
+sleep 10 # dmeventd only checks every 10 seconds :(
+post=`percent`
+
+test $pre = $post
+write 2 5000
+pre=`percent`
+sleep 10 # dmeventd only checks every 10 seconds :(
+post=`percent`
+test $pre -gt $post
diff --git a/test/t-lvextend-snapshot-policy.sh b/test/t-lvextend-snapshot-policy.sh
new file mode 100644 (file)
index 0000000..76ea980
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+extend() {
+       lvextend --use-policies --config "activation { snapshot_extend_threshold = $1 }" $vg/snap
+}
+
+write() {
+       mount $DM_DEV_DIR/$vg/snap mnt
+       dd if=/dev/zero of=mnt/file$1 bs=1k count=$2
+       umount mnt
+}
+
+percent() {
+       lvs $vg/snap -o snap_percent --noheadings | cut -c4- | cut -d. -f1
+}
+
+which mkfs.ext2 || exit 200
+
+aux prepare_vg 2
+lvcreate -l 8 -n base $vg
+mkfs.ext2 $DM_DEV_DIR/$vg/base
+
+lvcreate -s -l 4 -n snap $vg/base
+mkdir mnt
+
+write 1 4096
+pre=`percent`
+extend 50
+post=`percent`
+
+test $pre = $post
+write 2 4096
+pre=`percent`
+extend 50
+post=`percent`
+test $pre -gt $post
diff --git a/test/t-lvm-init.sh b/test/t-lvm-init.sh
new file mode 100644 (file)
index 0000000..cf324e4
--- /dev/null
@@ -0,0 +1,21 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# tests lvm initialization, and especially negative tests of error paths
+#
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+# invalid units
+not pvs --config 'global { units = "<" }'
+
diff --git a/test/t-lvmcache-exercise.sh b/test/t-lvmcache-exercise.sh
new file mode 100755 (executable)
index 0000000..aecb4a9
--- /dev/null
@@ -0,0 +1,23 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_pvs 5
+
+vgcreate $vg1 $dev1
+vgcreate $vg2 $dev3
+
+disable_dev $dev1
+pvscan
+vgcreate $vg1 $dev2
+enable_dev $dev1
+pvs
+pvs
diff --git a/test/t-lvresize-mirror.sh b/test/t-lvresize-mirror.sh
new file mode 100644 (file)
index 0000000..0d63102
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+aux prepare_vg 5 80
+
+# extend 2-way mirror
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+lvextend -l+2 $vg/$lv1
+check mirror $vg $lv1 $dev3
+check mirror_images_contiguous $vg $lv1
+lvremove -ff $vg
+
+# reduce 2-way mirror
+lvcreate -l4 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+lvreduce -l-2 $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# extend 2-way mirror (cling if not contiguous)
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvcreate -l1 -n $lv2 $vg $dev1
+lvcreate -l1 -n $lv3 $vg $dev2
+lvchange -an $vg/$lv1
+lvextend -l+2 $vg/$lv1
+check mirror $vg $lv1 $dev3
+check mirror_images_clung $vg $lv1
+lvremove -ff $vg
diff --git a/test/t-lvresize-usage.sh b/test/t-lvresize-usage.sh
new file mode 100755 (executable)
index 0000000..d9860e9
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_vg 2
+
+lvcreate -L 10M -n lv -i2 $vg
+lvresize -l +4 $vg/lv
+lvremove -ff $vg
+
+lvcreate -L 64M -n $lv -i2 $vg
+not lvresize -v -l +4 xxx/$lv
diff --git a/test/t-mdata-strings.sh b/test/t-mdata-strings.sh
new file mode 100755 (executable)
index 0000000..66d472a
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Test for proper escaping of strings in metadata (bz431474)'
+
+. ./test-utils.sh
+
+aux prepare_devs 1
+
+pv_ugly="__\"!@#\$%^&*,()|@||'\\\"__pv1"
+
+# 'set up temp files, loopback devices' 
+name=$(basename "$dev1")
+dmsetup rename "$name" "$PREFIX$pv_ugly"
+dev1=$(dirname "$dev1")/$PREFIX$pv_ugly
+
+# 'pvcreate, vgcreate on filename with backslashed chars' 
+pvcreate "$dev1" 
+vgcreate $vg "$dev1"
+
+# 'no parse errors and VG really exists' 
+vgs 2>err
+not grep "Parse error" err;
+vgs $vg
+
diff --git a/test/t-metadata-balance.sh b/test/t-metadata-balance.sh
new file mode 100755 (executable)
index 0000000..79570d9
--- /dev/null
@@ -0,0 +1,232 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_devs 6
+
+echo Make sure we can ignore / un-ignore mdas on a per-PV basis
+for pv_in_vg in 1 0; do
+for mdacp in 1 2; do
+       pvcreate --metadatacopies $mdacp $dev1 $dev2
+        pvcreate --metadatacopies 0 $dev3
+       if [ $pv_in_vg = 1 ]; then
+               vgcreate -c n "$vg" $dev1 $dev2 $dev3
+       fi
+       pvchange --metadataignore y $dev1
+       check_pv_field_ $dev1 pv_mda_count $mdacp
+       check_pv_field_ $dev1 pv_mda_used_count 0
+       check_pv_field_ $dev2 pv_mda_count $mdacp
+       check_pv_field_ $dev2 pv_mda_used_count $mdacp
+       if [ $pv_in_vg = 1 ]; then
+               check_vg_field_ $vg vg_mda_count $(($mdacp * 2))
+               check_vg_field_ $vg vg_mda_used_count $mdacp
+               check_vg_field_ $vg vg_mda_copies unmanaged
+       fi
+       pvchange --metadataignore n $dev1
+       check_pv_field_ $dev1 pv_mda_count $mdacp
+       check_pv_field_ $dev1 pv_mda_used_count $mdacp
+       if [ $pv_in_vg = 1 ]; then
+               check_vg_field_ $vg vg_mda_count $(($mdacp * 2))
+               check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+               check_vg_field_ $vg vg_mda_copies unmanaged
+               vgremove -f $vg
+       fi
+done
+done
+
+# Check if a PV has unignored (used) mdas, and if so, ignore
+pvignore_ () {
+       pv_mda_used_count=$(get_pv_field "$1" pv_mda_used_count)
+       if [ $pv_mda_used_count -ne 0 ]; then
+           pvchange --metadataignore y $1
+       fi
+}
+
+# Check if a PV has ignored mdas, and if so, unignore (make used)
+pvunignore_ () {
+       pv_mda_count=$(get_pv_field "$1" pv_mda_count)
+       pv_mda_used_count=$(get_pv_field "$1" pv_mda_used_count)
+       if [ $pv_mda_count -gt $pv_mda_used_count ]; then
+           pvchange --metadataignore n $1
+       fi
+}
+
+echo Test of vgmetadatacopies with vgcreate and vgchange
+for mdacp in 1 2; do
+       pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev4 $dev5
+       check_pv_field_ $dev1 pv_mda_used_count $mdacp
+       check_pv_field_ $dev2 pv_mda_used_count $mdacp
+       check_pv_field_ $dev4 pv_mda_used_count $mdacp
+       check_pv_field_ $dev5 pv_mda_used_count $mdacp
+       pvcreate --metadatacopies 0 $dev3
+       vgcreate -c n "$vg" $dev1 $dev2 $dev3
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       echo ensure both --vgmetadatacopies and --metadatacopies accepted
+       vgchange --metadatacopies $(($mdacp * 1)) $vg
+       echo --vgmetadatacopies is persistent on disk
+       echo --vgmetadatacopies affects underlying pv mda ignore
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+       check_vg_field_ $vg vg_mda_used_count $(($mdacp * 1))
+       vgchange --vgmetadatacopies $(($mdacp * 2)) $vg
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 2))
+       check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+       echo allow setting metadatacopies larger than number of PVs
+       vgchange --vgmetadatacopies $(($mdacp * 5)) $vg
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 5))
+       check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+       echo setting to 0 disables automatic balancing
+       vgchange --vgmetadatacopies unmanaged $vg
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       vgremove -f $vg
+       echo vgcreate succeeds even when creating a VG w/all ignored mdas
+       pvchange --metadataignore y $dev1 $dev2
+       check_pv_field_ $dev1 pv_mda_count $mdacp
+       check_pv_field_ $dev2 pv_mda_used_count 0
+       vgcreate -c n "$vg" $dev1 $dev2
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       vgremove -f $vg
+       echo vgcreate succeeds with a specific number of metadata copies
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 2)) "$vg" $dev1 $dev2
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 2))
+       vgremove -f $vg
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) "$vg" $dev1 $dev2
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+       vgremove -f $vg
+       echo vgcreate succeeds with a larger value than total metadatacopies
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 5)) "$vg" $dev1 $dev2
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 5))
+       vgremove -f $vg
+       echo vgcreate succeeds with --vgmetadatacopies unmanaged
+       vgcreate -c n --vgmetadatacopies unmanaged "$vg" $dev1 $dev2
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       vgremove -f $vg
+       pvunignore_ $dev1
+       pvunignore_ $dev2
+       pvunignore_ $dev4
+       pvunignore_ $dev5
+       echo vgcreate succeds with small value of --metadatacopies, ignores mdas
+       vgcreate -c n --vgmetadatacopies 1 "$vg" $dev1 $dev2 $dev4 $dev5
+       check_vg_field_ $vg vg_mda_copies 1
+       check_vg_field_ $vg vg_mda_count $(($mdacp * 4))
+       check_vg_field_ $vg vg_mda_used_count 1
+       echo Setting a larger value should trigger non-ignore of mdas
+       vgchange --metadatacopies 3 $vg
+       check_vg_field_ $vg vg_mda_copies 3
+       check_vg_field_ $vg vg_mda_used_count 3
+       echo Setting all should trigger unignore of all mdas
+       vgchange --vgmetadatacopies all $vg
+       check_vg_field_ $vg vg_mda_count $(($mdacp * 4))
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       check_vg_field_ $vg vg_mda_used_count $(($mdacp * 4))
+       echo --vgmetadatacopies 0 should be unmanaged for vgchange and vgcreate
+       vgchange --vgmetadatacopies 0 $vg
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       vgremove -f $vg
+       vgcreate -c n --vgmetadatacopies 0 "$vg" $dev1 $dev2 $dev4 $dev5
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       vgremove -f $vg
+done
+
+echo Test vgextend / vgreduce with vgmetadatacopies
+for mdacp in 1 2; do
+       pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev4 $dev5
+       pvcreate --metadatacopies 0 $dev3
+       echo Set a large value of vgmetadatacopies
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 5)) "$vg" $dev1 $dev2 $dev3
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 5))
+       echo Ignore mdas on devices to be used for vgextend
+       echo Large value of vgetadatacopies should automatically un-ignore mdas
+       pvchange --metadataignore y $dev4 $dev5
+       check_pv_field_ $dev4 pv_mda_used_count 0
+       vgextend $vg $dev4 $dev5
+       check_pv_field_ $dev4 pv_mda_used_count $mdacp
+       check_pv_field_ $dev5 pv_mda_used_count $mdacp
+       vgremove -f $vg
+       echo Set a small value of vgmetadatacopies
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) "$vg" $dev1 $dev2 $dev3
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+       echo Ignore mdas on devices to be used for vgextend
+       echo Small value of vgetadatacopies should leave mdas as ignored
+       pvchange --metadataignore y $dev4 $dev5
+       check_pv_field_ $dev4 pv_mda_used_count 0
+       vgextend $vg $dev4 $dev5
+       check_pv_field_ $dev4 pv_mda_used_count 0
+       check_pv_field_ $dev5 pv_mda_used_count 0
+       echo vgreduce of ignored pv w/mda should not trigger any change to ignore bits
+       vgreduce $vg $dev4
+       check_pv_field_ $dev4 pv_mda_used_count 0
+       check_pv_field_ $dev5 pv_mda_used_count 0
+       echo vgreduce of un-ignored pv w/mda should trigger un-ignore on an mda
+       vgreduce $vg $dev1 $dev2 $dev3
+       check_pv_field_ $dev5 pv_mda_used_count $mdacp
+       check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+       pvunignore_ $dev1
+       pvunignore_ $dev2
+       echo setting vgmetadatacopies to unmanaged should allow vgextend to add w/out balancing
+       vgchange --vgmetadatacopies unmanaged $vg
+       vgextend $vg $dev1 $dev2
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       check_vg_field_ $vg vg_mda_count $(($mdacp * 3))
+       check_vg_field_ $vg vg_mda_used_count $((mdacp * 3))
+       check_pv_field_ $dev1 pv_mda_used_count $mdacp
+       check_pv_field_ $dev2 pv_mda_used_count $mdacp
+       vgremove -f $vg
+done
+
+echo Test special situations, vgsplit, vgmerge, etc
+for mdacp in 1 2; do
+       pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev3 $dev4 $dev5
+       vgcreate -c n --vgmetadatacopies 2 $vg1 $dev1 $dev2 $dev3
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) $vg2 $dev4 $dev5
+       echo vgsplit/vgmerge preserves value of metadata copies
+       check_vg_field_ $vg1 vg_mda_copies 2
+       check_vg_field_ $vg2 vg_mda_copies $(($mdacp * 1))
+       vgsplit $vg1 $vg2 $dev1
+       check_vg_field_ $vg2 vg_mda_copies $(($mdacp * 1))
+       vgmerge $vg1 $vg2
+       check_vg_field_ $vg1 vg_mda_copies 2
+       check_vg_field_ $vg1 vg_mda_count $(($mdacp * 5))
+       echo vgsplit into new vg sets proper value of vgmetadatacopies
+       vgsplit --vgmetadatacopies $(($mdacp * 2)) $vg1 $vg2 $dev1 $dev2
+       check_vg_field_ $vg2 vg_mda_copies $(($mdacp * 2))
+       echo vgchange fails if given both vgmetadatacopies and metadatacopies
+       not vgchange --vgmetadatacopies 5 --metadatacopies 7 $vg2
+       vgremove -f $vg1
+       vgremove -f $vg2
+done
+
+echo Test combination of --vgmetadatacopies and pvchange --metadataignore
+for mdacp in 1 2; do
+       pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev3 $dev4 $dev5
+       vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) $vg1 $dev1 $dev2
+       check_vg_field_ $vg1 vg_mda_copies $(($mdacp * 1))
+       check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 1))
+       pvignore_ $dev3
+       echo Ensure vgextend of PVs with ignored MDAs does not add to vg_mda_used_count
+       vgextend $vg1 $dev3
+       check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 1))
+       echo Using pvchange to unignore should update vg_mda_used_count
+       pvchange -f --metadataignore n $dev3
+       check_pv_field_ $dev3 pv_mda_used_count $mdacp
+       check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 2))
+       echo Set unmanaged on the vg should keep ignore bits the same during vgextend
+       vgchange --vgmetadatacopies unmanaged $vg1
+       check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 2))
+       pvunignore_ $dev4
+       vgextend $vg1 $dev4
+       check_pv_field_ $dev4 pv_mda_used_count $mdacp
+       check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 3))
+       echo Using pvchange to ignore should update vg_mda_used_count
+       pvchange -f --metadataignore y $dev4
+       check_pv_field_ $dev4 pv_mda_used_count 0
+       check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 2))
+       vgremove -f $vg1
+done
diff --git a/test/t-metadata.sh b/test/t-metadata.sh
new file mode 100755 (executable)
index 0000000..49066c7
--- /dev/null
@@ -0,0 +1,80 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 0 $dev3
+pvcreate $dev4
+pvcreate --metadatacopies 0 $dev5
+
+vgcreate -c n "$vg" $devs
+lvcreate -n $lv -l 1 -i5 -I256 $vg
+
+pvchange -x n $dev1
+pvchange -x y $dev1
+vgchange -a n $vg
+pvchange --uuid $dev1
+pvchange --uuid $dev2
+vgremove -f $vg
+
+# check that PVs without metadata don't cause too many full device rescans (bz452606)
+for mdacp in 1 0; do
+       pvcreate --metadatacopies $mdacp $devs
+       pvcreate $dev1
+       vgcreate -c n $vg $devs
+       lvcreate -n $lv1 -l 2 -i5 -I256 $vg
+       lvcreate -n $lv2 -m2 -l 2  $vg
+       #lvchange -an $vg
+       lvchange -an $vg/$lv1
+       lvchange -an $vg/$lv2
+       vgchange -ay $vg
+       lvchange -vvvv -an $vg/$lv1 >out$mdacp 2>&1 
+       lvchange -vvvv -an $vg/$lv2 >>out$mdacp 2>&1 
+       vgremove -f $vg
+done
+not grep "Cached VG .* incorrect PV list" out0
+
+# some M1 metadata tests
+pvcreate -M1 $dev1
+pvcreate -M1 $dev2
+pvcreate -M1 $dev3
+pv3_uuid=$(pvs --noheadings -o pv_uuid $dev3)
+vgcreate -M1 -c n $vg $dev1 $dev2 $dev3
+pvchange --uuid $dev1
+
+# verify pe_start of all M1 PVs
+pv_align="128.00k"
+check_pv_field_ $dev1 pe_start $pv_align
+check_pv_field_ $dev2 pe_start $pv_align
+check_pv_field_ $dev3 pe_start $pv_align
+
+pvs --units k -o name,pe_start,vg_mda_size,vg_name
+
+# upgrade from v1 to v2 metadata
+vgconvert -M2 $vg
+
+# verify pe_start of all M2 PVs
+check_pv_field_ $dev1 pe_start $pv_align
+check_pv_field_ $dev2 pe_start $pv_align
+check_pv_field_ $dev3 pe_start $pv_align
+
+pvs --units k -o name,pe_start,vg_mda_size,vg_name
+
+# create backup and then restore $dev3
+vgcfgbackup -f $TESTDIR/bak-%s $vg
+pvcreate -ff -y --restorefile $TESTDIR/bak-$vg --uuid $pv3_uuid $dev3
+vgcfgrestore -f $TESTDIR/bak-$vg $vg
+
+# verify pe_start of $dev3
+check_pv_field_ $dev3 pe_start $pv_align
diff --git a/test/t-mirror-names.sh b/test/t-mirror-names.sh
new file mode 100644 (file)
index 0000000..be6045f
--- /dev/null
@@ -0,0 +1,156 @@
+#!/bin/sh
+# Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2007-2008 NEC Corporation
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description="check namings of mirrored LV"
+
+. ./test-utils.sh
+
+# ---------------------------------------------------------------------
+# Utilities
+
+lv_devices_() {
+  local d
+  local lv=$1
+  shift
+  local devices=$*
+
+  local devs=$(lvs -a -odevices --noheadings $lv | sed 's/([0-9]*)//g' |
+               sed 's/ //g' | sed 's/,/ /g')
+
+  for d in $devs; do
+    (echo $devices | grep -q $d)  || return 1
+    devices=$(echo $devices | sed "s/$d//")
+  done
+
+  [ "$(echo $devices | sed 's/ //g')" = "" ]
+}
+
+lv_mirror_log_() {
+  local lv=$1
+
+  echo $(lvs -a -omirror_log --noheadings $lv | sed 's/ //g')
+}
+
+lv_convert_lv_() {
+  local lv=$1
+
+  echo $(lvs -a -oconvert_lv --noheadings $lv | sed 's/ //g')
+}
+
+# ---------------------------------------------------------------------
+# Initialize PVs and VGs
+
+aux prepare_vg 5 80
+
+# ---------------------------------------------------------------------
+# Common environment setup/cleanup for each sub testcases
+
+prepare_lvs_() {
+       lvremove -ff $vg
+       if dmsetup table|grep $vg; then
+               echo "ERROR: lvremove did leave some some mappings in DM behind!"
+               return 1
+       fi
+  :
+}
+
+check_and_cleanup_lvs_() {
+  lvs -a -o+devices $vg 
+  lvremove -ff $vg
+       if dmsetup table|grep $vg; then
+               echo "ERROR: lvremove did leave some some mappings in DM behind!"
+               return 1
+       fi
+}
+
+prepare_lvs_ 
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# basic
+
+#COMM "init: lvcreate" 
+prepare_lvs_
+
+#COMM "mirror images are ${lv1}_mimage_x"
+lvcreate -l2 -m1 -n $lv1 $vg 
+lv_devices_ $vg/$lv1 "$lv1"_mimage_0 "$lv1"_mimage_1
+
+#COMM "mirror log is ${lv1}_mlog"
+lv_mirror_log_ $vg/$lv1 "$lv1"_mlog
+
+# "cleanup" 
+check_and_cleanup_lvs_
+
+#COMM "mirror with name longer than 22 characters (bz221322)"
+name="LVwithanamelogerthan22characters_butidontwonttocounthem"
+lvcreate -m1 -l2 -n"$name" $vg
+lvs $vg/"$name"
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# lvrename
+
+#COMM "init: lvrename" 
+prepare_lvs_
+
+#COMM "renamed mirror names: $lv1 to $lv2" 
+lvcreate -l2 -m1 -n $lv1 $vg 
+lvrename $vg/$lv1 $vg/$lv2 
+lv_devices_ $vg/$lv2 "$lv2"_mimage_0 "$lv2"_mimage_1 
+lv_mirror_log_ $vg/$lv2 "$lv2"_mlog
+
+#COMM "cleanup" 
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# lvconvert
+
+#COMM "init: lvconvert" 
+prepare_lvs_
+
+#COMM "converting mirror names is ${lv1}_mimagetmp_2"
+lvcreate -l2 -m1 -n $lv1 $vg 
+lvconvert -m+1 -i+10 -b $vg/$lv1
+convlv=$(lv_convert_lv_ "$vg/$lv1") 
+test "$convlv" = "$lv1"_mimagetmp_2 
+lv_devices_ $vg/$lv1 "$convlv" "$lv1"_mimage_2 
+lv_devices_ "$vg/$convlv" "$lv1"_mimage_0 "$lv1"_mimage_1 
+loglv=$(lv_mirror_log_ "$vg/$convlv") 
+test "$loglv" = "$lv1"_mlog
+
+#COMM "mirror log name after re-adding is ${lv1}_mlog" \
+lvconvert --mirrorlog core $vg/$lv1 
+lvconvert --mirrorlog disk $vg/$lv1 
+convlv=$(lv_convert_lv_ "$vg/$lv1") 
+lv_devices_ $vg/$lv1 "$convlv" "$lv1"_mimage_2 
+lv_devices_ "$vg/$convlv" "$lv1"_mimage_0 "$lv1"_mimage_1 
+loglv=$(lv_mirror_log_ "$vg/$convlv") 
+test "$loglv" = "$lv1"_mlog
+
+#COMM "renamed converting mirror names: $lv1 to $lv2" \
+lvrename $vg/$lv1 $vg/$lv2 
+convlv=$(lv_convert_lv_ "$vg/$lv2") 
+lv_devices_ $vg/$lv2 "$convlv" "$lv2"_mimage_2 
+lv_devices_ "$vg/$convlv" "$lv2"_mimage_0 "$lv2"_mimage_1 
+loglv=$(lv_mirror_log_ "$vg/$convlv") 
+test "$loglv" = "$lv2"_mlog
+
+#COMM "cleanup" 
+check_and_cleanup_lvs_
+
+# Temporary mirror log should have "_mlogtmp_<n>" suffix
+# but currently lvconvert doesn't have an option to add the log.
+# If such feature is added in future, a test for that should
+# be added.
+
+# ---------------------------------------------------------------------
diff --git a/test/t-mirror-vgreduce-removemissing.sh b/test/t-mirror-vgreduce-removemissing.sh
new file mode 100755 (executable)
index 0000000..0f6a8b0
--- /dev/null
@@ -0,0 +1,421 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2007 NEC Corporation
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description="ensure that 'vgreduce --removemissing' works on mirrored LV"
+
+. ./test-utils.sh
+
+lv_is_on_ ()
+{
+       local lv=$vg/$1
+       shift
+       local pvs=$*
+
+       echo "Check if $lv is exactly on PVs $pvs"
+       rm -f out1 out2
+       echo $pvs | sed 's/ /\n/g' | sort | uniq > out1
+
+       lvs -a -o+devices $lv
+       lvs -a -odevices --noheadings $lv | \
+       sed 's/([^)]*)//g; s/[ ,]/\n/g' | sort | uniq > out2
+
+       diff --ignore-blank-lines out1 out2
+}
+
+mimages_are_on_ ()
+{
+       local lv=$1
+       shift
+       local pvs="$*"
+       local mimages
+       local i
+
+       echo "Check if mirror images of $lv are on PVs $pvs"
+       rm -f out1 out2
+       echo $pvs | sed 's/ /\n/g' | sort | uniq > out1
+
+       mimages=$(lvs --noheadings -a -o lv_name $vg | grep "${lv}_mimage_" | \
+               sed 's/\[//g; s/\]//g')
+       for i in $mimages; do
+               echo "Checking $vg/$i"
+               lvs -a -o+devices $vg/$i
+               lvs -a -odevices --noheadings $vg/$i | \
+                       sed 's/([^)]*)//g; s/ //g; s/,/ /g' | sort | uniq >> out2
+       done
+
+       diff --ignore-blank-lines out1 out2
+}
+
+mirrorlog_is_on_()
+{
+       local lv="$1"_mlog
+       shift
+       lv_is_on_ $lv $*
+}
+
+lv_is_linear_()
+{
+       echo "Check if $1 is linear LV (i.e. not a mirror)"
+       lvs -o stripes,attr --noheadings $vg/$1 | sed 's/ //g'
+       lvs -o stripes,attr --noheadings $vg/$1 | sed 's/ //g' | grep -q '^1-'
+}
+
+rest_pvs_()
+{
+       local index=$1
+       local num=$2
+       local rem=""
+       local n
+
+       for n in $(seq 1 $(($index - 1))) $(seq $(($index + 1)) $num); do
+               eval local dev=$\dev$n
+               rem="$rem $dev"
+       done
+
+       echo "$rem"
+}
+
+# ---------------------------------------------------------------------
+# Initialize PVs and VGs
+
+prepare_vg 5
+
+# ---------------------------------------------------------------------
+# Common environment setup/cleanup for each sub testcases
+
+prepare_lvs_()
+{
+       lvremove -ff $vg;
+       if dmsetup table|grep $vg; then
+               echo "ERROR: lvremove did leave some some mappings in DM behind!"
+               return 1
+       fi
+       :
+}
+
+check_and_cleanup_lvs_()
+{
+       lvs -a -o+devices $vg 
+       lvremove -ff $vg
+       if dmsetup table|grep $vg; then
+               echo "ERROR: lvremove did leave some some mappings in DM behind!"
+               return 1
+       fi
+}
+
+recover_vg_()
+{
+       enable_dev $* 
+       pvcreate -ff $* 
+       vgextend $vg $* 
+       check_and_cleanup_lvs_
+}
+
+#COMM "check environment setup/cleanup" 
+prepare_lvs_ 
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# one of mirror images has failed
+
+#COMM "basic: fail the 2nd mirror image of 2-way mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvchange -an $vg/$lv1
+aux mimages_are_on_ $lv1 $dev1 $dev2
+mirrorlog_is_on_ $lv1 $dev3
+disable_dev $dev2
+vgreduce --removemissing --force $vg
+lv_is_linear_ $lv1
+lv_is_on_ $lv1 $dev1
+
+# "cleanup"
+recover_vg_ $dev2
+
+# ---------------------------------------------------------------------
+# LV has 3 images in flat,
+# 1 out of 3 images fails
+
+#COMM test_3way_mirror_fail_1_ <PV# to fail>
+test_3way_mirror_fail_1_()
+{
+       local index=$1
+
+       lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev4:0
+       lvchange -an $vg/$lv1
+       aux mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+       mirrorlog_is_on_ $lv1 $dev4
+       eval disable_dev \$dev$index
+       vgreduce --removemissing --force $vg
+       lvs -a -o+devices $vg
+       mimages_are_on_ $lv1 $(rest_pvs_ $index 3)
+       mirrorlog_is_on_ $lv1 $dev4
+}
+
+for n in $(seq 1 3); do
+       #COMM fail mirror image $(($n - 1)) of 3-way mirrored LV"
+       prepare_lvs_
+       test_3way_mirror_fail_1_ $n
+       eval recover_vg_ \$dev$n
+done
+
+# ---------------------------------------------------------------------
+# LV has 3 images in flat,
+# 2 out of 3 images fail
+
+#COMM test_3way_mirror_fail_2_ <PV# NOT to fail>
+test_3way_mirror_fail_2_()
+{
+       local index=$1
+
+       lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev4:0
+       lvchange -an $vg/$lv1
+       mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+       mirrorlog_is_on_ $lv1 $dev4
+       rest_pvs_ $index 3
+       disable_dev $(rest_pvs_ $index 3)
+       vgreduce --force --removemissing $vg
+       lvs -a -o+devices $vg
+       aux lv_is_linear_ $lv1
+       eval lv_is_on_ $lv1 \$dev$n
+}
+
+for n in $(seq 1 3); do
+       #COMM fail mirror images other than mirror image $(($n - 1)) of 3-way mirrored LV
+       prepare_lvs_
+       test_3way_mirror_fail_2_ $n
+       recover_vg_ $(rest_pvs_ $n 3)
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 1 of them is in the temporary mirror for syncing.
+# 1 out of 4 images fails
+
+#COMM test_3way_mirror_plus_1_fail_1_ <PV# to fail>
+test_3way_mirror_plus_1_fail_1_()
+{
+       local index=$1
+
+       lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev5:0
+       lvchange -an $vg/$lv1 
+       lvconvert -m+1 $vg/$lv1 $dev4 
+       mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4 
+       mirrorlog_is_on_ $lv1 $dev5 
+       eval disable_dev \$dev$n 
+       vgreduce --removemissing --force $vg 
+       lvs -a -o+devices $vg 
+       mimages_are_on_ $lv1 $(rest_pvs_ $index 4) 
+       mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+       #COMM "fail mirror image $(($n - 1)) of 4-way (1 converting) mirrored LV"
+       prepare_lvs_
+       test_3way_mirror_plus_1_fail_1_ $n
+       eval recover_vg_ \$dev$n
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 1 of them is in the temporary mirror for syncing.
+# 3 out of 4 images fail
+
+#COMM test_3way_mirror_plus_1_fail_3_ <PV# NOT to fail>
+test_3way_mirror_plus_1_fail_3_()
+{
+       local index=$1
+
+       lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev5:0
+       lvchange -an $vg/$lv1 
+       lvconvert -m+1 $vg/$lv1 $dev4 
+       mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4 
+       mirrorlog_is_on_ $lv1 $dev5 
+       disable_dev $(rest_pvs_ $index 4) 
+       vgreduce --removemissing --force $vg 
+       lvs -a -o+devices $vg 
+       eval local dev=\$dev$n
+       mimages_are_on_ $lv1 $dev || lv_is_on_ $lv1 $dev
+       not mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+       #COMM "fail mirror images other than mirror image $(($n - 1)) of 4-way (1 converting) mirrored LV"
+       prepare_lvs_
+       test_3way_mirror_plus_1_fail_3_ $n
+       recover_vg_ $(rest_pvs_ $n 4)
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 2 of them are in the temporary mirror for syncing.
+# 1 out of 4 images fail
+
+# test_2way_mirror_plus_2_fail_1_ <PV# to fail>
+test_2way_mirror_plus_2_fail_1_()
+{
+       local index=$1
+
+       lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+       lvchange -an $vg/$lv1 
+       lvconvert -m+2 $vg/$lv1 $dev3 $dev4 
+       mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4 
+       mirrorlog_is_on_ $lv1 $dev5 
+       eval disable_dev \$dev$n 
+       vgreduce --removemissing --force $vg 
+       lvs -a -o+devices $vg 
+       mimages_are_on_ $lv1 $(rest_pvs_ $index 4) 
+       mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+       #COMM "fail mirror image $(($n - 1)) of 4-way (2 converting) mirrored LV" 
+       prepare_lvs_ 
+       test_2way_mirror_plus_2_fail_1_ $n
+       eval recover_vg_ \$dev$n
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 2 of them are in the temporary mirror for syncing.
+# 3 out of 4 images fail
+
+# test_2way_mirror_plus_2_fail_3_ <PV# NOT to fail>
+test_2way_mirror_plus_2_fail_3_()
+{
+       local index=$1
+
+       lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+       lvchange -an $vg/$lv1 
+       lvconvert -m+2 $vg/$lv1 $dev3 $dev4 
+       mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4 
+       mirrorlog_is_on_ $lv1 $dev5 
+       disable_dev $(rest_pvs_ $index 4) 
+       vgreduce --removemissing --force $vg 
+       lvs -a -o+devices $vg 
+       eval local dev=\$dev$n
+       mimages_are_on_ $lv1 $dev || lv_is_on_ $lv1 $dev
+       not mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+       #COMM "fail mirror images other than mirror image $(($n - 1)) of 4-way (2 converting) mirrored LV"
+       prepare_lvs_
+       test_2way_mirror_plus_2_fail_3_ $n
+       recover_vg_ $(rest_pvs_ $n 4)
+done
+
+# ---------------------------------------------------------------------
+# log device is gone (flat mirror and stacked mirror)
+
+#COMM "fail mirror log of 2-way mirrored LV" 
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+mimages_are_on_ $lv1 $dev1 $dev2 
+mirrorlog_is_on_ $lv1 $dev5 
+disable_dev $dev5 
+vgreduce --removemissing --force $vg 
+mimages_are_on_ $lv1 $dev1 $dev2 
+not mirrorlog_is_on_ $lv1 $dev5
+recover_vg_ $dev5
+
+#COMM "fail mirror log of 3-way (1 converting) mirrored LV" 
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+lvconvert -m+1 $vg/$lv1 $dev3 
+mimages_are_on_ $lv1 $dev1 $dev2 $dev3 
+mirrorlog_is_on_ $lv1 $dev5 
+disable_dev $dev5 
+vgreduce --removemissing --force $vg 
+mimages_are_on_ $lv1 $dev1 $dev2 $dev3 
+not mirrorlog_is_on_ $lv1 $dev5
+recover_vg_ $dev5
+
+# ---------------------------------------------------------------------
+# all images are gone (flat mirror and stacked mirror)
+
+#COMM "fail all mirror images of 2-way mirrored LV"
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+mimages_are_on_ $lv1 $dev1 $dev2 
+mirrorlog_is_on_ $lv1 $dev5 
+disable_dev $dev1 $dev2 
+vgreduce --removemissing --force $vg 
+not lvs $vg/$lv1
+recover_vg_ $dev1 $dev2
+
+#COMM "fail all mirror images of 3-way (1 converting) mirrored LV"
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+lvconvert -m+1 $vg/$lv1 $dev3 
+mimages_are_on_ $lv1 $dev1 $dev2 $dev3 
+mirrorlog_is_on_ $lv1 $dev5 
+disable_dev $dev1 $dev2 $dev3 
+vgreduce --removemissing --force $vg 
+not lvs $vg/$lv1
+recover_vg_ $dev1 $dev2 $dev3
+
+# ---------------------------------------------------------------------
+# Multiple LVs
+
+#COMM "fail a mirror image of one of mirrored LV"
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+lvcreate -l2 -m1 -n $lv2 $vg $dev3 $dev4 $dev5:1 
+lvchange -an $vg/$lv2 
+mimages_are_on_ $lv1 $dev1 $dev2 
+mimages_are_on_ $lv2 $dev3 $dev4 
+mirrorlog_is_on_ $lv1 $dev5 
+mirrorlog_is_on_ $lv2 $dev5 
+disable_dev $dev2 
+vgreduce --removemissing --force $vg 
+mimages_are_on_ $lv2 $dev3 $dev4 
+mirrorlog_is_on_ $lv2 $dev5 
+lv_is_linear_ $lv1 
+lv_is_on_ $lv1 $dev1
+recover_vg_ $dev2
+
+#COMM "fail mirror images, one for each mirrored LV"
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+lvcreate -l2 -m1 -n $lv2 $vg $dev3 $dev4 $dev5:1 
+lvchange -an $vg/$lv2 
+mimages_are_on_ $lv1 $dev1 $dev2 
+mimages_are_on_ $lv2 $dev3 $dev4 
+mirrorlog_is_on_ $lv1 $dev5 
+mirrorlog_is_on_ $lv2 $dev5 
+disable_dev $dev2 
+disable_dev $dev4 
+vgreduce --removemissing --force $vg 
+lv_is_linear_ $lv1 
+lv_is_on_ $lv1 $dev1 
+lv_is_linear_ $lv2 
+lv_is_on_ $lv2 $dev3
+recover_vg_ $dev2 $dev4
+
+# ---------------------------------------------------------------------
+# no failure
+
+#COMM "no failures"
+prepare_lvs_ 
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1 
+mimages_are_on_ $lv1 $dev1 $dev2 
+mirrorlog_is_on_ $lv1 $dev5 
+vgreduce --removemissing --force $vg 
+mimages_are_on_ $lv1 $dev1 $dev2 
+mirrorlog_is_on_ $lv1 $dev5
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+
diff --git a/test/t-nomda-missing.sh b/test/t-nomda-missing.sh
new file mode 100644 (file)
index 0000000..fb0b882
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_devs 4
+pvcreate $dev1 $dev2
+pvcreate --metadatacopies 0 $dev3 $dev4
+vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4
+
+lvcreate -l1 -n linear1 $vg $dev1
+lvcreate -l1 -n linear2 $vg $dev2
+lvcreate -l2 -n linear12 $vg $dev1:4 $dev2:4
+
+lvcreate -l1 -n origin1 $vg $dev1
+lvcreate -s $vg/origin1 -l1 -n s_napshot2 $dev2
+
+lvcreate -l1 -m1 -n mirror12 --mirrorlog core $vg $dev1 $dev2
+lvcreate -l1 -m1 -n mirror123 $vg $dev1 $dev2 $dev3
+
+vgchange -a n $vg
+disable_dev $dev1
+not vgchange -a y $vg
+not vgck $vg
+
+check inactive $vg linear1
+check active $vg linear2
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg linear12
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev1
+disable_dev $dev2
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg linear1
+check inactive $vg linear2
+check inactive $vg linear12
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev2
+disable_dev $dev3
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check inactive $vg mirror123
+check active $vg mirror12
+
+vgchange -a n $vg
+enable_dev $dev3
+disable_dev $dev4
+vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check active $vg mirror12
+check active $vg mirror123
diff --git a/test/t-pool-labels.sh b/test/t-pool-labels.sh
new file mode 100755 (executable)
index 0000000..57f4f9a
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+# create the old GFS pool labeled linear devices
+create_pool_label_()
+{
+  # FIXME
+  # echo -e is bashism, dash builtin sh doesn't do \xNN in printf either
+  # printf comes from coreutils, and is probably not posix either
+  env printf "\x01\x16\x70\x06\x5f\xcf\xff\xb9\xf8\x24\x8apool1" | dd of=$2 bs=5 seek=1 conv=notrunc
+  env printf "\x04\x01\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0$1\x68\x01\x16\x70\x00\x00\x00\x00\x00\x06\x5f\xd0" | dd of=$2 bs=273 seek=1 conv=notrunc
+}
+
+env printf "" || exit 200 # skip if printf is not available
+
+aux prepare_devs 2
+
+create_pool_label_ 0 "$dev1"
+create_pool_label_ 1 "$dev2"
+
+# check that pvcreate fails without -ff on the pool device
+not pvcreate "$dev1"
+
+# check that vgdisplay and pvcreate -ff works with the pool device
+vgdisplay --config 'global { locking_type = 0 }'
+disable_dev "$dev2"
+# FIXME! since pool1 cannot be opened, vgdisplay gives error... should we say
+# "not" there instead, checking that it indeed does fail?
+vgdisplay --config 'global { locking_type = 0 }' || true
+pvcreate -ff -y "$dev1"
diff --git a/test/t-pv-range-overflow.sh b/test/t-pv-range-overflow.sh
new file mode 100755 (executable)
index 0000000..04674cb
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Ensure that pvmove diagnoses PE-range values 2^32 and larger.'
+
+. ./test-utils.sh
+
+aux prepare_vg 2
+
+lvcreate -L4 -n"$lv" $vg
+
+# Test for the bogus diagnostic reported in BZ 284771
+# http://bugzilla.redhat.com/284771.
+# 'run pvmove with an unrecognized LV name to show bad diagnostic'
+not pvmove -v -nbogus $dev1 $dev2 2> err
+grep "  Logical volume bogus not found." err
+
+# With lvm-2.02.28 and earlier, on a system with 64-bit "long int",
+# the PE range parsing code would accept values up to 2^64-1, but would
+# silently truncate them to int32_t.  I.e., $dev1:$(echo 2^32|bc) would be
+# treated just like $dev1:0.
+# 'run the offending pvmove command'
+not pvmove -v -n$lv $dev1:4294967296 $dev2
+
diff --git a/test/t-pvchange-usage.sh b/test/t-pvchange-usage.sh
new file mode 100755 (executable)
index 0000000..0f69249
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Test pvchange option values'
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+for mda in 0 1 2 
+do
+# "setup pv with metadatacopies = $mda" 
+       pvcreate $dev4 
+       pvcreate --metadatacopies $mda $dev1 
+       vgcreate $vg1 $dev1 $dev4 
+
+# "pvchange adds/dels tag to pvs with metadatacopies = $mda " 
+       pvchange $dev1 --addtag test$mda 
+       check_pv_field_ $dev1 pv_tags test$mda 
+       pvchange $dev1 --deltag test$mda 
+       check_pv_field_ $dev1 pv_tags ""
+
+# "vgchange disable/enable allocation for pvs with metadatacopies = $mda (bz452982)"
+       pvchange $dev1 -x n 
+       check_pv_field_ $dev1 pv_attr  --  
+       pvchange $dev1 -x y 
+       check_pv_field_ $dev1 pv_attr  a- 
+
+# 'remove pv'
+       vgremove $vg1 
+       pvremove $dev1 $dev4
+done
+
+# "pvchange uuid"
+pvcreate --metadatacopies 0 $dev1 
+pvcreate --metadatacopies 2 $dev2 
+vgcreate $vg1 $dev1 $dev2 
+pvchange -u $dev1 
+pvchange -u $dev2 
+vg_validate_pvlv_counts_ $vg1 2 0 0
+pvchange -u --all
+vg_validate_pvlv_counts_ $vg1 2 0 0
+
+# "pvchange rejects uuid change under an active lv" 
+lvcreate -l 16 -i 2 -n $lv --alloc anywhere $vg1 
+vg_validate_pvlv_counts_ $vg1 2 1 0 
+not pvchange -u $dev1
+lvchange -an "$vg1"/"$lv" 
+pvchange -u $dev1
+
+# "cleanup" 
+lvremove -f "$vg1"/"$lv"
+vgremove $vg1
+
+# "pvchange reject --addtag to lvm1 pv"
+pvcreate -M1 $dev1 
+not pvchange $dev1 --addtag test
+
diff --git a/test/t-pvcreate-metadata0.sh b/test/t-pvcreate-metadata0.sh
new file mode 100755 (executable)
index 0000000..8447ce5
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# Testcase for bugzilla #450651
+# also checks that vgremove properly removes all lv devices in the right order
+#
+# 'Test pvcreate without metadata on all pvs'
+
+. ./test-utils.sh
+
+aux prepare_devs 2 128
+
+#lv_snap=$lv2
+pvcreate "$dev1"
+pvcreate --metadatacopies 0 "$dev2"
+
+# "check lv snapshot" 
+vgcreate -c n "$vg" "$dev1" "$dev2" 
+lvcreate -n "$lv" -l 60%FREE "$vg" 
+lvcreate -s -n $lv2 -l 10%FREE "$vg"/"$lv" 
+pvdisplay 
+lvdisplay
+vgremove -f "$vg"
diff --git a/test/t-pvcreate-operation-md.sh b/test/t-pvcreate-operation-md.sh
new file mode 100644 (file)
index 0000000..cab63a0
--- /dev/null
@@ -0,0 +1,143 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# skip this test if mdadm or sfdisk (or others) aren't available
+which mdadm || exit 200
+which sfdisk || exit 200
+which perl || exit 200
+which awk || exit 200
+which cut || exit 200
+
+test -f /proc/mdstat && grep -q raid0 /proc/mdstat || \
+modprobe raid0 || exit 200
+
+. ./test-utils.sh
+
+prepare_lvmconf '[ "a|/dev/md.*|", "a/dev\/mapper\/.*$/", "r/.*/" ]'
+aux prepare_devs 2
+
+# Have MD use a non-standard name to avoid colliding with an existing MD device
+# - mdadm >= 3.0 requires that non-standard device names be in /dev/md/
+# - newer mdadm _completely_ defers to udev to create the associated device node
+mdadm_maj=$(mdadm --version 2>&1 | perl -pi -e 's|.* v(\d+).*|\1|')
+[ $mdadm_maj -ge 3 ] && \
+    mddev=/dev/md/md_lvm_test0 || \
+    mddev=/dev/md_lvm_test0
+
+cleanup_md() {
+    # sleeps offer hack to defeat: 'md: md127 still in use'
+    # see: https://bugzilla.redhat.com/show_bug.cgi?id=509908#c25
+    sleep 2
+    mdadm --stop $mddev || true
+    if [ -b "$mddev" ]; then
+        # mdadm doesn't always cleanup the device node
+       sleep 2
+       rm -f $mddev
+    fi
+}
+
+cleanup_md_and_teardown() {
+    cleanup_md
+    teardown
+}
+
+# create 2 disk MD raid0 array (stripe_width=128K)
+test -b "$mddev" && exit 200
+mdadm --create --metadata=1.0 $mddev --auto=md --level 0 --raid-devices=2 --chunk 64 $dev1 $dev2
+trap 'aux cleanup_md_and_teardown' EXIT # cleanup this MD device at the end of the test
+test -b "$mddev" || exit 200
+
+# Test alignment of PV on MD without any MD-aware or topology-aware detection
+# - should treat $mddev just like any other block device
+pv_align="1.00m"
+pvcreate --metadatasize 128k \
+    --config 'devices {md_chunk_alignment=0 data_alignment_detection=0 data_alignment_offset_detection=0}' \
+    $mddev
+check_pv_field_ $mddev pe_start $pv_align
+
+# Test md_chunk_alignment independent of topology-aware detection
+pv_align="1.00m"
+pvcreate --metadatasize 128k \
+    --config 'devices {data_alignment_detection=0 data_alignment_offset_detection=0}' \
+    $mddev
+check_pv_field_ $mddev pe_start $pv_align
+
+# Get linux minor version
+linux_minor=$(echo `uname -r` | cut -d'.' -f3 | cut -d'-' -f1)
+
+# Test newer topology-aware alignment detection
+# - first added to 2.6.31 but not "reliable" until 2.6.33
+if [ $linux_minor -ge 33 ]; then
+    pv_align="1.00m"
+    # optimal_io_size=131072, minimum_io_size=65536
+    pvcreate --metadatasize 128k \
+       --config 'devices { md_chunk_alignment=0 }' $mddev
+    check_pv_field_ $mddev pe_start $pv_align
+fi
+
+# partition MD array directly, depends on blkext in Linux >= 2.6.28
+if [ $linux_minor -ge 28 ]; then
+    # create one partition
+    sfdisk $mddev <<EOF
+,,83
+EOF
+    # make sure partition on MD is _not_ removed
+    # - tests partition -> parent lookup via sysfs paths
+    not pvcreate --metadatasize 128k $mddev
+
+    # verify alignment_offset is accounted for in pe_start
+    # - topology infrastructure is available in Linux >= 2.6.31
+    # - also tests partition -> parent lookup via sysfs paths
+
+    # Oh joy: need to lookup /sys/block/md127 rather than /sys/block/md_lvm_test0
+    mddev_maj_min=$(ls -lL $mddev | awk '{ print $5 $6 }' | perl -pi -e 's|,|:|')
+    mddev_p_sysfs_name=$(echo /sys/dev/block/${mddev_maj_min}/*p1)
+    base_mddev_p=`basename $mddev_p_sysfs_name`
+    mddev_p=/dev/${base_mddev_p}
+
+    # Checking for 'alignment_offset' in sysfs implies Linux >= 2.6.31
+    # but reliable alignment_offset support requires kernel.org Linux >= 2.6.33
+    sysfs_alignment_offset=/sys/dev/block/${mddev_maj_min}/${base_mddev_p}/alignment_offset
+    [ -f $sysfs_alignment_offset -a $linux_minor -ge 33 ] && \
+       alignment_offset=`cat $sysfs_alignment_offset` || \
+       alignment_offset=0
+
+    if [ $alignment_offset -gt 0 ]; then    
+        # default alignment is 1M, add alignment_offset
+       pv_align=$((1048576+$alignment_offset))B
+       pvcreate --metadatasize 128k $mddev_p
+       check_pv_field_ $mddev_p pe_start $pv_align "--units b"
+       pvremove $mddev_p
+    fi
+fi
+
+# Test newer topology-aware alignment detection w/ --dataalignment override
+if [ $linux_minor -ge 33 ]; then
+    cleanup_md
+    pvcreate -f $dev1
+    pvcreate -f $dev2
+
+    # create 2 disk MD raid0 array (stripe_width=2M)
+    test -b "$mddev" && exit 200
+    mdadm --create --metadata=1.0 $mddev --auto=md --level 0 --raid-devices=2 --chunk 1024 $dev1 $dev2
+    test -b "$mddev" || exit 200
+
+    # optimal_io_size=2097152, minimum_io_size=1048576
+    pv_align="2.00m"
+    pvcreate --metadatasize 128k \
+       --config 'devices { md_chunk_alignment=0 }' $mddev
+    check_pv_field_ $mddev pe_start $pv_align
+
+    # now verify pe_start alignment override using --dataalignment
+    pv_align="192.00k"
+    pvcreate --dataalignment 64k --metadatasize 128k \
+       --config 'devices { md_chunk_alignment=0 }' $mddev
+    check_pv_field_ $mddev pe_start $pv_align
+fi
diff --git a/test/t-pvcreate-operation.sh b/test/t-pvcreate-operation.sh
new file mode 100755 (executable)
index 0000000..2c94696
--- /dev/null
@@ -0,0 +1,121 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+for mdatype in 1 2
+do
+# pvcreate (lvm$mdatype) refuses to overwrite an mounted filesystem (bz168330)
+       test ! -d $TESTDIR/mnt && mkdir $TESTDIR/mnt
+       if mke2fs $dev1; then
+               mount $dev1 $TESTDIR/mnt
+               not pvcreate -M$mdatype $dev1 2>err
+               grep "Can't open $dev1 exclusively.  Mounted filesystem?" err
+               umount $dev1
+       fi
+
+# pvcreate (lvm$mdatype) succeeds when run repeatedly (pv not in a vg) (bz178216)
+    pvcreate -M$mdatype $dev1
+    pvcreate -M$mdatype $dev1
+    pvremove -f $dev1
+
+# pvcreate (lvm$mdatype) fails when PV belongs to VG" \
+    pvcreate -M$mdatype $dev1
+    vgcreate -M$mdatype $vg1 $dev1
+    not pvcreate -M$mdatype $dev1
+
+    vgremove -f $vg1
+    pvremove -f $dev1
+
+# pvcreate (lvm$mdatype) fails when PV1 does and PV2 does not belong to VG
+    pvcreate -M$mdatype $dev1
+    pvcreate -M$mdatype $dev2
+    vgcreate -M$mdatype $vg1 $dev1
+
+# pvcreate a second time on $dev2 and $dev1
+    not pvcreate -M$mdatype $dev2 $dev1
+
+    vgremove -f $vg1
+    pvremove -f $dev2
+    pvremove -f $dev1
+
+# NOTE: Force pvcreate after test completion to ensure clean device
+#test_expect_success \
+#  "pvcreate (lvm$mdatype) fails on md component device" \
+#  'mdadm -C -l raid0 -n 2 /dev/md0 $dev1 $dev2 &&
+#   pvcreate -M$mdatype $dev1;
+#   status=$?; echo status=$status; test $status != 0 &&
+#   mdadm --stop /dev/md0 &&
+#   pvcreate -ff -y -M$mdatype $dev1 $dev2 &&
+#   pvremove -f $dev1 $dev2'
+done
+
+# pvcreate (lvm2) fails without -ff when PV with metadatacopies=0 belongs to VG
+pvcreate --metadatacopies 0 $dev1
+pvcreate --metadatacopies 1 $dev2
+vgcreate $vg1 $dev1 $dev2
+not pvcreate $dev1
+vgremove -f $vg1
+pvremove -f $dev2
+pvremove -f $dev1
+
+# pvcreate (lvm2) succeeds with -ff when PV with metadatacopies=0 belongs to VG
+pvcreate --metadatacopies 0 $dev1 
+pvcreate --metadatacopies 1 $dev2
+vgcreate $vg1 $dev1 $dev2 
+pvcreate -ff -y $dev1 
+vgreduce --removemissing $vg1 
+vgremove -ff $vg1 
+pvremove -f $dev2 
+pvremove -f $dev1
+
+for i in 0 1 2 3
+do
+# pvcreate (lvm2) succeeds writing LVM label at sector $i
+    pvcreate --labelsector $i $dev1
+    dd if=$dev1 bs=512 skip=$i count=1 2>/dev/null | strings | grep -q LABELONE;
+    pvremove -f $dev1
+done
+
+# pvcreate (lvm2) fails writing LVM label at sector 4
+not pvcreate --labelsector 4 $dev1
+
+backupfile=$PREFIX.mybackupfile
+uuid1=freddy-fred-fred-fred-fred-fred-freddy
+uuid2=freddy-fred-fred-fred-fred-fred-fredie
+bogusuuid=fred
+
+# pvcreate rejects uuid option with less than 32 characters
+not pvcreate --norestorefile --uuid $bogusuuid $dev1
+
+# pvcreate rejects uuid option without restorefile
+not pvcreate --uuid $uuid1 $dev1
+
+# pvcreate rejects uuid already in use
+pvcreate --norestorefile --uuid $uuid1 $dev1
+not pvcreate --norestorefile --uuid $uuid1 $dev2
+
+# pvcreate rejects non-existent file given with restorefile
+not pvcreate --uuid $uuid1 --restorefile $backupfile $dev1
+
+# pvcreate rejects restorefile with uuid not found in file
+pvcreate --norestorefile --uuid $uuid1 $dev1
+vgcfgbackup -f $backupfile
+not pvcreate --uuid $uuid2 --restorefile $backupfile $dev2
+
+# pvcreate wipes swap signature when forced
+dd if=/dev/zero of=$dev1 bs=1024 count=64
+mkswap $dev1
+blkid -c /dev/null $dev1 | grep "swap"
+pvcreate -f $dev1
+# blkid cannot make up its mind whether not finding anything it knows is a failure or not
+(blkid -c /dev/null $dev1 || true) | not grep "swap"
diff --git a/test/t-pvcreate-usage.sh b/test/t-pvcreate-usage.sh
new file mode 100755 (executable)
index 0000000..35dc1c0
--- /dev/null
@@ -0,0 +1,191 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description='Test pvcreate option values'
+PAGESIZE=$(getconf PAGESIZE)
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+#COMM 'pvcreate rejects negative setphysicalvolumesize'
+not pvcreate --setphysicalvolumesize -1024 $dev1
+
+#COMM 'pvcreate rejects negative metadatasize'
+not pvcreate --metadatasize -1024 $dev1
+
+# x. metadatasize 0, defaults to 255
+# FIXME: unable to check default value, not in reporting cmds
+# should default to 255 according to code
+#   check_pv_field_ pv_mda_size 255 
+#COMM 'pvcreate accepts metadatasize 0'
+pvcreate --metadatasize 0 $dev1
+pvremove $dev1
+
+#Verify vg_mda_size is smaller pv_mda_size
+pvcreate --metadatasize 512k $dev1
+pvcreate --metadatasize 96k $dev2
+vgcreate $vg $dev1 $dev2
+compare_two_fields_ vgs $vg vg_mda_size pvs $dev2 pv_mda_size
+vgremove -ff $vg
+
+# x. metadatasize too large
+# For some reason we allow this, even though there's no room for data?
+##COMM  'pvcreate rejects metadatasize too large' 
+#not pvcreate --metadatasize 100000000000000 $dev1
+
+#COMM 'pvcreate rejects metadatacopies < 0'
+not pvcreate --metadatacopies -1 $dev1
+
+#COMM 'pvcreate accepts metadatacopies = 0, 1, 2'
+for j in metadatacopies pvmetadatacopies
+do
+pvcreate --$j 0 $dev1
+pvcreate --$j 1 $dev2
+pvcreate --$j 2 $dev3
+check_pv_field_ $dev1 pv_mda_count 0
+check_pv_field_ $dev2 pv_mda_count 1
+check_pv_field_ $dev3 pv_mda_count 2
+pvremove $dev1 
+pvremove $dev2 
+pvremove $dev3
+done
+
+#COMM 'pvcreate rejects metadatacopies > 2'
+not pvcreate --metadatacopies 3 $dev1
+
+#COMM 'pvcreate rejects invalid device'
+not pvcreate $dev1bogus
+
+#COMM 'pvcreate rejects labelsector < 0'
+not pvcreate --labelsector -1 $dev1
+
+#COMM 'pvcreate rejects labelsector > 1000000000000'
+not pvcreate --labelsector 1000000000000 $dev1
+
+# other possibilites based on code inspection (not sure how hard)
+# x. device too small (min of 512 * 1024 KB)
+# x. device filtered out
+# x. unable to open /dev/urandom RDONLY
+# x. device too large (pe_count > UINT32_MAX)
+# x. device read-only
+# x. unable to open device readonly
+# x. BLKGETSIZE64 fails
+# x. set size to value inconsistent with device / PE size
+
+#COMM 'pvcreate basic dataalignment sanity checks'
+not pvcreate --dataalignment -1 $dev1
+not pvcreate -M 1 --dataalignment 1 $dev1
+not pvcreate --dataalignment 1e $dev1
+
+#COMM 'pvcreate always rounded up to page size for start of device'
+#pvcreate --metadatacopies 0 --dataalignment 1 $dev1
+# amuse shell experts
+#check_pv_field_ $dev1 pe_start $(($(getconf PAGESIZE)/1024))".00k"
+
+#COMM 'pvcreate sets data offset directly'
+pvcreate --dataalignment 512k $dev1
+check_pv_field_ $dev1 pe_start 512.00k
+
+#COMM 'vgcreate/vgremove do not modify data offset of existing PV'
+vgcreate $vg $dev1  --config 'devices { data_alignment = 1024 }'
+check_pv_field_ $dev1 pe_start 512.00k
+vgremove $vg --config 'devices { data_alignment = 1024 }'
+check_pv_field_ $dev1 pe_start 512.00k
+
+#COMM 'pvcreate sets data offset next to mda area'
+pvcreate --metadatasize 100k --dataalignment 100k $dev1
+check_pv_field_ $dev1 pe_start 200.00k
+
+# metadata area start is aligned according to pagesize
+# pagesize should be 64k or 4k ...
+if [ $PAGESIZE -eq 65536 ] ; then
+       pv_align="192.50k"
+else
+       pv_align="133.00k"
+fi
+
+pvcreate --metadatasize 128k --dataalignment 3.5k $dev1
+check_pv_field_ $dev1 pe_start $pv_align
+
+pvcreate --metadatasize 128k --metadatacopies 2 --dataalignment 3.5k $dev1
+check_pv_field_ $dev1 pe_start $pv_align
+
+# data area is aligned to 1M by default,
+# data area start is shifted by the specified alignment_offset
+pv_align="1052160B" # 1048576 + (7*512)
+pvcreate --metadatasize 128k --dataalignmentoffset 7s $dev1
+check_pv_field_ $dev1 pe_start $pv_align "--units b"
+
+# 2nd metadata area is created without problems when
+# data area start is shifted by the specified alignment_offset
+pvcreate --metadatasize 128k --metadatacopies 2 --dataalignmentoffset 7s $dev1
+check_pv_field_ $dev1 pv_mda_count 2
+# FIXME: compare start of 2nd mda with and without --dataalignmentoffset
+
+#COMM 'pv with LVM1 compatible data alignment can be convereted'
+#compatible == LVM1_PE_ALIGN == 64k
+pvcreate --dataalignment 256k $dev1
+vgcreate -s 1m $vg $dev1
+vgconvert -M1 $vg
+vgconvert -M2 $vg
+check_pv_field_ $dev1 pe_start 256.00k
+vgremove $vg
+
+#COMM 'pv with LVM1 incompatible data alignment cannot be convereted'
+pvcreate --dataalignment 10k $dev1
+vgcreate -s 1m $vg $dev1
+not vgconvert -M1 $vg
+vgremove $vg
+
+#COMM 'vgcfgrestore allows pe_start=0'
+#basically it produces nonsense, but it tests vgcfgrestore,
+#not that final cfg is usable...
+pvcreate --metadatacopies 0 $dev1
+pvcreate $dev2
+vgcreate $vg $dev1 $dev2
+vgcfgbackup -f "$(pwd)/backup.$$" $vg
+sed 's/pe_start = [0-9]*/pe_start = 0/' "$(pwd)/backup.$$" > "$(pwd)/backup.$$1"
+vgcfgrestore -f "$(pwd)/backup.$$1" $vg
+check_pv_field_ $dev1 pe_start 0
+check_pv_field_ $dev2 pe_start 0
+vgremove $vg
+
+echo test pvcreate --metadataignore
+for pv_in_vg in 1 0; do
+for mdacp in 1 2; do
+for ignore in y n; do
+       echo pvcreate --metadataignore has proper mda_count and mda_used_count
+       pvcreate --metadatacopies $mdacp --metadataignore $ignore $dev1 $dev2
+       check_pv_field_ $dev1 pv_mda_count $mdacp
+       check_pv_field_ $dev2 pv_mda_count $mdacp
+       if [ $ignore = y ]; then
+               check_pv_field_ $dev1 pv_mda_used_count 0
+               check_pv_field_ $dev2 pv_mda_used_count 0
+       else
+               check_pv_field_ $dev1 pv_mda_used_count $mdacp
+               check_pv_field_ $dev2 pv_mda_used_count $mdacp
+       fi
+       echo vgcreate has proper vg_mda_count and vg_mda_used_count
+       if [ $pv_in_vg = 1 ]; then
+               vgcreate -c n "$vg" $dev1 $dev2
+               check_vg_field_ $vg vg_mda_count $(($mdacp * 2))
+               if [ $ignore = y ]; then
+                       check_vg_field_ $vg vg_mda_used_count 1
+               else
+                       check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+               fi
+               check_vg_field_ $vg vg_mda_copies unmanaged
+               vgremove $vg
+       fi
+done
+done
+done
diff --git a/test/t-pvmove-basic.sh b/test/t-pvmove-basic.sh
new file mode 100755 (executable)
index 0000000..44b533c
--- /dev/null
@@ -0,0 +1,374 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2007 NEC Corporation
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description="ensure that pvmove works with basic options"
+
+. ./test-utils.sh
+
+# ---------------------------------------------------------------------
+# Utilities
+
+lvdev_() {
+  echo "$DM_DEV_DIR/$1/$2"
+}
+
+lv_is_on_() {
+  local lv=$1 #allready vg/lv
+  shift 1
+  lvs -a -odevices --noheadings $lv | sed 's/,/\n/g' > out
+#is on all specified devs
+  for d in $*; do grep "$d(" out; done
+#isn't on any other dev (we are set -e remember)
+  for d in $*; do ! grep -v "$d(" out; done
+  return 0
+}
+
+save_dev_sum_() {
+  mkfs.ext3 $1 > /dev/null && md5sum $1 > md5.$(basename $1)
+}
+
+check_dev_sum_() {
+  md5sum $1 > md5.tmp && cmp md5.$(basename $1) md5.tmp
+}
+
+# ---------------------------------------------------------------------
+# Initialize PVs and VGs
+
+aux prepare_vg 5 80
+
+# ---------------------------------------------------------------------
+# Common environment setup/cleanup for each sub testcases
+
+prepare_lvs_() {
+  lvcreate -l2 -n $lv1 $vg $dev1 
+    lv_is_on_ $vg/$lv1 $dev1 
+  lvcreate -l9 -i3 -n $lv2 $vg $dev2 $dev3 $dev4 
+    lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev4 
+  lvextend -l+2 $vg/$lv1 $dev2 
+    lv_is_on_ $vg/$lv1 $dev1 $dev2 
+  lvextend -l+2 $vg/$lv1 $dev3 
+    lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev3 
+  lvextend -l+2 $vg/$lv1 $dev1 
+    lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev3 $dev1 
+  lvcreate -l1 -n $lv3 $vg $dev2 
+    lv_is_on_ $vg/$lv3 $dev2 
+  save_dev_sum_ $(lvdev_ $vg $lv1) 
+  save_dev_sum_ $(lvdev_ $vg $lv2) 
+  save_dev_sum_ $(lvdev_ $vg $lv3) 
+  lvs -a -o devices --noheadings $vg/$lv1 > ${lv1}_devs 
+  lvs -a -o devices --noheadings $vg/$lv2 > ${lv2}_devs 
+  lvs -a -o devices --noheadings $vg/$lv3 > ${lv3}_devs
+}
+
+lv_not_changed_() {
+  lvs -a -o devices --noheadings $1 > out
+  diff $(basename $1)_devs out
+}
+
+check_and_cleanup_lvs_() {
+  lvs -a -o+devices $vg
+  check_dev_sum_ $(lvdev_ $vg $lv1)
+  check_dev_sum_ $(lvdev_ $vg $lv2)
+  check_dev_sum_ $(lvdev_ $vg $lv3)
+  lvs -a -o name $vg > out && ! grep ^pvmove out
+  lvremove -ff $vg
+       if ! dmsetup table|not grep $vg; then
+               echo "ERROR: lvremove did leave some some mappings in DM behind!" && \
+                       return 1
+       fi
+       :
+}
+
+#COMM "check environment setup/cleanup"
+prepare_lvs_
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# pvmove tests
+
+# ---
+# filter by LV
+
+#COMM "only specified LV is moved: from pv2 to pv5 only for lv1"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv1 $dev2 $dev5 
+lv_is_on_ $vg/$lv1 $dev1 $dev5 $dev3 $dev1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# segments in a LV
+
+#COMM "the 1st seg of 3-segs LV is moved: from pv1 of lv1 to pv4"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv1 $dev1 $dev4 
+lv_is_on_ $vg/$lv1 $dev4 $dev2 $dev3 $dev4 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "the 2nd seg of 3-segs LV is moved: from pv2 of lv1 to pv4"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv1 $dev2 $dev4 
+lv_is_on_ $vg/$lv1 $dev1 $dev4 $dev3 $dev1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "the 3rd seg of 3-segs LV is moved: from pv3 of lv1 to pv4"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv1 $dev3 $dev4 
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev4 $dev1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# multiple LVs matching
+
+#COMM "1 out of 3 LVs is moved: from pv4 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev4 $dev5 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev5 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "2 out of 3 LVs are moved: from pv3 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev3 $dev5 
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev5 $dev1 
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "3 out of 3 LVs are moved: from pv2 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev2 $dev5 
+lv_is_on_ $vg/$lv1 $dev1 $dev5 $dev3 $dev1 
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4 
+lv_is_on_ $vg/$lv3 $dev5 
+check_and_cleanup_lvs_
+
+# ---
+# areas of striping
+
+#COMM "move the 1st stripe: from pv2 of lv2 to pv1"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv2 $dev2 $dev1 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev1 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "move the 2nd stripe: from pv3 of lv2 to pv1"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv2 $dev3 $dev1 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev2 $dev1 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "move the 3rd stripe: from pv4 of lv2 to pv1"
+prepare_lvs_ 
+pvmove -i1 -n $vg/$lv2 $dev4 $dev1 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev1 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# partial segment match (source segment splitted)
+
+#COMM "match to the start of segment:from pv2:0-0 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev2:0-0 $dev5 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev5 $dev2 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "match to the middle of segment: from pv2:1-1 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev2:1-1 $dev5 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev2 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "match to the end of segment: from pv2:2-2 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev2:2-2 $dev5 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# destination segment splitted
+
+#COMM "no destination split: from pv2:0-2 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev2:0-2 $dev5 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "destination split into 2: from pv2:0-2 to pv5:5-5 and pv4:5-6"
+prepare_lvs_ 
+pvmove -i1 --alloc anywhere $dev2:0-2 $dev5:5-5 $dev4:5-6 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev5 $dev4 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "destination split into 3: from pv2:0-2 to {pv3,4,5}:5-5"
+prepare_lvs_ 
+pvmove -i1 --alloc anywhere $dev2:0-2 $dev3:5-5 $dev4:5-5 $dev5:5-5 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev3 $dev4 $dev5 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# alloc policy (anywhere, contiguous) with both success and failure cases
+
+#COMM "alloc normal on same PV for source and destination: from pv3:0-2 to pv3:5-7" 
+prepare_lvs_ 
+not pvmove -i1 $dev3:0-2 $dev3:5-7
+# "(cleanup previous test)"
+lv_not_changed_ $vg/$lv1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "alloc anywhere on same PV for source and destination: from pv3:0-2 to pv3:5-7"
+prepare_lvs_ 
+pvmove -i1 --alloc anywhere $dev3:0-2 $dev3:5-7 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "alloc anywhere but better area available: from pv3:0-2 to pv3:5-7 or pv5:5-6,pv4:5-5"
+prepare_lvs_ 
+pvmove -i1 --alloc anywhere $dev3:0-2 $dev3:5-7 $dev5:5-6 $dev4:5-5 
+lv_not_changed_ $vg/$lv1 
+#lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev4 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "alloc contiguous but area not available: from pv2:0-2 to pv5:5-5 and pv4:5-6"
+prepare_lvs_ 
+not pvmove -i1 --alloc contiguous $dev2:0-2 $dev5:5-5 $dev4:5-6
+# "(cleanup previous test)"
+lv_not_changed_ $vg/$lv1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "alloc contiguous and contiguous area available: from pv2:0-2 to pv5:0-0,pv5:3-5 and pv4:5-6"
+prepare_lvs_ 
+pvmove -i1 --alloc contiguous $dev2:0-2 $dev5:0-0 $dev5:3-5 $dev4:5-6 
+lv_not_changed_ $vg/$lv1 
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# multiple segments in a LV
+
+#COMM "multiple source LVs: from pv3 to pv5"
+prepare_lvs_ 
+pvmove -i1 $dev3 $dev5 
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev5 
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---
+# move inactive LV
+
+#COMM "move inactive LV: from pv2 to pv5"
+prepare_lvs_ 
+lvchange -an $vg/$lv1 
+lvchange -an $vg/$lv3 
+pvmove -i1 $dev2 $dev5 
+lv_is_on_ $vg/$lv1 $dev1 $dev5 $dev3 
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4 
+lv_is_on_ $vg/$lv3 $dev5 
+check_and_cleanup_lvs_
+
+# ---
+# other failure cases
+
+#COMM "no PEs to move: from pv3 to pv1"
+prepare_lvs_ 
+pvmove -i1 $dev3 $dev1 
+not pvmove -i1 $dev3 $dev1
+# "(cleanup previous test)"
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev1 
+lv_is_on_ $vg/$lv2 $dev2 $dev1 $dev4 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "no space available: from pv2:0-0 to pv1:0-0" 
+prepare_lvs_ 
+not pvmove -i1 $dev2:0-0 $dev1:0-0
+# "(cleanup previous test)"
+lv_not_changed_ $vg/$lv1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM 'same source and destination: from pv1 to pv1'
+prepare_lvs_ 
+not pvmove -i1 $dev1 $dev1
+#"(cleanup previous test)"
+lv_not_changed_ $vg/$lv1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+#COMM "sum of specified destination PEs is large enough, but it includes source PEs and the free PEs are not enough"
+prepare_lvs_ 
+not pvmove --alloc anywhere $dev1:0-2 $dev1:0-2 $dev5:0-0 2> err
+#"(cleanup previous test)"
+grep "Insufficient free space" err 
+lv_not_changed_ $vg/$lv1 
+lv_not_changed_ $vg/$lv2 
+lv_not_changed_ $vg/$lv3 
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+
+#COMM "pvmove abort"
+prepare_lvs_ 
+pvmove -i100 -b $dev1 $dev3 
+pvmove --abort 
+check_and_cleanup_lvs_
+
+#COMM "pvmove out of --metadatacopies 0 PV (bz252150)"
+vgremove -ff $vg
+pvcreate $devs
+pvcreate --metadatacopies 0 $dev1 $dev2
+vgcreate -c n $vg $devs
+lvcreate -l4 -n $lv1 $vg $dev1
+pvmove $dev1
+
+#COMM "pvmove fails activating mirror, properly restores state before pvmove"
+dmsetup create "$vg-pvmove0" --notable
+not pvmove -i 1 $dev2
+test $(dmsetup info --noheadings -c -o suspended "$vg-$lv1") = "Active"
+dmsetup remove "$vg-pvmove0"
diff --git a/test/t-pvremove-usage.sh b/test/t-pvremove-usage.sh
new file mode 100755 (executable)
index 0000000..5b5700f
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_devs 3
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 2 $dev3
+pvremove $dev2
+
+# failing, but still removing everything what can be removed
+# is somewhat odd as default, what do we have -f for?
+pvs | not grep $dev2
+pvcreate  --metadatacopies 0 $dev2
+
+# check pvremove refuses to remove pv in a vg
+vgcreate -c n $vg $dev1 $dev2
+not pvremove $dev2 $dev3
+
+for mdacp in 0 1 2; do
+    # check pvremove truly wipes the label (pvscan wont find) (---metadatacopies $mdacp)
+    pvcreate --metadatacopies $mdacp $dev3
+    pvremove $dev3
+    # try to remove agail - should fail cleanly
+    not pvremove $dev3
+    pvscan | not grep $dev3
+
+       # bz179473 refuse to wipe non-PV device without -f
+    not pvremove $dev3
+    pvremove -f $dev3
+
+    # reset setup
+    vgremove -ff $vg
+    pvcreate --metadatacopies $mdacp $dev1
+    pvcreate $dev2
+    vgcreate $vg $dev1 $dev2 
+
+    # pvremove -f fails when pv in a vg (---metadatacopies $mdacp)
+    not pvremove -f $dev1
+    pvs $dev1
+
+    # pvremove -ff fails without confirmation when pv in a vg (---metadatacopies $mdacp)
+    echo n | not pvremove -ff $dev1
+
+    # pvremove -ff succeds with confirmation when pv in a vg (---metadatacopies $mdacp)
+    pvremove -ffy $dev1
+    not pvs $dev1
+
+    vgreduce --removemissing $vg
+    pvcreate --metadatacopies $mdacp $dev1
+    vgextend $vg $dev1
+
+    # pvremove -ff -y is sufficient when pv in a vg (---metadatacopies $mdacp)" '
+    echo n | pvremove -ff -y $dev1
+
+    vgreduce --removemissing $vg
+    pvcreate --metadatacopies $mdacp $dev1
+    vgextend $vg $dev1
+done
diff --git a/test/t-read-ahead.sh b/test/t-read-ahead.sh
new file mode 100755 (executable)
index 0000000..6130561
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# tests basic functionality of read-ahead and ra regressions
+#
+
+test_description='Test read-ahead functionality'
+
+. ./test-utils.sh
+
+
+get_lvs_() {
+   lvs --units s --nosuffix --noheadings -o $1 "$vg"/"$lv"
+}
+
+check_lvs_() {
+   case $(get_lvs_ $1) in
+    *$2) true ;;
+    *) false ;;
+   esac
+}
+
+aux prepare_vg 5
+
+#COMM "test various read ahead settings (bz450922)"
+lvcreate -n "$lv" -l 100%FREE -i5 -I256 "$vg"
+ra="$(get_lvs_ lv_kernel_read_ahead)"
+test "$(( ( $ra / 5 ) * 5 ))" -eq $ra
+lvdisplay "$vg"/"$lv"
+not lvchange -r auto "$vg"/"$lv" 2>&1 | grep auto
+check_lvs_ lv_read_ahead auto
+check_lvs_ lv_kernel_read_ahead 5120
+lvchange -r 640 "$vg/$lv"
+check_lvs_ lv_read_ahead 640
+lvremove -ff "$vg"
+
+#COMM "read ahead is properly inherited from underlying PV"
+blockdev --setra 768 $dev1
+vgscan
+lvcreate -n $lv -L4m $vg $dev1
+test $(blockdev --getra $DM_DEV_DIR/$vg/$lv) -eq 768
+lvremove -ff $vg
+
+# Check default, active/inactive values for read_ahead / kernel_read_ahead
+lvcreate -n $lv -l 50%FREE $vg
+lvchange -an $vg/$lv
+check_lv_field_ $vg/$lv lv_read_ahead auto
+check_lv_field_ $vg/$lv lv_kernel_read_ahead -1
+lvchange -r 512 $vg/$lv
+lvchange -ay $vg/$lv
+check_lv_field_ $vg/$lv lv_read_ahead 256.00k
+check_lv_field_ $vg/$lv lv_kernel_read_ahead 256.00k
+lvremove -ff $vg
diff --git a/test/t-snapshot-autoumount-dmeventd.sh b/test/t-snapshot-autoumount-dmeventd.sh
new file mode 100644 (file)
index 0000000..32659af
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# no automatic extensions please
+LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND="
+    snapshot_autoextend_percent = 0
+    snapshot_autoextend_threshold = 100"
+
+. ./test-utils.sh
+
+which mkfs.ext2 || exit 200
+
+prepare_lvmconf
+
+aux prepare_vg 2
+aux prepare_dmeventd
+
+lvcreate -l 8 -n base $vg
+mkfs.ext2 $DM_DEV_DIR/$vg/base
+
+lvcreate -s -l 4 -n snap $vg/base
+lvchange --monitor y $vg/snap
+
+mkdir mnt
+mount $DM_DEV_DIR/$vg/snap mnt
+mount
+cat /proc/mounts | grep $vg-snap
+
+dd if=/dev/zero of=mnt/file$1 bs=1M count=17
+sync
+sleep 10 # dmeventd only checks every 10 seconds :(
+
+cat /proc/mounts | not grep $vg-snap
diff --git a/test/t-snapshot-merge.sh b/test/t-snapshot-merge.sh
new file mode 100755 (executable)
index 0000000..72d96da
--- /dev/null
@@ -0,0 +1,132 @@
+#!/bin/sh
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+set -xv
+
+which mkfs.ext3 || exit 200
+
+. ./test-utils.sh
+
+lvdev_()
+{
+    echo "$DM_DEV_DIR/$1/$2"
+}
+
+snap_lv_name_() {
+    echo ${1}_snap
+}
+
+setup_merge() {
+    local VG_NAME=$1
+    local LV_NAME=$2
+    local NUM_EXTRA_SNAPS="$3"
+    test -z "$NUM_EXTRA_SNAPS" && NUM_EXTRA_SNAPS=0
+    local BASE_SNAP_LV_NAME=$(snap_lv_name_ $LV_NAME)
+
+    lvcreate -n $LV_NAME -l 50%FREE $VG_NAME
+    lvcreate -s -n $BASE_SNAP_LV_NAME -l 20%FREE ${VG_NAME}/${LV_NAME}
+    mkfs.ext3 $(lvdev_ $VG_NAME $LV_NAME)
+
+    if [ $NUM_EXTRA_SNAPS -gt 0 ]; then
+       for i in `seq 1 $NUM_EXTRA_SNAPS`; do
+           lvcreate -s -n ${BASE_SNAP_LV_NAME}_${i} -l 20%FREE ${VG_NAME}/${LV_NAME}
+       done
+    fi
+}
+
+aux prepare_vg 1 100
+
+
+# test full merge of a single LV
+setup_merge $vg $lv1
+# now that snapshot LV is created: test if snapshot-merge target is available
+$(dmsetup targets | grep -q snapshot-merge) || exit 200
+lvs -a
+# make sure lvconvert --merge requires explicit LV listing
+not lvconvert --merge 2>err
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+lvremove -f $vg/$lv1
+
+
+# test that an actively merging snapshot may not be removed
+setup_merge $vg $lv1
+lvconvert -i+100 --merge --background $vg/$(snap_lv_name_ $lv1)
+not lvremove -f $vg/$(snap_lv_name_ $lv1)
+lvremove -f $vg/$lv1
+
+
+# "onactivate merge" test
+setup_merge $vg $lv1
+lvs -a
+mkdir test_mnt
+mount $(lvdev_ $vg $lv1) test_mnt
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+# -- refresh LV while FS is still mounted (merge must not start),
+#    verify 'snapshot-origin' target is still being used
+lvchange --refresh $vg/$lv1
+umount test_mnt
+rm -r test_mnt
+dmsetup table ${vg}-${lv1} | grep -q " snapshot-origin "
+# -- refresh LV to start merge (now that FS is unmounted),
+#    an active merge uses the 'snapshot-merge' target
+lvchange --refresh $vg/$lv1
+dmsetup table ${vg}-${lv1} | grep -q " snapshot-merge "
+# -- don't care if merge is still active; lvremove at this point
+#    may test stopping an active merge
+lvremove -f $vg/$lv1
+
+
+# "onactivate merge" test
+# -- deactivate/remove after disallowed merge attempt, tests
+#    to make sure preload of origin's metadata is _not_ performed
+setup_merge $vg $lv1
+lvs -a
+mkdir test_mnt
+mount $(lvdev_ $vg $lv1) test_mnt
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+# -- refresh LV while FS is still mounted (merge must not start),
+#    verify 'snapshot-origin' target is still being used
+lvchange --refresh $vg/$lv1
+umount test_mnt
+rm -r test_mnt
+dmsetup table ${vg}-${lv1} | grep -q " snapshot-origin "
+lvremove -f $vg/$lv1
+
+
+# test multiple snapshot merge; tests copy out that is driven by merge
+setup_merge $vg $lv1 1
+lvs -a
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+lvremove -f $vg/$lv1
+
+
+# test merging multiple snapshots that share the same tag
+setup_merge $vg $lv1
+setup_merge $vg $lv2
+lvs -a
+lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv1)
+lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv2)
+lvconvert --merge @this_is_a_test
+lvs | not grep $(snap_lv_name_ $lv1)
+lvs | not grep $(snap_lv_name_ $lv2)
+lvremove -f $vg/$lv1
+lvremove -f $vg/$lv2
+
+# FIXME following tests would need to poll merge progress, via periodic lvs?
+# Background processes don't lend themselves to lvm testsuite...
+
+# test: onactivate merge of a single lv
+
+# test: do onactivate, deactivate the origin LV, reactivate the LV, merge should resume
+
+# test: multiple onactivate merge
+
+
+vgremove -f "$vg"
diff --git a/test/t-snapshots-of-mirrors.sh b/test/t-snapshots-of-mirrors.sh
new file mode 100644 (file)
index 0000000..fbde102
--- /dev/null
@@ -0,0 +1,44 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_vg 4
+
+# Create snapshot of a mirror origin
+lvcreate -m 1 -L 10M -n lv $vg
+lvcreate -s $vg/lv -L 10M -n snap
+
+# Down-convert (mirror -> linear) under a snapshot
+lvconvert -m0 $vg/lv
+
+# Up-convert (linear -> mirror)
+lvconvert -m2 $vg/lv
+
+# Down-convert (mirror -> mirror)
+lvconvert -m1 $vg/lv
+
+# Up-convert (mirror -> mirror) -- Not supported!
+not lvconvert -m2 $vg/lv
+
+# Log conversion (disk -> core)
+lvconvert --mirrorlog core $vg/lv
+
+# Log conversion (core -> mirrored)
+lvconvert --mirrorlog mirrored $vg/lv
+
+# Log conversion (mirrored -> core)
+lvconvert --mirrorlog core $vg/lv
+
+# Log conversion (core -> disk)
+lvconvert --mirrorlog disk $vg/lv
+
+# Clean-up
+lvremove -ff $vg
diff --git a/test/t-tags.sh b/test/t-tags.sh
new file mode 100755 (executable)
index 0000000..906181c
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_pvs 5
+
+# vgcreate with --addtag
+vgcreate -c n --addtag firstvg $vg1 $dev1 $dev2
+vgcreate -c n --addtag secondvg $vg2 $dev3 $dev4
+check_vg_field_ $vg1 tags firstvg
+check_vg_field_ $vg2 tags secondvg
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+# vgchange with --addtag and --deltag
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+vgchange --addtag firstvgtag1 $vg1
+# adding a tag multiple times is not an error
+vgchange --addtag firstvgtag2 $vg1
+vgchange --addtag firstvgtag2 $vg1
+vgchange --addtag firstvgtag3 $vg1
+vgchange --addtag secondvgtag1 $vg2
+vgchange --addtag secondvgtag2 $vg2
+vgchange --addtag secondvgtag3 $vg2
+check_vg_field_ @firstvgtag2 tags "firstvgtag1,firstvgtag2,firstvgtag3"
+check_vg_field_ @secondvgtag1 tags "secondvgtag1,secondvgtag2,secondvgtag3"
+vgchange --deltag firstvgtag2 $vg1
+check_vg_field_ @firstvgtag1 tags "firstvgtag1,firstvgtag3"
+# deleting a tag multiple times is not an error
+vgchange --deltag firstvgtag2 $vg1
+vgchange --deltag firstvgtag1 $vg2
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+# lvcreate with --addtag
+vgcreate -c n $vg1 $dev1 $dev2
+lvcreate --addtag firstlvtag1 -l 4 -n $lv1 $vg1
+lvcreate --addtag secondlvtag1 -l 4 -n $lv2 $vg1
+check_lv_field_ @firstlvtag1 tags "firstlvtag1"
+not check_lv_field_ @secondlvtag1 tags "firstlvtag1"
+check_lv_field_ $vg1/$lv2 tags "secondlvtag1"
+not check_lv_field_ $vg1/$lv1 tags "secondlvtag1"
+vgremove -ff $vg1
+
+# lvchange with --addtag and --deltag
+vgcreate -c n $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv2 $vg1
+lvchange --addtag firstlvtag1 $vg1/$lv1
+# adding a tag multiple times is not an error
+lvchange --addtag firstlvtag2 $vg1/$lv1
+lvchange --addtag firstlvtag2 $vg1/$lv1
+lvchange --addtag firstlvtag3 $vg1/$lv1
+lvchange --addtag secondlvtag1 $vg1/$lv2
+lvchange --addtag secondlvtag2 $vg1/$lv2
+lvchange --addtag secondlvtag3 $vg1/$lv2
+check_lv_field_ $vg1/$lv1 tags "firstlvtag1,firstlvtag2,firstlvtag3"
+not $(check_lv_field_ $vg1/$lv1 tags "secondlvtag1")
+check_lv_field_ $vg1/$lv2 tags "secondlvtag1,secondlvtag2,secondlvtag3"
+not $(check_lv_field_ $vg1/$lv1 tags "secondlvtag1")
+# deleting a tag multiple times is not an error
+lvchange --deltag firstlvtag2 $vg1/$lv1
+lvchange --deltag firstlvtag2 $vg1/$lv1
+check_lv_field_ $vg1/$lv1 tags "firstlvtag1,firstlvtag3"
+check_lv_field_ $vg1/$lv2 tags "secondlvtag1,secondlvtag2,secondlvtag3"
diff --git a/test/t-test-partition.sh b/test/t-test-partition.sh
new file mode 100644 (file)
index 0000000..45a0aba
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# Testcase for bugzilla #621173 
+# excercises partition table scanning code path
+#
+
+which sfdisk || exit 200
+
+LVM_TEST_CONFIG_DEVICES="types = [\"device-mapper\", 142]"
+
+. ./test-utils.sh
+
+aux prepare_pvs 1 30
+
+pvs
+
+# create small partition table
+echo "1 2" | sfdisk $dev1
+
+pvs
diff --git a/test/t-topology-support.sh b/test/t-topology-support.sh
new file mode 100644 (file)
index 0000000..b25ed7e
--- /dev/null
@@ -0,0 +1,104 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+which mkfs.ext3 || exit 200
+
+# Get linux minor version
+linux_minor=$(echo `uname -r` | cut -d'.' -f3 | cut -d'-' -f1)
+
+test $linux_minor -ge 31 || exit 200
+
+. ./test-utils.sh
+
+check_logical_block_size()
+{
+    local DEV_=$1
+    local LOGICAL_BS=$2
+    # Verify logical_block_size - requires Linux >= 2.6.31
+    SYSFS_LOGICAL_BLOCK_SIZE=`echo /sys/block/$(basename $DEV_)/queue/logical_block_size`
+    if [ -f "$SYSFS_LOGICAL_BLOCK_SIZE" ] ; then
+       ACTUAL_LOGICAL_BLOCK_SIZE=`cat $SYSFS_LOGICAL_BLOCK_SIZE`
+       test $ACTUAL_LOGICAL_BLOCK_SIZE = $LOGICAL_BS
+    fi
+}
+
+lvdev_()
+{
+    echo "$DM_DEV_DIR/$1/$2"
+}
+
+test_snapshot_mount()
+{
+    lvcreate -L 16M -n $lv1 $vg $dev1
+    mkfs.ext3 $(lvdev_ $vg $lv1)
+    mkdir test_mnt
+    mount $(lvdev_ $vg $lv1) test_mnt
+    lvcreate -L 16M -n $lv2 -s $vg/$lv1
+    umount test_mnt
+    # mount the origin
+    mount $(lvdev_ $vg $lv1) test_mnt
+    umount test_mnt
+    # mount the snapshot
+    mount $(lvdev_ $vg $lv2) test_mnt
+    umount test_mnt
+    rm -r test_mnt
+    vgchange -an $vg
+    lvremove -f $vg/$lv2
+    lvremove -f $vg/$lv1
+}
+
+# FIXME add more topology-specific tests and validation (striped LVs, etc)
+
+NUM_DEVS=1
+PER_DEV_SIZE=34
+DEV_SIZE=$(($NUM_DEVS*$PER_DEV_SIZE))
+
+# ---------------------------------------------
+# Create "desktop-class" 4K drive
+# (logical_block_size=512, physical_block_size=4096, alignment_offset=0):
+LOGICAL_BLOCK_SIZE=512
+prepare_scsi_debug_dev $DEV_SIZE \
+    sector_size=$LOGICAL_BLOCK_SIZE physblk_exp=3
+check_logical_block_size $SCSI_DEBUG_DEV $LOGICAL_BLOCK_SIZE
+
+aux prepare_pvs $NUM_DEVS $PER_DEV_SIZE
+vgcreate -c n $vg $devs
+test_snapshot_mount
+vgremove $vg
+
+cleanup_scsi_debug_dev
+
+# ---------------------------------------------
+# Create "desktop-class" 4K drive w/ 63-sector DOS partition compensation
+# (logical_block_size=512, physical_block_size=4096, alignment_offset=3584):
+LOGICAL_BLOCK_SIZE=512
+prepare_scsi_debug_dev $DEV_SIZE \
+    sector_size=$LOGICAL_BLOCK_SIZE physblk_exp=3 lowest_aligned=7
+check_logical_block_size $SCSI_DEBUG_DEV $LOGICAL_BLOCK_SIZE
+
+aux prepare_pvs $NUM_DEVS $PER_DEV_SIZE
+vgcreate -c n $vg $devs
+test_snapshot_mount
+vgremove $vg
+
+cleanup_scsi_debug_dev
+
+# ---------------------------------------------
+# Create "enterprise-class" 4K drive
+# (logical_block_size=4096, physical_block_size=4096, alignment_offset=0):
+LOGICAL_BLOCK_SIZE=4096
+prepare_scsi_debug_dev $DEV_SIZE \
+    sector_size=$LOGICAL_BLOCK_SIZE
+check_logical_block_size $SCSI_DEBUG_DEV $LOGICAL_BLOCK_SIZE
+
+aux prepare_pvs $NUM_DEVS $PER_DEV_SIZE
+vgcreate -c n $vg $devs
+test_snapshot_mount
+vgremove $vg
diff --git a/test/t-unknown-segment.sh b/test/t-unknown-segment.sh
new file mode 100644 (file)
index 0000000..74a2710
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_vg 4
+
+lvcreate -l 1 -n $lv1 $vg
+lvcreate -l 2 -m 1 -n $lv2 $vg
+
+vgcfgbackup -f bak0 $vg
+sed -e 's,striped,unstriped,;s,mirror,unmirror,' -i.orig bak0
+vgcfgrestore -f bak0 $vg
+
+# we have on-disk metadata with unknown segments now
+not lvchange -a y $vg/$lv1 # check that activation is refused
+
+vgcfgbackup -f bak1 $vg
+cat bak1
+sed -e 's,unstriped,striped,;s,unmirror,mirror,' -i.orig bak1
+vgcfgrestore -f bak1 $vg
+vgcfgbackup -f bak2 $vg
+
+egrep -v 'description|seqno|creation_time|Generated' < bak0.orig > a
+egrep -v 'description|seqno|creation_time|Generated' < bak2 > b
+diff -u a b
diff --git a/test/t-unlost-pv.sh b/test/t-unlost-pv.sh
new file mode 100644 (file)
index 0000000..7a120fb
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_vg 3
+
+lvcreate -m 1 -l 1 -n mirror $vg
+lvchange -a n $vg/mirror
+
+check() {
+vgscan 2>&1 | tee vgscan.out
+grep "Inconsistent metadata found for VG $vg" vgscan.out
+vgscan 2>&1 | tee vgscan.out
+not grep "Inconsistent metadata found for VG $vg" vgscan.out
+}
+
+# try orphaning a missing PV (bz45867)
+disable_dev $dev1
+vgreduce --removemissing --force $vg
+enable_dev $dev1
+check
+
+# try to just change metadata; we expect the new version (with MISSING_PV set
+# on the reappeared volume) to be written out to the previously missing PV
+vgextend $vg $dev1
+disable_dev $dev1
+lvremove $vg/mirror
+enable_dev $dev1
+check
diff --git a/test/t-vgcfgbackup-usage.sh b/test/t-vgcfgbackup-usage.sh
new file mode 100644 (file)
index 0000000..72b37bb
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_pvs 4
+
+# vgcfgbackup handles similar VG names (bz458941)
+vg1=${PREFIX}vg00
+vg2=${PREFIX}vg01
+vgcreate $vg1 $dev1
+vgcreate $vg2 $dev2
+vgcfgbackup -f $TESTDIR/bak-%s >out
+grep "Volume group \"$vg1\" successfully backed up." out
+grep "Volume group \"$vg2\" successfully backed up." out
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+# vgcfgbackup correctly stores metadata with missing PVs
+# and vgcfgrestore able to restore them when device reappears
+pv1_uuid=$(pvs --noheadings -o pv_uuid $dev1)
+pv2_uuid=$(pvs --noheadings -o pv_uuid $dev2)
+vgcreate $vg $devs
+lvcreate -l1 -n $lv1 $vg $dev1
+lvcreate -l1 -n $lv2 $vg $dev2
+lvcreate -l1 -n $lv3 $vg $dev3
+vgchange -a n $vg
+pvcreate -ff -y $dev1
+pvcreate -ff -y $dev2
+vgcfgbackup -f "$(pwd)/backup.$$" $vg
+sed 's/flags = \[\"MISSING\"\]/flags = \[\]/' "$(pwd)/backup.$$" > "$(pwd)/backup.$$1"
+pvcreate -ff -y --norestorefile -u $pv1_uuid $dev1
+pvcreate -ff -y --norestorefile -u $pv2_uuid $dev2
+vgcfgrestore -f "$(pwd)/backup.$$1" $vg
+vgremove -ff $vg
+
+# vgcfgbackup correctly stores metadata LVM1 with missing PVs
+# FIXME: clvmd seems to have problem with metadata format change here
+# fix it and remove this vgscan
+vgscan
+pvcreate -M1 $devs
+vgcreate -M1 -c n $vg $devs
+lvcreate -l1 -n $lv1 $vg $dev1
+pvremove -ff -y $dev2
+not lvcreate -l1 -n $lv1 $vg $dev3
+vgcfgbackup -f "$(pwd)/backup.$$" $vg
diff --git a/test/t-vgchange-maxlv.sh b/test/t-vgchange-maxlv.sh
new file mode 100644 (file)
index 0000000..0148315
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_dmeventd
+aux prepare_pvs 3
+
+vgcreate -c n -l 2 $vg $dev1 $dev2 $dev3
+lvcreate -n one -l 1 $vg
+lvcreate -n two -l 1 $vg
+not lvcreate -n three -l 1 $vg
+vgchange -an $vg
+vgremove -ff $vg
+
+vgcreate -c n -l 3 $vg $dev1 $dev2 $dev3
+lvcreate -n one -l 1 $vg
+lvcreate -n snap -s -l 1 $vg/one
+lvcreate -n two -l 1 $vg
+not lvcreate -n three -l 1 $vg
+vgchange --monitor y $vg
+vgchange -an $vg 2>&1 | tee vgchange.out
+not grep "event server" vgchange.out
diff --git a/test/t-vgchange-usage.sh b/test/t-vgchange-usage.sh
new file mode 100644 (file)
index 0000000..4baaab3
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description='Exercise some vgchange diagnostics'
+
+. ./test-utils.sh
+
+aux prepare_pvs 3
+pvcreate --metadatacopies 0 $dev1
+vgcreate $vg $devs
+
+vgdisplay $vg
+
+# vgchange -p MaxPhysicalVolumes (bz202232)
+aux check_vg_field_ $vg max_pv 0
+vgchange -p 128 $vg
+aux check_vg_field_ $vg max_pv 128
+
+pv_count=$(get_vg_field $vg pv_count)
+not vgchange -p 2 $vg 2>err
+grep "MaxPhysicalVolumes is less than the current number $pv_count of PVs for" err
+aux check_vg_field_ $vg max_pv 128
+
+# vgchange -l MaxLogicalVolumes
+aux check_vg_field_ $vg max_lv 0
+vgchange -l 128 $vg
+aux check_vg_field_ $vg max_lv 128
+
+lvcreate -l4 -n$lv1 $vg
+lvcreate -l4 -n$lv2 $vg
+
+lv_count=$(get_vg_field $vg lv_count)
+not vgchange -l 1 $vg 2>err
+grep "MaxLogicalVolume is less than the current number $lv_count of LVs for"  err
+aux check_vg_field_ $vg max_lv 128
+
diff --git a/test/t-vgcreate-usage.sh b/test/t-vgcreate-usage.sh
new file mode 100755 (executable)
index 0000000..9f1cd82
--- /dev/null
@@ -0,0 +1,163 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description='Exercise some vgcreate diagnostics'
+
+. ./test-utils.sh
+
+aux prepare_devs 3
+pvcreate $dev1 $dev2
+pvcreate --metadatacopies 0 $dev3
+
+vg=${PREFIX}vg
+
+#COMM 'vgcreate accepts 8.00m physicalextentsize for VG'
+vgcreate -c n $vg --physicalextentsize 8.00m $dev1 $dev2
+check_vg_field_ $vg vg_extent_size 8.00m
+vgremove $vg
+# try vgck and to remove it again - should fail (but not segfault)
+not vgremove $vg
+not vgck $vg
+
+#COMM 'vgcreate accepts smaller (128) maxlogicalvolumes for VG'
+vgcreate -c n $vg --maxlogicalvolumes 128 $dev1 $dev2 
+check_vg_field_ $vg max_lv 128 
+vgremove $vg
+
+#COMM 'vgcreate accepts smaller (128) maxphysicalvolumes for VG'
+vgcreate -c n $vg --maxphysicalvolumes 128 $dev1 $dev2
+check_vg_field_ $vg max_pv 128
+vgremove $vg
+
+#COMM 'vgcreate rejects a zero physical extent size'
+not vgcreate -c n --physicalextentsize 0 $vg $dev1 $dev2 2>err
+grep "^  Physical extent size may not be zero\$" err
+
+#COMM 'vgcreate rejects "inherit" allocation policy'
+not vgcreate -c n --alloc inherit $vg $dev1 $dev2 2>err
+grep "^  Volume Group allocation policy cannot inherit from anything\$" err
+
+#COMM 'vgcreate rejects vgname "."'
+vginvalid=.; 
+not vgcreate -c n $vginvalid $dev1 $dev2 2>err
+grep "New volume group name \"$vginvalid\" is invalid\$" err
+
+#COMM 'vgcreate rejects vgname greater than 128 characters'
+vginvalid=thisnameisridiculouslylongtotestvalidationcodecheckingmaximumsizethisiswhathappenswhenprogrammersgetboredandorarenotcreativedonttrythisathome
+not vgcreate -c n $vginvalid $dev1 $dev2 2>err
+grep "New volume group name \"$vginvalid\" is invalid\$" err
+
+#COMM 'vgcreate rejects already existing vgname "/tmp/$vg"'
+#touch /tmp/$vg
+#not vgcreate $vg $dev1 $dev2 2>err
+#grep "New volume group name \"$vg\" is invalid\$" err
+
+#COMM "vgcreate rejects repeated invocation (run 2 times) (bz178216)"
+vgcreate -c n $vg $dev1 $dev2
+not vgcreate -c n $vg $dev1 $dev2
+vgremove -ff $vg
+
+#COMM 'vgcreate rejects MaxLogicalVolumes > 255'
+not vgcreate -c n --metadatatype 1 --maxlogicalvolumes 1024 $vg $dev1 $dev2 2>err
+grep "^  Number of volumes may not exceed 255\$" err
+
+#COMM "vgcreate fails when the only pv has --metadatacopies 0"
+not vgcreate -c n $vg $dev3
+
+# Test default (4MB) vg_extent_size as well as limits of extent_size
+not vgcreate -c n --physicalextentsize 0k $vg $dev1 $dev2
+vgcreate -c n --physicalextentsize 1k $vg $dev1 $dev2
+check_vg_field_ $vg vg_extent_size 1.00k
+vgremove -ff $vg
+not vgcreate -c n --physicalextentsize 3K $vg $dev1 $dev2
+not vgcreate -c n --physicalextentsize 1024t $vg $dev1 $dev2
+#not vgcreate --physicalextentsize 1T $vg $dev1 $dev2
+# FIXME: vgcreate allows physicalextentsize larger than pv size!
+
+# Test default max_lv, max_pv, extent_size, alloc_policy, clustered
+vgcreate -c n $vg $dev1 $dev2
+check_vg_field_ $vg vg_extent_size 4.00m
+check_vg_field_ $vg max_lv 0
+check_vg_field_ $vg max_pv 0
+check_vg_field_ $vg vg_attr "wz--n-"
+vgremove -ff $vg
+
+# Implicit pvcreate tests, test pvcreate options on vgcreate
+# --force, --yes, --metadata{size|copies|type}, --zero
+# --dataalignment[offset]
+pvremove $dev1 $dev2
+vgcreate -c n --force --yes --zero y $vg $dev1 $dev2
+vgremove -f $vg
+pvremove -f $dev1
+
+for i in 0 1 2 3
+do
+# vgcreate (lvm2) succeeds writing LVM label at sector $i
+    vgcreate -c n --labelsector $i $vg $dev1
+    dd if=$dev1 bs=512 skip=$i count=1 2>/dev/null | strings | grep -q LABELONE;
+    vgremove -f $vg
+    pvremove -f $dev1
+done
+
+# pvmetadatacopies
+for i in 1 2
+do
+    vgcreate -c n --pvmetadatacopies $i $vg $dev1
+    check_pv_field_ $dev1 pv_mda_count $i
+    vgremove -f $vg
+    pvremove -f $dev1
+done
+not vgcreate -c n --pvmetadatacopies 0 $vg $dev1
+pvcreate --metadatacopies 1 $dev2
+vgcreate -c n --pvmetadatacopies 0 $vg $dev1 $dev2
+check_pv_field_ $dev1 pv_mda_count 0
+check_pv_field_ $dev2 pv_mda_count 1
+vgremove -f $vg
+pvremove -f $dev1
+
+# metadatasize, dataalignment, dataalignmentoffset
+#COMM 'pvcreate sets data offset next to mda area'
+vgcreate -c n --metadatasize 100k --dataalignment 100k $vg $dev1
+check_pv_field_ $dev1 pe_start 200.00k
+vgremove -f $vg
+pvremove -f $dev1
+
+# data area is aligned to 1M by default,
+# data area start is shifted by the specified alignment_offset
+pv_align="1052160B" # 1048576 + (7*512)
+vgcreate -c n --metadatasize 128k --dataalignmentoffset 7s $vg $dev1
+check_pv_field_ $dev1 pe_start $pv_align "--units b"
+vgremove -f $vg
+pvremove -f $dev1
+
+# metadatatype
+for i in 1 2
+do
+    vgcreate -c n -M $i $vg $dev1
+    check_vg_field_ $vg vg_fmt lvm$i
+    vgremove -f $vg
+    pvremove -f $dev1
+done
+
+# vgcreate fails if pv belongs to existing vg
+vgcreate -c n $vg1 $dev1 $dev2
+not vgcreate $vg2 $dev2
+vgremove -f $vg1
+pvremove -f $dev1 $dev2
+
+# all PVs exist in the VG after created
+pvcreate $dev1
+vgcreate -c n $vg1 $dev1 $dev2 $dev3
+check_pv_field_ $dev1 vg_name $vg1
+check_pv_field_ $dev2 vg_name $vg1
+check_pv_field_ $dev3 vg_name $vg1
+vgremove -f $vg1
+pvremove -f $dev1 $dev2 $dev3
diff --git a/test/t-vgextend-restoremissing.sh b/test/t-vgextend-restoremissing.sh
new file mode 100644 (file)
index 0000000..87ff954
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+
+aux prepare_vg 3
+lvcreate -m 1 -l 1 -n mirror $vg
+lvchange -a n $vg/mirror
+lvcreate -l 1 -n lv1 $vg $dev1
+
+# try to just change metadata; we expect the new version (with MISSING_PV set
+# on the reappeared volume) to be written out to the previously missing PV
+disable_dev $dev1
+lvremove $vg/mirror
+enable_dev $dev1
+not vgck $vg 2>&1 | tee log
+grep "missing 1 physical volume" log
+not lvcreate -m 1 -l 1 -n mirror $vg # write operations fail
+vgextend --restore $vg $dev1 # restore the missing device
+vgck $vg
+lvcreate -m 1 -l 1 -n mirror $vg
diff --git a/test/t-vgextend-usage.sh b/test/t-vgextend-usage.sh
new file mode 100644 (file)
index 0000000..eda8904
--- /dev/null
@@ -0,0 +1,129 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+#
+# Exercise various vgextend commands
+#
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+for mdatype in 1 2
+do
+
+# Explicit pvcreate
+pvcreate -M$mdatype $dev1 $dev2 $dev3 $dev4 $dev5
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgextend $vg1 $dev3 $dev4 $dev5
+vgremove -ff $vg1
+
+# Implicit pvcreate
+pvremove $dev1 $dev2 $dev3 $dev4 $dev5
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgextend -M$mdatype $vg1 $dev3 $dev4 $dev5
+vgremove -ff $vg1
+pvremove $dev1 $dev2 $dev3 $dev4 $dev5
+
+done
+
+# Implicit pvcreate tests, test pvcreate options on vgcreate
+# --force, --yes, --metadata{size|copies|type}, --zero
+# --dataalignment[offset]
+vgcreate $vg $dev2
+vgextend --force --yes --zero y $vg $dev1
+vgreduce $vg $dev1
+pvremove -f $dev1
+
+for i in 0 1 2 3
+do
+# vgcreate (lvm2) succeeds writing LVM label at sector $i
+    vgextend --labelsector $i $vg $dev1
+    dd if=$dev1 bs=512 skip=$i count=1 2>/dev/null | strings | grep -q LABELONE;
+    vgreduce $vg $dev1
+    pvremove -f $dev1
+done
+
+# pvmetadatacopies
+for i in 0 1 2
+do
+    vgextend --pvmetadatacopies $i $vg $dev1
+    check_pv_field_ $dev1 pv_mda_count $i
+    vgreduce $vg $dev1
+    pvremove -f $dev1
+done
+
+# metadatasize, dataalignment, dataalignmentoffset
+#COMM 'pvcreate sets data offset next to mda area'
+vgextend --metadatasize 100k --dataalignment 100k $vg $dev1
+check_pv_field_ $dev1 pe_start 200.00k
+vgreduce $vg $dev1
+pvremove -f $dev1
+
+# data area is aligned to 1M by default,
+# data area start is shifted by the specified alignment_offset
+pv_align="1052160B" # 1048576 + (7*512)
+vgextend --metadatasize 128k --dataalignmentoffset 7s $vg $dev1
+check_pv_field_ $dev1 pe_start $pv_align "--units b"
+vgremove -f $vg
+pvremove -f $dev1
+
+# vgextend fails if pv belongs to existing vg
+vgcreate $vg1 $dev1 $dev3
+vgcreate $vg2 $dev2
+not vgextend $vg2 $dev3
+vgremove -f $vg1
+vgremove -f $vg2
+pvremove -f $dev1 $dev2 $dev3
+
+#vgextend fails if vg is not resizeable
+vgcreate $vg1 $dev1 $dev2
+vgchange --resizeable n $vg1
+not vgextend $vg1 $dev3
+vgremove -f $vg1
+pvremove -f $dev1 $dev2
+
+# all PVs exist in the VG after extended
+pvcreate $dev1
+vgcreate $vg1 $dev2
+vgextend $vg1 $dev1 $dev3
+check_pv_field_ $dev1 vg_name $vg1
+check_pv_field_ $dev2 vg_name $vg1
+check_pv_field_ $dev3 vg_name $vg1
+vgremove -f $vg1
+pvremove -f $dev1 $dev2 $dev3
+
+echo test vgextend --metadataignore
+for mdacp in 1 2; do
+for ignore in y n; do
+       echo vgextend --metadataignore has proper mda_count and mda_used_count
+       vgcreate $vg $dev3
+       vgextend --metadataignore $ignore --pvmetadatacopies $mdacp $vg $dev1 $dev2
+       check_pv_field_ $dev1 pv_mda_count $mdacp
+       check_pv_field_ $dev2 pv_mda_count $mdacp
+       if [ $ignore = y ]; then
+               check_pv_field_ $dev1 pv_mda_used_count 0
+               check_pv_field_ $dev2 pv_mda_used_count 0
+       else
+               check_pv_field_ $dev1 pv_mda_used_count $mdacp
+               check_pv_field_ $dev2 pv_mda_used_count $mdacp
+       fi
+       echo vg has proper vg_mda_count and vg_mda_used_count
+       check_vg_field_ $vg vg_mda_count $(($mdacp * 2 + 1))
+       if [ $ignore = y ]; then
+               check_vg_field_ $vg vg_mda_used_count 1
+       else
+               check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2 + 1))
+       fi
+       check_vg_field_ $vg vg_mda_copies unmanaged
+       vgremove $vg
+       pvremove -ff $dev1 $dev2 $dev3
+done
+done
diff --git a/test/t-vgmerge-operation.sh b/test/t-vgmerge-operation.sh
new file mode 100755 (executable)
index 0000000..3c7121b
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+# Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+test_description='Test vgmerge operation'
+
+. ./test-utils.sh
+
+aux prepare_pvs 4 64
+
+# 'vgmerge succeeds with single linear LV in source VG'
+vgcreate -c n $vg1 $dev1 $dev2 
+vgcreate -c n $vg2 $dev3 $dev4 
+lvcreate -l 4 -n $lv1 $vg1 $dev1 
+vgchange -an $vg1 
+vg_validate_pvlv_counts_ $vg1 2 1 0 
+vg_validate_pvlv_counts_ $vg2 2 0 0 
+vgmerge $vg2 $vg1 
+vg_validate_pvlv_counts_ $vg2 4 1 0 
+vgremove -f $vg2
+
+# 'vgmerge succeeds with single linear LV in source and destination VG'
+vgcreate -c n $vg1 $dev1 $dev2 
+vgcreate -c n $vg2 $dev3 $dev4 
+lvcreate -l 4 -n $lv1 $vg1 
+lvcreate -l 4 -n $lv2 $vg2 
+vgchange -an $vg1 
+vgchange -an $vg2 
+vg_validate_pvlv_counts_ $vg1 2 1 0 
+vg_validate_pvlv_counts_ $vg2 2 1 0 
+vgmerge $vg2 $vg1 
+vg_validate_pvlv_counts_ $vg2 4 2 0 
+vgremove -f $vg2
+
+# 'vgmerge succeeds with linear LV + snapshots in source VG'
+vgcreate -c n $vg1 $dev1 $dev2 
+vgcreate -c n $vg2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 $vg1 
+lvcreate -l 4 -s -n $lv2 $vg1/$lv1 
+vgchange -an $vg1 
+vg_validate_pvlv_counts_ $vg1 2 2 1 
+vg_validate_pvlv_counts_ $vg2 2 0 0 
+vgmerge $vg2 $vg1 
+vg_validate_pvlv_counts_ $vg2 4 2 1 
+lvremove -f $vg2/$lv2 
+vgremove -f $vg2
+
+# 'vgmerge succeeds with mirrored LV in source VG'
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 
+vgcreate -c n $vg2 $dev4 
+lvcreate -l 4 -n $lv1 -m1 $vg1 
+vgchange -an $vg1 
+vg_validate_pvlv_counts_ $vg1 3 1 0 
+vg_validate_pvlv_counts_ $vg2 1 0 0 
+vgmerge $vg2 $vg1 
+vg_validate_pvlv_counts_ $vg2 4 1 0 
+lvremove -f $vg2/$lv1 
+vgremove -f $vg2
+
+# 'vgmerge rejects LV name collision'
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv1 $vg2
+vgchange -an $vg1
+aux vg_validate_pvlv_counts_ $vg1 2 1 0
+aux vg_validate_pvlv_counts_ $vg2 2 1 0
+not vgmerge $vg2 $vg1 2>err
+grep "Duplicate logical volume name \"$lv1\" in \"$vg2\" and \"$vg1" err
+aux vg_validate_pvlv_counts_ $vg1 2 1 0
+aux vg_validate_pvlv_counts_ $vg2 2 1 0
+vgremove -f $vg1
+vgremove -f $vg2
+
diff --git a/test/t-vgmerge-usage.sh b/test/t-vgmerge-usage.sh
new file mode 100755 (executable)
index 0000000..4be9e1e
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# 'Test vgmerge command options for validity'
+
+. ./test-utils.sh
+
+aux prepare_pvs 4
+
+# 'vgmerge normal operation'
+# ensure ordering does not matter
+vgcreate  $vg1 $dev1 $dev2 
+vgcreate  $vg2 $dev3 $dev4 
+vgmerge $vg1 $vg2 
+vgremove $vg1
+vgcreate -c n $vg2 $dev1 $dev2
+vgcreate -c n $vg1 $dev3 $dev4
+vgmerge $vg2 $vg1
+vgremove $vg2
+
+# 'vgmerge rejects duplicate vg name'
+vgcreate  $vg1 $dev1 $dev2 
+vgcreate  $vg2 $dev3 $dev4 
+not vgmerge $vg1 $vg1 2>err
+grep "^  Duplicate volume group name \"$vg1\"\$" err 
+vgremove $vg2 
+vgremove $vg1
+
+# 'vgmerge rejects vgs with incompatible extent_size'
+vgcreate  --physicalextentsize 4M $vg1 $dev1 $dev2 
+vgcreate  --physicalextentsize 8M $vg2 $dev3 $dev4 
+not vgmerge $vg1 $vg2 2>err
+grep "^  Extent sizes differ" err 
+vgremove $vg2 
+vgremove $vg1
+
+# 'vgmerge rejects vgmerge because max_pv is exceeded'
+vgcreate  --maxphysicalvolumes 2 $vg1 $dev1 $dev2 
+vgcreate  --maxphysicalvolumes 2 $vg2 $dev3 $dev4 
+not vgmerge $vg1 $vg2 2>err
+grep "^  Maximum number of physical volumes (2) exceeded" err 
+vgremove $vg2 
+vgremove $vg1
+
+# 'vgmerge rejects vg with active lv'
+vgcreate $vg1 $dev1 $dev2 
+vgcreate $vg2 $dev3 $dev4 
+lvcreate -l 4 -n lv1 $vg2 
+not vgmerge $vg1 $vg2 2>err
+grep "^  Logical volumes in \"$vg2\" must be inactive\$" err 
+vgremove -f $vg2 
+vgremove -f $vg1
+
+# 'vgmerge rejects vgmerge because max_lv is exceeded' 
+vgcreate --maxlogicalvolumes 2 $vg1 $dev1 $dev2 
+vgcreate --maxlogicalvolumes 2 $vg2 $dev3 $dev4 
+lvcreate -l 4 -n lv1 $vg1 
+lvcreate -l 4 -n lv2 $vg1 
+lvcreate -l 4 -n lv3 $vg2 
+vgchange -an $vg1 
+vgchange -an $vg2 
+not vgmerge $vg1 $vg2 2>err
+grep "^  Maximum number of logical volumes (2) exceeded" err 
+vgremove -f $vg2 
+vgremove -f $vg1
diff --git a/test/t-vgreduce-usage.sh b/test/t-vgreduce-usage.sh
new file mode 100755 (executable)
index 0000000..6a09cfa
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+for mdatype in 1 2
+do
+    # setup PVs
+    pvcreate -M$mdatype $dev1 $dev2 
+
+    # (lvm$mdatype) vgreduce removes only the specified pv from vg (bz427382)" '
+    vgcreate -c n -M$mdatype $vg1 $dev1 $dev2
+    vgreduce $vg1 $dev1
+    check_pv_field_ $dev2 vg_name $vg1
+    vgremove -f $vg1
+
+    # (lvm$mdatype) vgreduce rejects removing the last pv (--all)
+    vgcreate -c n -M$mdatype $vg1 $dev1 $dev2
+    not vgreduce --all $vg1
+    vgremove -f $vg1
+
+    # (lvm$mdatype) vgreduce rejects removing the last pv
+    vgcreate -c n -M$mdatype $vg1 $dev1 $dev2
+    not vgreduce $vg1 $dev1 $dev2
+    vgremove -f $vg1
+
+    pvremove -ff $dev1 $dev2
+done
+
+mdatype=2 # we only expect the following to work for lvm2 metadata
+
+# (lvm$mdatype) setup PVs (--metadatacopies 0)
+pvcreate -M$mdatype $dev1 $dev2
+pvcreate --metadatacopies 0 -M$mdatype $dev3 $dev4
+
+# (lvm$mdatype) vgreduce rejects removing pv with the last mda copy (bz247448)
+vgcreate -c n -M$mdatype $vg1 $dev1 $dev3
+not vgreduce $vg1 $dev1
+vgremove -f $vg1
+
+#COMM "(lvm$mdatype) vgreduce --removemissing --force repares to linear (bz221921)"
+# (lvm$mdatype) setup: create mirror & damage one pv
+vgcreate -c n -M$mdatype $vg1 $dev1 $dev2 $dev3
+lvcreate -n $lv1 -m1 -l 4 $vg1
+lvcreate -n $lv2  -l 4 $vg1 $dev2
+lvcreate -n $lv3 -l 4 $vg1 $dev3
+vgchange -an $vg1
+aux disable_dev $dev1
+# (lvm$mdatype) vgreduce --removemissing --force repares to linear
+vgreduce --removemissing --force $vg1
+check_lv_field_ $vg1/$lv1 segtype linear
+vg_validate_pvlv_counts_ $vg1 2 3 0
+# cleanup
+aux enable_dev $dev1
+vgremove -ff $vg1
+
+#COMM "vgreduce rejects --removemissing --mirrorsonly --force when nonmirror lv lost too"
+# (lvm$mdatype) setup: create mirror + linear lvs
+vgcreate -c n -M$mdatype $vg1 $devs
+lvcreate -n $lv2 -l 4 $vg1
+lvcreate -m1 -n $lv1 -l 4 $vg1 $dev1 $dev2 $dev3
+lvcreate -n $lv3 -l 4 $vg1 $dev3
+pvs --segments -o +lv_name # for record only
+# (lvm$mdatype) setup: damage one pv
+vgchange -an $vg1 
+aux disable_dev $dev1
+#pvcreate -ff -y $dev1
+# vgreduce rejects --removemissing --mirrorsonly --force when nonmirror lv lost too
+not vgreduce -c n --removemissing --mirrorsonly --force $vg1
+
+aux enable_dev $dev1
+
+pvs -P # for record
+lvs -P # for record
+vgs -P # for record
diff --git a/test/t-vgrename-usage.sh b/test/t-vgrename-usage.sh
new file mode 100755 (executable)
index 0000000..61e861c
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+pvcreate $dev1 $dev2
+pvcreate --metadatacopies 0 $dev3 $dev4
+
+# vgrename normal operation - rename vg1 to vg2
+# vgrename normal operation - rename vg2 to vg1
+# ensure name ordering does not matter
+vgcreate $vg1 $dev1 $dev2
+vgrename $vg1 $vg2
+check_vg_field_ $vg2 vg_name $vg2
+vgrename $vg2 $vg1
+check_vg_field_ $vg1 vg_name $vg1
+vgremove $vg1
+
+# vgrename by uuid (bz231187)
+vgcreate $vg1 $dev1 $dev3
+UUID=$(vgs --noheading -o vg_uuid $vg1)
+check_vg_field_ $vg1 vg_uuid $UUID
+vgrename $UUID $vg2
+check_vg_field_ $vg2 vg_name $vg2
+vgremove $vg2
+
+# vgrename fails - new vg already exists
+vgcreate $vg1 $dev1
+vgcreate $vg2 $dev2
+not vgrename $vg1 $vg2
+vgremove $vg1
+vgremove $vg2
+
diff --git a/test/t-vgsplit-operation.sh b/test/t-vgsplit-operation.sh
new file mode 100755 (executable)
index 0000000..9a46a8e
--- /dev/null
@@ -0,0 +1,290 @@
+#!/bin/sh
+# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Test vgsplit operation, including different LV types
+
+. ./test-utils.sh
+
+COMM() {  
+       LAST_TEST="$@"
+}
+
+prepare_pvs 5 258
+# FIXME: paramaterize lvm1 vs lvm2 metadata; most of these tests should run
+# fine with lvm1 metadata as well; for now, just add disks 5 and 6 as lvm1
+# metadata
+
+#
+# vgsplit can be done into a new or existing VG
+#
+for i in new existing
+do
+       #
+       # We can have PVs or LVs on the cmdline
+       #
+       for j in PV LV
+       do
+COMM "vgsplit correctly splits single linear LV into $i VG ($j args)"
+               vgcreate $vg1 $dev1 $dev2 
+               if [ $i = existing ]; then
+                  vgcreate $vg2 $dev3 $dev4
+               fi 
+               lvcreate -l 4 -n $lv1 $vg1 $dev1 
+               vgchange -an $vg1 
+               if [ $j = PV ]; then
+                 vgsplit $vg1 $vg2 $dev1
+               else
+                 vgsplit -n $lv1 $vg1 $vg2
+               fi 
+               vg_validate_pvlv_counts_ $vg1 1 0 0 
+               if [ $i = existing ]; then
+                  aux vg_validate_pvlv_counts_ $vg2 3 1 0
+               else
+                  aux vg_validate_pvlv_counts_ $vg2 1 1 0
+               fi 
+               lvremove -f $vg2/$lv1 
+               vgremove -f $vg2 
+               vgremove -f $vg1
+
+COMM "vgsplit correctly splits single striped LV into $i VG ($j args)"
+               vgcreate $vg1 $dev1 $dev2 
+               if [ $i = existing ]; then
+                  vgcreate $vg2 $dev3 $dev4
+               fi 
+               lvcreate -l 4 -i 2 -n $lv1 $vg1 $dev1 $dev2 
+               vgchange -an $vg1 
+               if [ $j = PV ]; then
+                 vgsplit $vg1 $vg2 $dev1 $dev2
+               else
+                 vgsplit -n $lv1 $vg1 $vg2
+               fi 
+               if [ $i = existing ]; then
+                 aux vg_validate_pvlv_counts_ $vg2 4 1 0
+               else
+                 aux vg_validate_pvlv_counts_ $vg2 2 1 0
+               fi 
+               lvremove -f $vg2/$lv1 
+               vgremove -f $vg2
+
+COMM "vgsplit correctly splits mirror LV into $i VG ($j args)" 
+               vgcreate -c n $vg1 $dev1 $dev2 $dev3 
+               if [ $i = existing ]; then
+                 vgcreate -c n $vg2 $dev4
+               fi 
+               lvcreate -l 64 -m1 -n $lv1 $vg1 $dev1 $dev2 $dev3 
+               vgchange -an $vg1 
+               if [ $j = PV ]; then
+                 vgsplit $vg1 $vg2 $dev1 $dev2 $dev3
+               else
+                 vgsplit -n $lv1 $vg1 $vg2
+               fi 
+               if [ $i = existing ]; then
+                 aux vg_validate_pvlv_counts_ $vg2 4 1 0
+               else
+                 aux vg_validate_pvlv_counts_ $vg2 3 1 0
+               fi 
+               lvremove -f $vg2/$lv1 
+               vgremove -f $vg2
+
+COMM "vgsplit correctly splits origin and snapshot LV into $i VG ($j args)" 
+               vgcreate -c n $vg1 $dev1 $dev2 
+               if [ $i = existing ]; then
+                 vgcreate -c n $vg2 $dev3 $dev4
+               fi 
+               lvcreate -l 64 -i 2 -n $lv1 $vg1 $dev1 $dev2 
+               lvcreate -l 4 -i 2 -s -n $lv2 $vg1/$lv1 
+               vgchange -an $vg1 
+               if [ $j = PV ]; then
+                 vgsplit $vg1 $vg2 $dev1 $dev2
+               else
+                 vgsplit -n $lv1 $vg1 $vg2
+               fi 
+               if [ $i = existing ]; then
+                 aux vg_validate_pvlv_counts_ $vg2 4 2 1
+               else
+                 aux vg_validate_pvlv_counts_ $vg2 2 2 1
+               fi 
+               lvremove -f $vg2/$lv2 
+               lvremove -f $vg2/$lv1 
+               vgremove -f $vg2
+
+COMM "vgsplit correctly splits linear LV but not snap+origin LV into $i VG ($j args)" 
+               vgcreate -c n $vg1 $dev1 $dev2 
+               if [ $i = existing ]; then
+                 vgcreate -c n $vg2 $dev3
+               fi 
+               lvcreate -l 64 -i 2 -n $lv1 $vg1 
+               lvcreate -l 4 -i 2 -s -n $lv2 $vg1/$lv1 
+               vgextend $vg1 $dev4 
+               lvcreate -l 64 -n $lv3 $vg1 $dev4 
+               vgchange -an $vg1 
+               if [ $j = PV ]; then
+                 vgsplit $vg1 $vg2 $dev4
+               else
+                 vgsplit -n $lv3 $vg1 $vg2
+               fi 
+               if [ $i = existing ]; then
+                 aux vg_validate_pvlv_counts_ $vg2 2 1 0
+                 aux vg_validate_pvlv_counts_ $vg1 2 2 1
+               else
+                 aux vg_validate_pvlv_counts_ $vg2 1 1 0
+                 aux vg_validate_pvlv_counts_ $vg1 2 2 1
+               fi 
+               lvremove -f $vg1/$lv2 
+               lvremove -f $vg1/$lv1 
+               lvremove -f $vg2/$lv3 
+               vgremove -f $vg1 
+               vgremove -f $vg2
+
+COMM "vgsplit correctly splits linear LV but not mirror LV into $i VG ($j args)" 
+               vgcreate -c n $vg1 $dev1 $dev2 $dev3 
+               if [ $i = existing ]; then
+                 vgcreate -c n $vg2 $dev5
+               fi 
+               lvcreate -l 64 -m1 -n $lv1 $vg1 $dev1 $dev2 $dev3 
+               vgextend $vg1 $dev4 
+               lvcreate -l 64 -n $lv2 $vg1 $dev4 
+               vgchange -an $vg1 
+               vgs
+               lvs 
+               pvs
+               if [ $j = PV ]; then
+                 vgsplit $vg1 $vg2 $dev4
+               else
+                 vgsplit -n $lv2 $vg1 $vg2
+               fi 
+               if [ $i = existing ]; then
+                 aux vg_validate_pvlv_counts_ $vg1 3 1 0
+                 aux vg_validate_pvlv_counts_ $vg2 2 1 0
+               else
+               vgs
+               lvs 
+               pvs
+                 aux vg_validate_pvlv_counts_ $vg1 3 1 0
+                 aux vg_validate_pvlv_counts_ $vg2 1 1 0
+               fi 
+               lvremove -f $vg1/$lv1 
+               lvremove -f $vg2/$lv2 
+               vgremove -f $vg1 
+               vgremove -f $vg2
+
+       done
+done
+
+#
+# Test more complex setups where the code has to find associated PVs and
+# LVs to split the VG correctly
+# 
+COMM "vgsplit fails splitting 3 striped LVs into VG when only 1 LV specified" 
+vgcreate $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 4 -n $lv1 -i 2 $vg1 $dev1 $dev2 
+lvcreate -l 4 -n $lv2 -i 2 $vg1 $dev2 $dev3 
+lvcreate -l 4 -n $lv3 -i 2 $vg1 $dev3 $dev4 
+vgchange -an $vg1 
+not vgsplit -n $lv1 $vg1 $vg2
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting one LV with 2 snapshots, only origin LV specified" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 $vg1 $dev1 $dev2 
+lvcreate -l 4 -n $lv2 -s $vg1/$lv1 $dev3
+lvcreate -l 4 -n $lv3 -s $vg1/$lv1 $dev4
+vg_validate_pvlv_counts_ $vg1 4 3 2 
+vgchange -an $vg1 
+not vgsplit -n $lv1 $vg1 $vg2;
+lvremove -f $vg1/$lv2 
+lvremove -f $vg1/$lv3 
+lvremove -f $vg1/$lv1 
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting one LV with 2 snapshots, only snapshot LV specified" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 $vg1 $dev1 $dev2 
+lvcreate -l 4 -n $lv2 -s $vg1/$lv1 $dev3
+lvcreate -l 4 -n $lv3 -s $vg1/$lv1 $dev4
+vg_validate_pvlv_counts_ $vg1 4 3 2 
+vgchange -an $vg1 
+not vgsplit -n $lv2 $vg1 $vg2
+lvremove -f $vg1/$lv2 
+lvremove -f $vg1/$lv3 
+lvremove -f $vg1/$lv1 
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting one mirror LV, only one PV specified" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3 
+vg_validate_pvlv_counts_ $vg1 4 1 0 
+vgchange -an $vg1 
+not vgsplit $vg1 $vg2 $dev2 
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting 1 mirror + 1 striped LV, only striped LV specified" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3 
+lvcreate -l 16 -n $lv2 -i 2 $vg1 $dev3 $dev4 
+vg_validate_pvlv_counts_ $vg1 4 2 0 
+vgchange -an $vg1 
+not vgsplit -n $lv2 $vg1 $vg2 2>err
+vgremove -ff $vg1
+
+#
+# Verify vgsplit rejects active LVs only when active LVs involved in split
+#
+COMM "vgsplit fails, active mirror involved in split" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3 
+lvcreate -l 16 -n $lv2 $vg1 $dev4 
+lvchange -an $vg1/$lv2 
+vg_validate_pvlv_counts_ $vg1 4 2 0 
+not vgsplit -n $lv1 $vg1 $vg2;
+vg_validate_pvlv_counts_ $vg1 4 2 0 
+vgremove -ff $vg1
+
+COMM "vgsplit succeeds, active mirror not involved in split" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3 
+lvcreate -l 16 -n $lv2 $vg1 $dev4 
+lvchange -an $vg1/$lv2 
+vg_validate_pvlv_counts_ $vg1 4 2 0 
+vgsplit -n $lv2 $vg1 $vg2 
+vg_validate_pvlv_counts_ $vg1 3 1 0 
+vg_validate_pvlv_counts_ $vg2 1 1 0 
+vgremove -ff $vg1 
+vgremove -ff $vg2
+
+COMM "vgsplit fails, active snapshot involved in split" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4 
+lvcreate -l 64 -i 2 -n $lv1 $vg1 $dev1 $dev2 
+lvcreate -l 4 -i 2 -s -n $lv2 $vg1/$lv1 
+lvcreate -l 64 -i 2 -n $lv3 $vg1 $dev3 $dev4 
+lvchange -an $vg1/$lv3 
+vg_validate_pvlv_counts_ $vg1 4 3 1 
+not vgsplit -n $lv2 $vg1 $vg2;
+vg_validate_pvlv_counts_ $vg1 4 3 1 
+lvremove -f $vg1/$lv2 
+vgremove -ff $vg1
+
+COMM "vgsplit succeeds, active snapshot not involved in split" 
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 
+lvcreate -l 64 -i 2 -n $lv1 $vg1 $dev1 $dev2 
+lvcreate -l 4 -s -n $lv2 $vg1/$lv1 
+vgextend $vg1 $dev4 
+lvcreate -l 64 -n $lv3 $vg1 $dev4 
+lvchange -an $vg1/$lv3 
+vg_validate_pvlv_counts_ $vg1 4 3 1 
+vgsplit -n $lv3 $vg1 $vg2 
+vg_validate_pvlv_counts_ $vg1 3 2 1 
+vg_validate_pvlv_counts_ $vg2 1 1 0 
+vgchange -an $vg1 
+lvremove -f $vg1/$lv2 
+vgremove -ff $vg1 
+vgremove -ff $vg2
+
diff --git a/test/t-vgsplit-stacked.sh b/test/t-vgsplit-stacked.sh
new file mode 100644 (file)
index 0000000..424156f
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+. ./test-utils.sh
+
+prepare_lvmconf '[ "a/dev\/mirror/", "a/dev\/mapper\/.*$/", "a/dev\/LVMTEST/", "r/.*/" ]'
+aux prepare_devs 3
+
+pvcreate $devs
+vgcreate $vg1 $dev1 $dev2
+lvcreate -n $lv1 -l 100%FREE $vg1
+
+#top VG
+pvcreate $DM_DEV_DIR/$vg1/$lv1
+vgcreate $vg $DM_DEV_DIR/$vg1/$lv1 $dev3
+
+vgchange -a n $vg
+vgchange -a n $vg1
+
+# this should fail but not segfault, RHBZ 481793.
+not vgsplit $vg $vg1 $dev3
diff --git a/test/t-vgsplit-usage.sh b/test/t-vgsplit-usage.sh
new file mode 100755 (executable)
index 0000000..ade39d8
--- /dev/null
@@ -0,0 +1,187 @@
+#!/bin/sh
+# Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+# Test vgsplit command options for validity
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+for mdatype in 1 2
+do
+
+pvcreate -M$mdatype $devs
+
+# ensure name order does not matter
+# NOTE: if we're using lvm1, we must use -M on vgsplit
+vgcreate -M$mdatype $vg1 $devs
+vgsplit -M$mdatype $vg1 $vg2 $dev1
+vgremove $vg1
+vgremove $vg2
+vgcreate -M$mdatype $vg2 $devs
+vgsplit -M$mdatype $vg2 $vg1 $dev1
+vgremove $vg1
+vgremove $vg2
+
+# vgsplit accepts new vg as destination of split
+# lvm1 -- bz244792
+vgcreate -M$mdatype $vg1 $devs
+vgsplit $vg1 $vg2 $dev1 1>err
+grep "New volume group \"$vg2\" successfully split from \"$vg1\"" err 
+vgremove $vg1 
+vgremove $vg2
+
+# vgsplit accepts existing vg as destination of split
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+vgcreate -M$mdatype $vg2 $dev3 $dev4 
+vgsplit $vg1 $vg2 $dev1 1>err
+grep "Existing volume group \"$vg2\" successfully split from \"$vg1\"" err 
+vgremove $vg1 
+vgremove $vg2
+
+# vgsplit accepts --maxphysicalvolumes 128 on new VG
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+vgsplit --maxphysicalvolumes 128 $vg1 $vg2 $dev1 
+check_vg_field_ $vg2 max_pv 128 
+vgremove $vg1 
+vgremove $vg2
+
+# vgsplit accepts --maxlogicalvolumes 128 on new VG
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+vgsplit --maxlogicalvolumes 128 $vg1 $vg2 $dev1 
+check_vg_field_ $vg2 max_lv 128 
+vgremove $vg1 
+vgremove $vg2
+
+# vgsplit rejects split because max_pv of destination would be exceeded
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg2 $dev3 $dev4
+not vgsplit $vg1 $vg2 $dev1 2>err;
+grep "^  Maximum number of physical volumes (2) exceeded" err
+vgremove $vg2
+vgremove $vg1
+
+# vgsplit rejects split because maxphysicalvolumes given with existing vg
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg1 $dev1 $dev2 
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg2 $dev3 $dev4 
+not vgsplit --maxphysicalvolumes 2 $vg1 $vg2 $dev1 2>err;
+grep "^  Volume group \"$vg2\" exists, but new VG option specified" err 
+vgremove $vg2 
+vgremove $vg1
+
+# vgsplit rejects split because maxlogicalvolumes given with existing vg
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg1 $dev1 $dev2 
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg2 $dev3 $dev4 
+not vgsplit --maxlogicalvolumes 2 $vg1 $vg2 $dev1 2>err
+grep "^  Volume group \"$vg2\" exists, but new VG option specified" err 
+vgremove $vg2 
+vgremove $vg1
+
+# vgsplit rejects split because alloc given with existing vg
+vgcreate -M$mdatype --alloc cling $vg1 $dev1 $dev2 
+vgcreate -M$mdatype --alloc cling $vg2 $dev3 $dev4 
+not vgsplit --alloc cling $vg1 $vg2 $dev1 2>err;
+grep "^  Volume group \"$vg2\" exists, but new VG option specified" err 
+vgremove $vg2 
+vgremove $vg1
+
+# vgsplit rejects split because clustered given with existing vg
+vgcreate -M$mdatype --clustered n $vg1 $dev1 $dev2 
+vgcreate -M$mdatype --clustered n $vg2 $dev3 $dev4 
+not vgsplit --clustered n $vg1 $vg2 $dev1 2>err
+grep "^  Volume group \"$vg2\" exists, but new VG option specified" err 
+vgremove $vg2 
+vgremove $vg1
+
+# vgsplit rejects vg with active lv
+pvcreate -M$mdatype -ff $dev3 $dev4 
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+vgcreate -M$mdatype $vg2 $dev3 $dev4 
+lvcreate -l 4 -n $lv1 $vg1 
+not vgsplit $vg1 $vg2 $dev1 2>err;
+grep "^  Logical volumes in \"$vg1\" must be inactive\$" err 
+vgremove -f $vg2 
+vgremove -f $vg1
+
+# vgsplit rejects split because max_lv is exceeded
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg1 $dev1 $dev2 
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg2 $dev3 $dev4 
+lvcreate -l 4 -n $lv1 $vg1 
+lvcreate -l 4 -n $lv2 $vg1 
+lvcreate -l 4 -n $lv3 $vg2 
+vgchange -an $vg1 
+vgchange -an $vg2 
+not vgsplit $vg1 $vg2 $dev1 2>err;
+grep "^  Maximum number of logical volumes (2) exceeded" err 
+vgremove -f $vg2 
+vgremove -f $vg1
+
+# vgsplit verify default - max_lv attribute from new VG is same as source VG" \
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+lvcreate -l 4 -n $lv1 $vg1 
+vgchange -an $vg1 
+vgsplit $vg1 $vg2 $dev1 
+compare_vg_field_ $vg1 $vg2 max_lv 
+vgremove -f $vg2 
+vgremove -f $vg1
+
+# vgsplit verify default - max_pv attribute from new VG is same as source VG" \
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+lvcreate -l 4 -n $lv1 $vg1 
+vgchange -an $vg1 
+vgsplit $vg1 $vg2 $dev1 
+compare_vg_field_ $vg1 $vg2 max_pv 
+vgremove -f $vg2 
+vgremove -f $vg1
+
+# vgsplit verify default - vg_fmt attribute from new VG is same as source VG" \
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+lvcreate -l 4 -n $lv1 $vg1 
+vgchange -an $vg1 
+vgsplit $vg1 $vg2 $dev1 
+compare_vg_field_ $vg1 $vg2 vg_fmt 
+vgremove -f $vg2 
+vgremove -f $vg1
+
+# vgsplit rejects split because PV not in VG
+vgcreate -M$mdatype $vg1 $dev1 $dev2 
+vgcreate -M$mdatype $vg2 $dev3 $dev4 
+lvcreate -l 4 -n $lv1 $vg1 
+lvcreate -l 4 -n $lv2 $vg1 
+vgchange -an $vg1 
+not vgsplit $vg1 $vg2 $dev3 2>err;
+vgremove -f $vg2 
+vgremove -f $vg1
+done
+
+# ONLY LVM2 metadata
+# setup PVs" '
+pvcreate --metadatacopies 0 $dev5
+
+# vgsplit rejects to give away pv with the last mda copy
+vgcreate $vg1 $dev5 $dev2  
+lvcreate -l 10 -n $lv1  $vg1 
+lvchange -an $vg1/$lv1 
+vg_validate_pvlv_counts_ $vg1 2 1 0 
+not vgsplit  $vg1 $vg2 $dev5;
+vg_validate_pvlv_counts_ $vg1 2 1 0 
+vgremove -ff $vg1
+
+# vgsplit rejects split because metadata types differ
+pvcreate -ff -M1 $dev3 $dev4 
+pvcreate -ff $dev1 $dev2 
+vgcreate -M1 $vg1 $dev3 $dev4 
+vgcreate $vg2 $dev1 $dev2 
+not vgsplit $vg1 $vg2 $dev3 2>err;
+grep "^  Metadata types differ" err 
+vgremove $vg2 
+vgremove $vg1
+
diff --git a/test/test-utils.sh b/test/test-utils.sh
new file mode 100644 (file)
index 0000000..e1aea8a
--- /dev/null
@@ -0,0 +1,457 @@
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+aux() {
+        # use just "$@" for verbose operation
+       "$@" > /dev/null 2> /dev/null
+       #"$@"
+}
+
+STACKTRACE() {
+       trap - ERR;
+       i=0;
+
+       while FUNC=${FUNCNAME[$i]}; test "$FUNC" != "main"; do 
+               echo "$i ${FUNC}() called from ${BASH_SOURCE[$i]}:${BASH_LINENO[$i]}"
+               i=$(($i + 1));
+       done
+
+       # Get backtraces from coredumps
+       if which gdb >& /dev/null; then
+               echo bt full > gdb_commands.txt
+               echo l >> gdb_commands.txt
+               echo quit >> gdb_commands.txt
+               for core in `ls core* 2>/dev/null`; do
+                       bin=$(gdb -batch -c $core 2>&1 | grep "generated by" | \
+                               sed -e "s,.*generated by \`\([^ ']*\).*,\1,")
+                       gdb -batch -c $core -x gdb_commands.txt `which $bin`
+               done
+       fi
+
+       test -f debug.log && {
+               sed -e "s,^,## DEBUG: ,;s,$top_srcdir/\?,," < debug.log
+       }
+}
+
+init_udev_transaction() {
+       if test "$DM_UDEV_SYNCHRONISATION" = 1; then
+               COOKIE=$(dmsetup udevcreatecookie)
+               # Cookie is not generated if udev is not running!
+               if test -n "$COOKIE"; then
+                       export DM_UDEV_COOKIE=$COOKIE
+               fi
+       fi
+}
+
+finish_udev_transaction() {
+       if test "$DM_UDEV_SYNCHRONISATION" = 1 -a -n "$DM_UDEV_COOKIE"; then
+               dmsetup udevreleasecookie
+               unset DM_UDEV_COOKIE
+       fi
+}
+
+prepare_clvmd() {
+       if test -z "$LVM_TEST_LOCKING" || test "$LVM_TEST_LOCKING" -ne 3 ; then
+               return 0 # not needed
+       fi
+
+       if pgrep clvmd ; then
+               echo "Cannot use fake cluster locking with real clvmd ($(pgrep clvmd)) running."
+               exit 200
+       fi
+
+       # skip if we don't have our own clvmd...
+       (which clvmd | grep $abs_builddir) || exit 200
+
+       # skip if we singlenode is not compiled in
+       (clvmd --help 2>&1 | grep "Available cluster managers" | grep singlenode) || exit 200
+
+       trap_teardown
+
+       clvmd -Isinglenode -d 1 &
+       LOCAL_CLVMD="$!"
+
+       # check that it is really running now
+       sleep .1
+       ps $LOCAL_CLVMD || exit 200
+}
+
+prepare_dmeventd() {
+       if pgrep dmeventd ; then
+               echo "Cannot test dmeventd with real dmeventd ($(pgrep dmeventd)) running."
+               exit 200
+       fi
+
+       # skip if we don't have our own dmeventd...
+       (which dmeventd | grep $abs_builddir) || exit 200
+
+       trap_teardown
+
+       dmeventd -f &
+       LOCAL_DMEVENTD="$!"
+}
+
+prepare_testroot() {
+       OLDPWD="`pwd`"
+       PREFIX="LVMTEST$$"
+
+       trap_teardown
+       TESTDIR=$($abs_srcdir/mkdtemp ${LVM_TEST_DIR-$(pwd)} $PREFIX.XXXXXXXXXX) \
+               || { echo "failed to create temporary directory in ${LVM_TEST_DIR-$(pwd)}"; exit 1; }
+
+       export LVM_SYSTEM_DIR=$TESTDIR/etc
+       export DM_DEV_DIR=$TESTDIR/dev
+       mkdir $LVM_SYSTEM_DIR $DM_DEV_DIR $DM_DEV_DIR/mapper $TESTDIR/lib
+
+       cd $TESTDIR
+
+       for i in `find $abs_top_builddir/daemons/dmeventd/plugins/ -name \*.so`; do
+               echo Setting up symlink from $i to $TESTDIR/lib
+               ln -s $i $TESTDIR/lib
+       done
+}
+
+teardown_devs() {
+       test -n "$PREFIX" && {
+               rm -rf $TESTDIR/dev/$PREFIX*
+
+               init_udev_transaction
+               while dmsetup table | grep -q ^$PREFIX; do
+                       for s in `dmsetup info -c -o name --noheading | grep ^$PREFIX`; do
+                               umount -fl $DM_DEV_DIR/mapper/$s || true
+                               dmsetup remove $s >& /dev/null || true
+                       done
+               done
+               finish_udev_transaction
+
+       }
+
+       # NOTE: SCSI_DEBUG_DEV test must come before the LOOP test because
+       # prepare_scsi_debug_dev() also sets LOOP to short-circuit prepare_loop()
+       if [ -n "$SCSI_DEBUG_DEV" ] ; then
+               modprobe -r scsi_debug
+       else
+               test -n "$LOOP" && losetup -d $LOOP
+               test -n "$LOOPFILE" && rm -f $LOOPFILE
+       fi
+       unset devs # devs is set in prepare_devs()
+       unset LOOP
+}
+
+teardown() {
+       echo $LOOP
+       echo $PREFIX
+
+       test -n "$LOCAL_CLVMD" && {
+               kill "$LOCAL_CLVMD"
+               sleep .1
+               kill -9 "$LOCAL_CLVMD" || true
+       }
+
+       test -n "$LOCAL_DMEVENTD" && kill -9 "$LOCAL_DMEVENTD"
+
+       teardown_devs
+
+       test -n "$TESTDIR" && {
+               cd $OLDPWD
+               rm -rf $TESTDIR || echo BLA
+       }
+}
+
+trap_teardown() {
+       trap 'set +vx; STACKTRACE; set -vx' ERR
+       trap 'aux teardown' EXIT # don't forget to clean up
+}
+
+make_ioerror() {
+       echo 0 10000000 error | dmsetup create ioerror
+       ln -s $DM_DEV_DIR/mapper/ioerror $DM_DEV_DIR/ioerror
+}
+
+prepare_loop() {
+       size=$1
+       test -n "$size" || size=32
+
+       # skip if prepare_scsi_debug_dev() was used
+       if [ -n "$SCSI_DEBUG_DEV" -a -n "$LOOP" ]; then
+               return 0
+       fi
+
+       test -z "$LOOP"
+       test -n "$DM_DEV_DIR"
+
+       trap_teardown
+
+       for i in 0 1 2 3 4 5 6 7; do
+               test -e $DM_DEV_DIR/loop$i || mknod $DM_DEV_DIR/loop$i b 7 $i
+       done
+
+       LOOPFILE="$PWD/test.img"
+       dd if=/dev/zero of="$LOOPFILE" bs=$((1024*1024)) count=0 seek=$(($size-1))
+       if LOOP=`losetup -s -f "$LOOPFILE" 2>/dev/null`; then
+               return 0
+       elif LOOP=`losetup -f` && losetup $LOOP "$LOOPFILE"; then
+               # no -s support
+               return 0
+       else
+               # no -f support 
+               # Iterate through $DM_DEV_DIR/loop{,/}{0,1,2,3,4,5,6,7}
+               for slash in '' /; do
+                       for i in 0 1 2 3 4 5 6 7; do
+                               local dev=$DM_DEV_DIR/loop$slash$i
+                               ! losetup $dev >/dev/null 2>&1 || continue
+                               # got a free
+                               losetup "$dev" "$LOOPFILE"
+                               LOOP=$dev
+                               break
+                       done
+                       if [ -n "$LOOP" ]; then 
+                               break
+                       fi
+               done
+               test -n "$LOOP" # confirm or fail
+               return 0
+       fi
+       exit 1 # should not happen
+}
+
+# A drop-in replacement for prepare_loop() that uses scsi_debug to create
+# a ramdisk-based SCSI device upon which all LVM devices will be created
+# - scripts must take care not to use a DEV_SIZE that will enduce OOM-killer
+prepare_scsi_debug_dev()
+{
+    local DEV_SIZE="$1"
+    shift
+    local SCSI_DEBUG_PARAMS="$@"
+
+    test -n "$SCSI_DEBUG_DEV" && return 0
+    test -z "$LOOP"
+    test -n "$DM_DEV_DIR"
+
+    trap_teardown
+
+    # Skip test if awk isn't available (required for get_sd_devs_)
+    which awk || exit 200
+
+    # Skip test if scsi_debug module is unavailable or is already in use
+    modprobe --dry-run scsi_debug || exit 200
+    lsmod | grep -q scsi_debug && exit 200
+
+    # Create the scsi_debug device and determine the new scsi device's name
+    # NOTE: it will _never_ make sense to pass num_tgts param;
+    # last param wins.. so num_tgts=1 is imposed
+    modprobe scsi_debug dev_size_mb=$DEV_SIZE $SCSI_DEBUG_PARAMS num_tgts=1 || exit 200
+    sleep 2 # allow for async Linux SCSI device registration
+
+    local DEBUG_DEV=/dev/$(grep -H scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+    [ -b $DEBUG_DEV ] || exit 1 # should not happen
+
+    # Create symlink to scsi_debug device in $DM_DEV_DIR
+    SCSI_DEBUG_DEV=$DM_DEV_DIR/$(basename $DEBUG_DEV)
+    # Setting $LOOP provides means for prepare_devs() override
+    LOOP=$SCSI_DEBUG_DEV
+    ln -snf $DEBUG_DEV $SCSI_DEBUG_DEV
+    return 0
+}
+
+cleanup_scsi_debug_dev()
+{
+    aux teardown_devs
+    unset SCSI_DEBUG_DEV
+    unset LOOP
+}
+
+prepare_devs() {
+       local n="$1"
+       test -z "$n" && n=3
+       local devsize="$2"
+       test -z "$devsize" && devsize=34
+       local pvname="$3"
+       test -z "$pvname" && pvname="pv"
+
+       prepare_loop $(($n*$devsize))
+
+       if ! loopsz=`blockdev --getsz $LOOP 2>/dev/null`; then
+               loopsz=`blockdev --getsize $LOOP 2>/dev/null`
+       fi
+
+       local size=$(($loopsz/$n))
+
+       init_udev_transaction
+       for i in `seq 1 $n`; do
+               local name="${PREFIX}$pvname$i"
+               local dev="$DM_DEV_DIR/mapper/$name"
+               eval "dev$i=$dev"
+               devs="$devs $dev"
+               echo 0 $size linear $LOOP $((($i-1)*$size)) > $name.table
+               dmsetup create $name $name.table
+       done
+       finish_udev_transaction
+
+       for i in `seq 1 $n`; do
+               local name="${PREFIX}$pvname$i"
+               dmsetup info -c $name
+       done
+       for i in `seq 1 $n`; do
+               local name="${PREFIX}$pvname$i"
+               dmsetup table $name
+       done
+}
+
+disable_dev() {
+
+       init_udev_transaction
+       for dev in "$@"; do
+        # first we make the device inaccessible
+               echo 0 10000000 error | dmsetup load $dev
+               dmsetup resume $dev
+        # now let's try to get rid of it if it's unused
+        #dmsetup remove $dev
+       done
+       finish_udev_transaction
+
+}
+
+enable_dev() {
+
+       init_udev_transaction
+       for dev in "$@"; do
+               local name=`echo "$dev" | sed -e 's,.*/,,'`
+               dmsetup create $name $name.table || dmsetup load $name $name.table
+               dmsetup resume $dev
+       done
+       finish_udev_transaction
+}
+
+backup_dev() {
+       for dev in "$@"; do
+               dd if=$dev of=$dev.backup bs=1024
+       done
+}
+
+restore_dev() {
+       for dev in "$@"; do
+               test -e $dev.backup || {
+                       echo "Internal error: $dev not backed up, can't restore!"
+                       exit 1
+               }
+               dd of=$dev if=$dev.backup bs=1024
+       done
+}
+
+prepare_pvs() {
+       prepare_devs "$@"
+       pvcreate -ff $devs
+}
+
+prepare_vg() {
+       vgremove -ff $vg || true
+       teardown_devs
+
+       prepare_pvs "$@"
+       vgcreate -c n $vg $devs
+       pvs -v
+}
+
+prepare_lvmconf() {
+       local filter="$1"
+       test -z "$filter" && \
+               filter='[ "a/dev\/mirror/", "a/dev\/mapper\/.*pv[0-9_]*$/", "r/.*/" ]'
+        locktype=
+       if test -z "$LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND"; then
+               LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND="
+    snapshot_autoextend_percent = 50
+    snapshot_autoextend_threshold = 50"
+       fi
+       if test -n "$LVM_TEST_LOCKING"; then locktype="locking_type = $LVM_TEST_LOCKING"; fi
+       cat > $TESTDIR/etc/lvm.conf.new <<-EOF
+  $LVM_TEST_CONFIG
+  devices {
+    dir = "$DM_DEV_DIR"
+    scan = "$DM_DEV_DIR"
+    filter = $filter
+    cache_dir = "$TESTDIR/etc"
+    sysfs_scan = 0
+    default_data_alignment = 1
+    $LVM_TEST_CONFIG_DEVICES
+  }
+  log {
+    syslog = 0
+    indent = 1
+    level = 9
+    file = "$TESTDIR/debug.log"
+    overwrite = 1
+    activation = 1
+  }
+  backup {
+    backup = 0
+    archive = 0
+  }
+  global {
+    abort_on_internal_errors = 1
+    library_dir = "$TESTDIR/lib"
+    locking_dir = "$TESTDIR/var/lock/lvm"
+    $locktype
+    si_unit_consistency = 1
+    fallback_to_local_locking = 0
+  }
+  activation {
+    udev_sync = 1
+    udev_rules = 1
+    polling_interval = 0
+    $LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND
+  }
+EOF
+       # FIXME remove this workaround after mmap & truncating file problems solved
+       mv -f $TESTDIR/etc/lvm.conf.new $TESTDIR/etc/lvm.conf
+       cat $TESTDIR/etc/lvm.conf
+}
+
+prepare() {
+       ulimit -c unlimited
+       # FIXME any way to set this just for our children?
+       # echo 1 > /proc/sys/kernel/core_uses_pid
+       prepare_testroot
+       prepare_lvmconf
+       prepare_clvmd
+
+       # set up some default names
+       vg=${PREFIX}vg
+       vg1=${PREFIX}vg1
+       vg2=${PREFIX}vg2
+       lv=LV
+       lv1=LV1
+       lv2=LV2
+       lv3=LV3
+       lv4=LV4
+}
+
+apitest() {
+       t=$1
+        shift
+       test -x $abs_top_builddir/test/api/$t.t || exit 200
+       $abs_top_builddir/test/api/$t.t "$@"
+}
+
+api() {
+       test -x $abs_top_builddir/test/api/wrapper || exit 200
+       $abs_top_builddir/test/api/wrapper "$@"
+}
+
+LANG=C
+LC_ALL=C
+TZ=UTC
+unset CDPATH
+
+. ./init.sh || { echo >&2 you must run make first; exit 1; }
+. ./lvm-utils.sh
+
+set -vexE -o pipefail
+aux prepare
diff --git a/tools/.exported_symbols b/tools/.exported_symbols
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tools/Makefile.in b/tools/Makefile.in
new file mode 100644 (file)
index 0000000..70a1b61
--- /dev/null
@@ -0,0 +1,207 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+       dumpconfig.c \
+       formats.c \
+       lvchange.c \
+       lvconvert.c \
+       lvcreate.c \
+       lvdisplay.c \
+       lvextend.c \
+       lvmchange.c \
+       lvmcmdline.c \
+       lvmdiskscan.c \
+       lvreduce.c \
+       lvremove.c \
+       lvrename.c \
+       lvresize.c \
+       lvscan.c \
+       polldaemon.c \
+       pvchange.c \
+       pvck.c \
+       pvcreate.c \
+       pvdisplay.c \
+       pvmove.c \
+       pvremove.c \
+       pvresize.c \
+       pvscan.c \
+       reporter.c \
+       segtypes.c \
+       toollib.c \
+       vgcfgbackup.c \
+       vgcfgrestore.c \
+       vgchange.c \
+       vgck.c \
+       vgcreate.c \
+       vgconvert.c \
+       vgdisplay.c \
+       vgexport.c \
+       vgextend.c \
+       vgimport.c \
+       vgmerge.c \
+       vgmknodes.c \
+       vgreduce.c \
+       vgremove.c \
+       vgrename.c \
+       vgscan.c \
+       vgsplit.c
+
+SOURCES2 =\
+       dmsetup.c \
+       lvm.c \
+       lvm2cmd-static.c \
+       lvm2cmd.c \
+       lvmcmdlib.c
+
+TARGETS =\
+       .commands \
+       liblvm2cmd.a \
+       lvm
+
+TARGETS_DM = dmsetup
+
+INSTALL_LVM_TARGETS = install_tools_dynamic
+INSTALL_DMSETUP_TARGETS = install_dmsetup_dynamic
+INSTALL_CMDLIB_TARGETS = install_cmdlib_dynamic install_cmdlib_include 
+
+ifeq ("@STATIC_LINK@", "yes")
+  TARGETS += lvm.static
+  TARGETS_DM += dmsetup.static
+  INSTALL_LVM_TARGETS += install_tools_static
+  INSTALL_DMSETUP_TARGETS += install_dmsetup_static
+  INSTALL_CMDLIB_TARGETS += install_cmdlib_static
+endif
+
+LVMLIBS = $(LVMINTERNAL_LIBS)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+CLEAN_TARGETS = liblvm2cmd.$(LIB_SUFFIX) $(TARGETS_DM) \
+       liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION) lvm-static.o \
+       liblvm2cmd-static.a dmsetup.static lvm.static
+
+ifeq ("@CMDLIB@", "yes")
+       TARGETS += liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION)
+       INSTALL_LVM_TARGETS += $(INSTALL_CMDLIB_TARGETS)
+endif
+
+ifeq ("@DMEVENTD@", "yes")
+       LVMLIBS += -ldevmapper-event
+endif
+
+LVMLIBS += -ldevmapper
+
+EXPORTED_HEADER = $(srcdir)/lvm2cmd.h
+EXPORTED_FN_PREFIX = lvm2
+
+DEFS += -DLVM_SHARED_PATH=\"$(exec_prefix)/sbin/lvm\"
+
+CFLOW_LIST = lvmcmdlib.c lvm2cmd.c
+CFLOW_LIST_TARGET = liblvm2cmd.cflow
+CFLOW_TARGET = lvm
+
+include $(top_builddir)/make.tmpl
+
+LIBS += $(UDEV_LIBS)
+
+device-mapper: $(TARGETS_DM)
+
+dmsetup: dmsetup.o $(top_builddir)/libdm/libdevmapper.$(LIB_SUFFIX)
+       $(CC) $(CFLAGS) $(LDFLAGS) -L$(top_builddir)/libdm \
+             -o $@ dmsetup.o -ldevmapper $(LIBS)
+
+dmsetup.static: dmsetup.o $(interfacebuilddir)/libdevmapper.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -static -L$(interfacebuilddir) \
+             -o $@ dmsetup.o -ldevmapper $(STATIC_LIBS) $(LIBS)
+
+all: device-mapper
+
+lvm: $(OBJECTS) lvm.o $(top_builddir)/lib/liblvm-internal.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) lvm.o \
+               $(LVMLIBS) $(READLINE_LIBS) $(LIBS) -rdynamic
+
+lvm.static: $(OBJECTS) lvm-static.o $(top_builddir)/lib/liblvm-internal.a  $(interfacebuilddir)/libdevmapper.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -static -L$(interfacebuilddir) -o $@ \
+             $(OBJECTS) lvm-static.o $(LVMLIBS) $(STATIC_LIBS) $(LIBS)
+
+liblvm2cmd.a: $(top_builddir)/lib/liblvm-internal.a $(OBJECTS) lvmcmdlib.o lvm2cmd.o
+       cat $(top_builddir)/lib/liblvm-internal.a > $@
+       $(AR) rs $@ $(OBJECTS) lvmcmdlib.o lvm2cmd.o
+
+liblvm2cmd-static.a: $(top_builddir)/lib/liblvm-internal.a $(OBJECTS) lvmcmdlib.o lvm2cmd-static.o
+       cat $(top_builddir)/lib/liblvm-internal.a > $@
+       $(AR) rs $@ $(OBJECTS) lvmcmdlib.o lvm2cmd-static.o
+
+liblvm2cmd.$(LIB_SUFFIX): liblvm2cmd.a $(LDDEPS)
+       $(CC) -shared -Wl,-soname,$@.$(LIB_VERSION) \
+               $(CFLAGS) $(CLDFLAGS) -o $@ \
+               @CLDWHOLEARCHIVE@ liblvm2cmd.a @CLDNOWHOLEARCHIVE@ \
+               $(LVMLIBS) $(LIBS)
+
+liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION): liblvm2cmd.$(LIB_SUFFIX)
+       $(LN_S) -f $< $@
+
+.commands: $(srcdir)/commands.h $(srcdir)/cmdnames.h Makefile
+       $(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \
+               egrep -v '^ *(|#.*|dumpconfig|formats|help|pvdata|segtypes|version) *$$' > .commands
+
+ifneq ("$(CFLOW_CMD)", "")
+CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
+-include $(top_builddir)/libdm/libdevmapper.cflow
+-include $(top_builddir)/lib/liblvm-internal.cflow
+endif
+
+.PHONY: install_cmdlib_dynamic install_cmdlib_static install_cmdlib_include \
+       install_tools_dynamic install_tools_static \
+       install_dmsetup_dynamic install_dmsetup_static
+
+install_cmdlib_include: $(srcdir)/lvm2cmd.h
+       $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_cmdlib_dynamic: liblvm2cmd.$(LIB_SUFFIX)
+       $(INSTALL_PROGRAM) -D $< $(libdir)/$(<F).$(LIB_VERSION)
+       $(INSTALL_DIR) $(usrlibdir)
+       $(LN_S) -f $(USRLIB_RELPATH)$(<F).$(LIB_VERSION) $(usrlibdir)/$(<F)
+
+install_cmdlib_static: liblvm2cmd-static.a
+       $(INSTALL_DATA) -D $< $(usrlibdir)/liblvm2cmd.a
+
+install_tools_dynamic: lvm .commands
+       $(INSTALL_PROGRAM) -D lvm $(sbindir)/lvm
+       @echo Creating symbolic links for individual commands in $(sbindir)
+       @for v in `cat .commands`; do \
+               echo "$(LN_S) -f lvm $(sbindir)/$$v"; \
+               $(LN_S) -f lvm $(sbindir)/$$v; \
+       done;
+
+install_tools_static: lvm.static
+       $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+
+install_dmsetup_dynamic: dmsetup
+       $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
+
+install_dmsetup_static: dmsetup.static
+       $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+
+install_device-mapper: $(INSTALL_DMSETUP_TARGETS)
+
+install_lvm2: $(INSTALL_LVM_TARGETS)
+
+install: install_lvm2 install_device-mapper
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/tools/args.h b/tools/args.h
new file mode 100644 (file)
index 0000000..ff7514b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Put all long args that don't have a corresponding short option first.
+ */
+/* *INDENT-OFF* */
+arg(version_ARG, '\0', "version", NULL, 0)
+arg(quiet_ARG, '\0', "quiet", NULL, 0)
+arg(physicalvolumesize_ARG, '\0', "setphysicalvolumesize", size_mb_arg, 0)
+arg(ignorelockingfailure_ARG, '\0', "ignorelockingfailure", NULL, 0)
+arg(nolocking_ARG, '\0', "nolocking", NULL, 0)
+arg(pvmetadatacopies_ARG, '\0', "pvmetadatacopies", int_arg, 0)
+arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", metadatacopies_arg, 0)
+arg(metadatacopies_ARG, '\0', "metadatacopies", metadatacopies_arg, 0)
+arg(metadatasize_ARG, '\0', "metadatasize", size_mb_arg, 0)
+arg(metadataignore_ARG, '\0', "metadataignore", yes_no_arg, 0)
+arg(norestorefile_ARG, '\0', "norestorefile", NULL, 0)
+arg(restorefile_ARG, '\0', "restorefile", string_arg, 0)
+arg(labelsector_ARG, '\0', "labelsector", int_arg, 0)
+arg(driverloaded_ARG, '\0', "driverloaded", yes_no_arg, 0)
+arg(aligned_ARG, '\0', "aligned", NULL, 0)
+arg(unbuffered_ARG, '\0', "unbuffered", NULL, 0)
+arg(noheadings_ARG, '\0', "noheadings", NULL, 0)
+arg(segments_ARG, '\0', "segments", NULL, 0)
+arg(units_ARG, '\0', "units", string_arg, 0)
+arg(nosuffix_ARG, '\0', "nosuffix", NULL, 0)
+arg(removemissing_ARG, '\0', "removemissing", NULL, 0)
+arg(restoremissing_ARG, '\0', "restoremissing", NULL, 0)
+arg(abort_ARG, '\0', "abort", NULL, 0)
+arg(addtag_ARG, '\0', "addtag", tag_arg, ARG_GROUPABLE)
+arg(deltag_ARG, '\0', "deltag", tag_arg, ARG_GROUPABLE)
+arg(refresh_ARG, '\0', "refresh", NULL, 0)
+arg(mknodes_ARG, '\0', "mknodes", NULL, 0)
+arg(minor_ARG, '\0', "minor", minor_arg, 0)
+arg(type_ARG, '\0', "type", segtype_arg, 0)
+arg(alloc_ARG, '\0', "alloc", alloc_arg, 0)
+arg(separator_ARG, '\0', "separator", string_arg, 0)
+arg(mirrorsonly_ARG, '\0', "mirrorsonly", NULL, 0)
+arg(nosync_ARG, '\0', "nosync", NULL, 0)
+arg(resync_ARG, '\0', "resync", NULL, 0)
+arg(corelog_ARG, '\0', "corelog", NULL, 0)
+arg(mirrorlog_ARG, '\0', "mirrorlog", string_arg, 0)
+arg(splitmirrors_ARG, '\0', "splitmirrors", int_arg, 0)
+arg(repair_ARG, '\0', "repair", NULL, 0)
+arg(use_policies_ARG, '\0', "use-policies", NULL, 0)
+arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0)
+arg(config_ARG, '\0', "config", string_arg, 0)
+arg(trustcache_ARG, '\0', "trustcache", NULL, 0)
+arg(ignoremonitoring_ARG, '\0', "ignoremonitoring", NULL, 0)
+arg(nameprefixes_ARG, '\0', "nameprefixes", NULL, 0)
+arg(unquoted_ARG, '\0', "unquoted", NULL, 0)
+arg(rows_ARG, '\0', "rows", NULL, 0)
+arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0)
+arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", size_kb_arg, 0)
+arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0)
+arg(virtualsize_ARG, '\0', "virtualsize", size_mb_arg, 0)
+arg(noudevsync_ARG, '\0', "noudevsync", NULL, 0)
+arg(poll_ARG, '\0', "poll", yes_no_arg, 0)
+arg(stripes_long_ARG, '\0', "stripes", int_arg, 0)
+arg(sysinit_ARG, '\0', "sysinit", NULL, 0)
+
+/* Allow some variations */
+arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0)
+arg(allocation_ARG, '\0', "allocation", yes_no_arg, 0)
+
+/*
+ * ... and now the short args.
+ */
+arg(available_ARG, 'a', "available", yes_no_excl_arg, 0)
+arg(all_ARG, 'a', "all", NULL, 0)
+arg(autobackup_ARG, 'A', "autobackup", yes_no_arg, 0)
+arg(activevolumegroups_ARG, 'A', "activevolumegroups", NULL, 0)
+arg(background_ARG, 'b', "background", NULL, 0)
+arg(blockdevice_ARG, 'b', "blockdevice", NULL, 0)
+arg(chunksize_ARG, 'c', "chunksize", size_kb_arg, 0)
+arg(clustered_ARG, 'c', "clustered", yes_no_arg, 0)
+arg(colon_ARG, 'c', "colon", NULL, 0)
+arg(columns_ARG, 'C', "columns", NULL, 0)
+arg(contiguous_ARG, 'C', "contiguous", yes_no_arg, 0)
+arg(debug_ARG, 'd', "debug", NULL, ARG_COUNTABLE)
+arg(exported_ARG, 'e', "exported", NULL, 0)
+arg(physicalextent_ARG, 'E', "physicalextent", NULL, 0)
+arg(file_ARG, 'f', "file", string_arg, 0)
+arg(force_ARG, 'f', "force", NULL, ARG_COUNTABLE)
+arg(full_ARG, 'f', "full", NULL, 0)
+arg(help_ARG, 'h', "help", NULL, 0)
+arg(help2_ARG, '?', "", NULL, 0)
+arg(stripesize_ARG, 'I', "stripesize", size_kb_arg, 0)
+arg(stripes_ARG, 'i', "stripes", int_arg, 0)
+arg(interval_ARG, 'i', "interval", int_arg, 0)
+arg(iop_version_ARG, 'i', "iop_version", NULL, 0)
+arg(logicalvolume_ARG, 'l', "logicalvolume", int_arg, 0)
+arg(maxlogicalvolumes_ARG, 'l', "maxlogicalvolumes", int_arg, 0)
+arg(extents_ARG, 'l', "extents", int_arg_with_sign_and_percent, 0)
+arg(lvmpartition_ARG, 'l', "lvmpartition", NULL, 0)
+arg(list_ARG, 'l', "list", NULL, 0)
+arg(size_ARG, 'L', "size", size_mb_arg, 0)
+arg(logicalextent_ARG, 'L', "logicalextent", int_arg_with_sign, 0)
+arg(persistent_ARG, 'M', "persistent", yes_no_arg, 0)
+arg(merge_ARG, '\0', "merge", NULL, 0)
+arg(major_ARG, 'j', "major", major_arg, 0)
+arg(mirrors_ARG, 'm', "mirrors", int_arg_with_sign, 0)
+arg(metadatatype_ARG, 'M', "metadatatype", metadatatype_arg, 0)
+arg(maps_ARG, 'm', "maps", NULL, 0)
+arg(name_ARG, 'n', "name", string_arg, 0)
+arg(oldpath_ARG, 'n', "oldpath", NULL, 0)
+arg(nofsck_ARG, 'n', "nofsck", NULL, 0)
+arg(novolumegroup_ARG, 'n', "novolumegroup", NULL, 0)
+arg(options_ARG, 'o', "options", string_arg, 0)
+arg(sort_ARG, 'O', "sort", string_arg, 0)
+arg(permission_ARG, 'p', "permission", permission_arg, 0)
+arg(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg, 0)
+arg(partial_ARG, 'P', "partial", NULL, 0)
+arg(physicalvolume_ARG, 'P', "physicalvolume", NULL, 0)
+arg(readahead_ARG, 'r', "readahead", readahead_arg, 0)
+arg(resizefs_ARG, 'r', "resizefs", NULL, 0)
+arg(reset_ARG, 'R', "reset", NULL, 0)
+arg(regionsize_ARG, 'R', "regionsize", size_mb_arg, 0)
+arg(physicalextentsize_ARG, 's', "physicalextentsize", size_mb_arg, 0)
+arg(stdin_ARG, 's', "stdin", NULL, 0)
+arg(snapshot_ARG, 's', "snapshot", NULL, 0)
+arg(short_ARG, 's', "short", NULL, 0)
+arg(test_ARG, 't', "test", NULL, 0)
+arg(uuid_ARG, 'u', "uuid", NULL, 0)
+arg(uuidstr_ARG, 'u', "uuid", string_arg, 0)
+arg(uuidlist_ARG, 'U', "uuidlist", NULL, 0)
+arg(verbose_ARG, 'v', "verbose", NULL, ARG_COUNTABLE)
+arg(volumegroup_ARG, 'V', "volumegroup", NULL, 0)
+arg(allocatable_ARG, 'x', "allocatable", yes_no_arg, 0)
+arg(resizeable_ARG, 'x', "resizeable", yes_no_arg, 0)
+arg(yes_ARG, 'y', "yes", NULL, 0)
+arg(zero_ARG, 'Z', "zero", yes_no_arg, 0)
+
+/* this should always be last */
+arg(ARG_COUNT, '-', "", NULL, 0)
+/* *INDENT-ON* */
diff --git a/tools/cmdnames.h b/tools/cmdnames.h
new file mode 100644 (file)
index 0000000..a5a4a90
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#define xx(a, b, c...) a
+
+#include "commands.h"
diff --git a/tools/commands.h b/tools/commands.h
new file mode 100644 (file)
index 0000000..fa177cd
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/***********  Replace with script?
+xx(e2fsadm,
+   "Resize logical volume and ext2 filesystem",
+   "e2fsadm "
+   "[-d|--debug] " "[-h|--help] " "[-n|--nofsck]" "\n"
+   "\t{[-l|--extents] [+|-]LogicalExtentsNumber |" "\n"
+   "\t [-L|--size] [+|-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}" "\n"
+   "\t[-t|--test] "  "\n"
+   "\t[-v|--verbose] "  "\n"
+   "\t[--version] " "\n"
+   "\tLogicalVolumePath" "\n",
+
+    extents_ARG, size_ARG, nofsck_ARG, test_ARG)
+*********/
+
+xx(dumpconfig,
+   "Dump active configuration",
+   PERMITTED_READ_ONLY,
+   "dumpconfig "
+   "\t[-f|--file filename] " "\n"
+   "[ConfigurationVariable...]\n",
+   file_ARG)
+
+xx(formats,
+   "List available metadata formats",
+   PERMITTED_READ_ONLY,
+   "formats\n")
+
+xx(help,
+   "Display help for commands",
+   PERMITTED_READ_ONLY,
+   "help <command>" "\n")
+
+/*********
+xx(lvactivate,
+   "Activate logical volume on given partition(s)",
+   "lvactivate "
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-v|--verbose]\n"
+   "Logical Volume(s)\n")
+***********/
+
+xx(lvchange,
+   "Change the attributes of logical volume(s)",
+   CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+   "lvchange\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-a|--available [e|l]y|n]\n"
+   "\t[--addtag Tag]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-C|--contiguous y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[--deltag Tag]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--ignoremonitoring]\n"
+   "\t[--monitor {y|n}]\n"
+   "\t[--poll {y|n}]\n"
+   "\t[--noudevsync]\n"
+   "\t[-M|--persistent y|n] [--major major] [--minor minor]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[-p|--permission r|rw]\n"
+   "\t[-r|--readahead ReadAheadSectors|auto|none]\n"
+   "\t[--refresh]\n"
+   "\t[--resync]\n"
+   "\t[--sysinit]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[-y|--yes]\n"
+   "\t[--version]" "\n"
+   "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
+
+   alloc_ARG, autobackup_ARG, available_ARG, contiguous_ARG, force_ARG,
+   ignorelockingfailure_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG,
+   monitor_ARG, noudevsync_ARG, partial_ARG, permission_ARG, persistent_ARG,
+   poll_ARG, readahead_ARG, resync_ARG, refresh_ARG, addtag_ARG, deltag_ARG,
+   sysinit_ARG, test_ARG, yes_ARG)
+
+xx(lvconvert,
+   "Change logical volume layout",
+   0,
+   "lvconvert "
+   "[-m|--mirrors Mirrors [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n"
+   "\t[--repair [--use-policies]]\n"
+   "\t[-R|--regionsize MirrorLogRegionSize]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-b|--background]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|-?|--help]\n"
+   "\t[-i|--interval seconds]\n"
+   "\t[--stripes Stripes [-I|--stripesize StripeSize]]\n"
+   "\t[--noudevsync]\n"
+   "\t[-v|--verbose]\n"
+   "\t[-y|--yes]\n"
+   "\t[--version]" "\n"
+   "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n"
+
+   "lvconvert "
+   "[--splitmirrors Images --name SplitLogicalVolumeName]\n"
+   "\tLogicalVolume[Path] [SplittablePhysicalVolume[Path]...]\n\n"
+
+   "lvconvert "
+   "[-s|--snapshot]\n"
+   "\t[-c|--chunksize]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|-?|--help]\n"
+   "\t[--noudevsync]\n"
+   "\t[-v|--verbose]\n"
+   "\t[-Z|--zero {y|n}]\n"
+   "\t[--version]" "\n"
+   "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n\n"
+
+   "lvconvert "
+   "--merge\n"
+   "\t[-b|--background]\n"
+   "\t[-i|--interval seconds]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|-?|--help]\n"
+   "\t[-v|--verbose]\n"
+   "\tSnapshotLogicalVolume[Path]\n",
+
+   alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
+   merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, noudevsync_ARG,
+   regionsize_ARG, repair_ARG, snapshot_ARG, splitmirrors_ARG,
+   stripes_long_ARG, stripesize_ARG, test_ARG,
+   use_policies_ARG, yes_ARG, force_ARG, zero_ARG)
+
+xx(lvcreate,
+   "Create a logical volume",
+   0,
+   "lvcreate " "\n"
+   "\t[-A|--autobackup {y|n}]\n"
+   "\t[--addtag Tag]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-C|--contiguous {y|n}]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|-?|--help]\n"
+   "\t[--ignoremonitoring]\n"
+   "\t[--monitor {y|n}]\n"
+   "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+   "\t{-l|--extents LogicalExtentsNumber[%{VG|PVS|FREE}] |\n"
+   "\t -L|--size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+   "\t[-M|--persistent {y|n}] [--major major] [--minor minor]\n"
+   "\t[-m|--mirrors Mirrors [--nosync] [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n"
+   "\t[-n|--name LogicalVolumeName]\n"
+   "\t[--noudevsync]\n"
+   "\t[-p|--permission {r|rw}]\n"
+   "\t[-r|--readahead ReadAheadSectors|auto|none]\n"
+   "\t[-R|--regionsize MirrorLogRegionSize]\n"
+   "\t[-t|--test]\n"
+   "\t[--type VolumeType]\n"
+   "\t[-v|--verbose]\n"
+   "\t[-Z|--zero {y|n}]\n"
+   "\t[--version]\n"
+   "\tVolumeGroupName [PhysicalVolumePath...]\n\n"
+
+   "lvcreate \n"
+   "\t{ {-s|--snapshot} OriginalLogicalVolume[Path] |\n"
+   "\t  [-s|--snapshot] VolumeGroupName[Path] --virtualsize VirtualSize}\n"
+   "\t[-c|--chunksize]\n"
+   "\t[-A|--autobackup {y|n}]\n"
+   "\t[--addtag Tag]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-C|--contiguous {y|n}]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|-?|--help]\n"
+   "\t[--ignoremonitoring]\n"
+   "\t[--monitor {y|n}]\n"
+   "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+   "\t{-l|--extents LogicalExtentsNumber[%{VG|FREE|ORIGIN}] |\n"
+   "\t -L|--size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+   "\t[-M|--persistent {y|n}] [--major major] [--minor minor]\n"
+   "\t[-n|--name LogicalVolumeName]\n"
+   "\t[--noudevsync]\n"
+   "\t[-p|--permission {r|rw}]\n"
+   "\t[-r|--readahead ReadAheadSectors|auto|none]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]\n"
+
+   "\t[PhysicalVolumePath...]\n\n",
+
+   addtag_ARG, alloc_ARG, autobackup_ARG, chunksize_ARG, contiguous_ARG,
+   corelog_ARG, extents_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG,
+   mirrorlog_ARG, mirrors_ARG, monitor_ARG, name_ARG, nosync_ARG, noudevsync_ARG,
+   permission_ARG, persistent_ARG, readahead_ARG, regionsize_ARG, size_ARG,
+   snapshot_ARG, stripes_ARG, stripesize_ARG, test_ARG, type_ARG,
+   virtualoriginsize_ARG, virtualsize_ARG, zero_ARG)
+
+xx(lvdisplay,
+   "Display information about a logical volume",
+   PERMITTED_READ_ONLY,
+   "lvdisplay\n"
+   "\t[-a|--all]\n"
+   "\t[-c|--colon]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[-m|--maps]\n"
+   "\t[--nosuffix]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n"
+   "\n"
+   "lvdisplay --columns|-C\n"
+   "\t[--aligned]\n"
+   "\t[-a|--all]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--noheadings]\n"
+   "\t[--nosuffix]\n"
+   "\t[-o|--options [+]Field[,Field]]\n"
+   "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--segments]\n"
+   "\t[--separator Separator]\n"
+   "\t[--unbuffered]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n",
+
+    aligned_ARG, all_ARG, colon_ARG, columns_ARG,
+    ignorelockingfailure_ARG, maps_ARG, noheadings_ARG, nosuffix_ARG,
+    options_ARG, sort_ARG, partial_ARG, segments_ARG, separator_ARG,
+    unbuffered_ARG, units_ARG)
+
+xx(lvextend,
+   "Add space to a logical volume",
+   0,
+   "lvextend\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+   "\t{-l|--extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |\n"
+   "\t -L|--size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+   "\t[-m|--mirrors Mirrors]\n"
+   "\t[--use-policies]\n"
+   "\t[-n|--nofsck]\n"
+   "\t[--noudevsync]\n"
+   "\t[-r|--resizefs]\n"
+   "\t[-t|--test]\n"
+   "\t[--type VolumeType]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
+
+   alloc_ARG, autobackup_ARG, extents_ARG, force_ARG, mirrors_ARG,
+   nofsck_ARG, noudevsync_ARG, resizefs_ARG, size_ARG, stripes_ARG,
+   stripesize_ARG, test_ARG, type_ARG, use_policies_ARG)
+
+xx(lvmchange,
+   "With the device mapper, this is obsolete and does nothing.",
+   0,
+   "lvmchange\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-R|--reset]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n",
+
+    reset_ARG)
+
+xx(lvmdiskscan,
+   "List devices that may be used as physical volumes",
+   PERMITTED_READ_ONLY,
+   "lvmdiskscan\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-l|--lvmpartition]\n"
+   "\t[--version]" "\n",
+
+   lvmpartition_ARG)
+
+xx(lvmsadc,
+   "Collect activity data",
+   0,
+   "lvmsadc\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[LogFilePath]\n" )
+
+xx(lvmsar,
+   "Create activity report",
+   0,
+   "lvmsar\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--full]\n"
+   "\t[-h|--help]\n"
+   "\t[-s|--stdin]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tLogFilePath\n",
+
+   full_ARG, stdin_ARG)
+
+xx(lvreduce,
+   "Reduce the size of a logical volume",
+   0,
+   "lvreduce\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t{-l|--extents [-]LogicalExtentsNumber[%{VG|LV|FREE|ORIGIN}] |\n"
+   "\t -L|--size [-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+   "\t[-n|--nofsck]\n"
+   "\t[--noudevsync]\n"
+   "\t[-r|--resizefs]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[-y|--yes]\n"
+   "\t[--version]" "\n"
+   "\tLogicalVolume[Path]\n",
+
+   autobackup_ARG, force_ARG,  extents_ARG, nofsck_ARG, noudevsync_ARG,
+   resizefs_ARG, size_ARG, test_ARG, yes_ARG)
+
+xx(lvremove,
+   "Remove logical volume(s) from the system",
+   0,
+   "lvremove\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[--noudevsync]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
+
+   autobackup_ARG, force_ARG, noudevsync_ARG, test_ARG)
+
+xx(lvrename,
+   "Rename a logical volume",
+   0,
+   "lvrename "
+   "\t[-A|--autobackup {y|n}] " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[--noudevsync]\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose]" "\n"
+   "\t[--version] " "\n"
+   "\t{ OldLogicalVolumePath NewLogicalVolumePath |" "\n"
+   "\t  VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName }\n",
+
+   autobackup_ARG, noudevsync_ARG, test_ARG)
+
+xx(lvresize,
+   "Resize a logical volume",
+   0,
+   "lvresize\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+   "\t{-l|--extents [+|-]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |\n"
+   "\t -L|--size [+|-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+   "\t[-n|--nofsck]\n"
+   "\t[--noudevsync]\n"
+   "\t[-r|--resizefs]\n"
+   "\t[-t|--test]\n"
+   "\t[--type VolumeType]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
+
+   alloc_ARG, autobackup_ARG, extents_ARG, force_ARG, nofsck_ARG,
+   noudevsync_ARG, resizefs_ARG, size_ARG, stripes_ARG, stripesize_ARG,
+   test_ARG, type_ARG)
+
+xx(lvs,
+   "Display information about logical volumes",
+   PERMITTED_READ_ONLY,
+   "lvs" "\n"
+   "\t[-a|--all]\n"
+   "\t[--aligned]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--nameprefixes]\n"
+   "\t[--noheadings]\n"
+   "\t[--nosuffix]\n"
+   "\t[-o|--options [+]Field[,Field]]\n"
+   "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--rows]\n"
+   "\t[--segments]\n"
+   "\t[--separator Separator]\n"
+   "\t[--trustcache]\n"
+   "\t[--unbuffered]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[--unquoted]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n",
+
+   aligned_ARG, all_ARG, ignorelockingfailure_ARG, nameprefixes_ARG,
+   noheadings_ARG, nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG, 
+   rows_ARG, segments_ARG, separator_ARG, sort_ARG, trustcache_ARG,
+   unbuffered_ARG, units_ARG, unquoted_ARG)
+
+xx(lvscan,
+   "List all logical volumes in all volume groups",
+   PERMITTED_READ_ONLY,
+   "lvscan " "\n"
+   "\t[-a|--all]\n"
+   "\t[-b|--blockdevice] " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version]\n",
+
+   all_ARG, blockdevice_ARG, ignorelockingfailure_ARG, partial_ARG)
+
+xx(pvchange,
+   "Change attributes of physical volume(s)",
+   0,
+   "pvchange\n"
+   "\t[-a|--all]\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[-t|--test]\n"
+   "\t[-u|--uuid]\n"
+   "\t[-x|--allocatable y|n]\n"
+   "\t[--metadataignore y|n]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--addtag Tag]\n"
+   "\t[--deltag Tag]\n"
+   "\t[--version]" "\n"
+   "\t[PhysicalVolumePath...]\n",
+
+   all_ARG, allocatable_ARG, allocation_ARG, autobackup_ARG, deltag_ARG,
+   addtag_ARG, force_ARG, metadataignore_ARG, test_ARG, uuid_ARG)
+
+xx(pvresize,
+   "Resize physical volume(s)",
+   0,
+   "pvresize " "\n"
+   "\t[-d|--debug]" "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[--setphysicalvolumesize PhysicalVolumeSize[bBsSkKmMgGtTpPeE]" "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version] " "\n"
+   "\tPhysicalVolume [PhysicalVolume...]\n",
+
+   physicalvolumesize_ARG, test_ARG)
+
+xx(pvck,
+   "Check the consistency of physical volume(s)",
+   0,
+   "pvck "
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--labelsector sector] " "\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tPhysicalVolume [PhysicalVolume...]\n",
+
+   labelsector_ARG)
+
+xx(pvcreate,
+   "Initialize physical volume(s) for use by LVM",
+   0,
+   "pvcreate " "\n"
+   "\t[--norestorefile]\n"
+   "\t[--restorefile file]\n"
+   "\t[-d|--debug]" "\n"
+   "\t[-f[f]|--force [--force]] " "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[--labelsector sector] " "\n"
+   "\t[-M|--metadatatype 1|2]" "\n"
+   "\t[--pvmetadatacopies #copies]" "\n"
+   "\t[--metadatasize MetadataSize[bBsSkKmMgGtTpPeE]]" "\n"
+   "\t[--dataalignment Alignment[bBsSkKmMgGtTpPeE]]" "\n"
+   "\t[--dataalignmentoffset AlignmentOffset[bBsSkKmMgGtTpPeE]]" "\n"
+   "\t[--setphysicalvolumesize PhysicalVolumeSize[bBsSkKmMgGtTpPeE]" "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-u|--uuid uuid] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[-y|--yes]" "\n"
+   "\t[-Z|--zero {y|n}]\n"
+   "\t[--version] " "\n"
+   "\tPhysicalVolume [PhysicalVolume...]\n",
+
+   dataalignment_ARG, dataalignmentoffset_ARG, force_ARG, test_ARG,
+   labelsector_ARG, metadatatype_ARG, metadatacopies_ARG,
+   metadatasize_ARG, metadataignore_ARG, norestorefile_ARG,
+   physicalvolumesize_ARG, pvmetadatacopies_ARG,
+   restorefile_ARG, uuidstr_ARG, yes_ARG, zero_ARG)
+
+xx(pvdata,
+   "Display the on-disk metadata for physical volume(s)",
+   0,
+   "pvdata " "\n"
+   "\t[-a|--all] " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-E|--physicalextent] " "\n"
+   "\t[-h|-?|--help]" "\n"
+   "\t[-L|--logicalvolume] " "\n"
+   "\t[-P[P]|--physicalvolume [--physicalvolume]]" "\n"
+   "\t[-U|--uuidlist] " "\n"
+   "\t[-v[v]|--verbose [--verbose]] " "\n"
+   "\t[-V|--volumegroup]" "\n"
+   "\t[--version] " "\n"
+   "\tPhysicalVolume [PhysicalVolume...]\n",
+
+   all_ARG,  logicalextent_ARG, physicalextent_ARG,
+   physicalvolume_ARG, uuidlist_ARG, volumegroup_ARG)
+
+xx(pvdisplay,
+   "Display various attributes of physical volume(s)",
+   CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+   "pvdisplay\n"
+   "\t[-c|--colon]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[-m|--maps]\n"
+   "\t[--nosuffix]\n"
+   "\t[-s|--short]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[PhysicalVolumePath [PhysicalVolumePath...]]\n"
+   "\n"
+   "pvdisplay --columns|-C\n"
+   "\t[--aligned]\n"
+   "\t[-a|--all]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--noheadings]\n"
+   "\t[--nosuffix]\n"
+   "\t[-o|--options [+]Field[,Field]]\n"
+   "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+   "\t[--separator Separator]\n"
+   "\t[--unbuffered]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[PhysicalVolumePath [PhysicalVolumePath...]]\n",
+
+   aligned_ARG, all_ARG, colon_ARG, columns_ARG, ignorelockingfailure_ARG,
+   maps_ARG, noheadings_ARG, nosuffix_ARG, options_ARG, separator_ARG,
+   short_ARG, sort_ARG, unbuffered_ARG, units_ARG)
+
+xx(pvmove,
+   "Move extents from one physical volume to another",
+   0,
+   "pvmove " "\n"
+   "\t[--abort]\n"
+   "\t[-A|--autobackup {y|n}]\n"
+   "\t[--alloc AllocationPolicy]\n"
+   "\t[-b|--background]\n"
+   "\t[-d|--debug]\n "
+   "\t[-h|-?|--help]\n"
+   "\t[-i|--interval seconds]\n"
+   "\t[--noudevsync]\n"
+   "\t[-t|--test]\n "
+   "\t[-v|--verbose]\n "
+   "\t[--version]\n"
+   "\t[{-n|--name} LogicalVolume]\n"
+/* "\t[{-n|--name} LogicalVolume[:LogicalExtent[-LogicalExtent]...]]\n" */
+   "\tSourcePhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]}\n"
+   "\t[DestinationPhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]...]\n",
+
+   abort_ARG, alloc_ARG, autobackup_ARG, background_ARG,
+   interval_ARG, name_ARG, noudevsync_ARG, test_ARG)
+
+xx(pvremove,
+   "Remove LVM label(s) from physical volume(s)",
+   0,
+   "pvremove " "\n"
+   "\t[-d|--debug]" "\n"
+   "\t[-f[f]|--force [--force]] " "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[-y|--yes]" "\n"
+   "\t[--version] " "\n"
+   "\tPhysicalVolume [PhysicalVolume...]\n",
+
+   force_ARG, test_ARG, yes_ARG)
+
+xx(pvs,
+   "Display information about physical volumes",
+   CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+   "pvs" "\n"
+   "\t[-a|--all]\n"
+   "\t[--aligned]\n"
+   "\t[-d|--debug]" "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--nameprefixes]\n"
+   "\t[--noheadings]\n"
+   "\t[--nosuffix]\n"
+   "\t[-o|--options [+]Field[,Field]]\n"
+   "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--rows]\n"
+   "\t[--segments]\n"
+   "\t[--separator Separator]\n"
+   "\t[--trustcache]\n"
+   "\t[--unbuffered]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[--unquoted]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]\n"
+   "\t[PhysicalVolume [PhysicalVolume...]]\n",
+
+   aligned_ARG, all_ARG, ignorelockingfailure_ARG, nameprefixes_ARG,
+   noheadings_ARG, nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG,
+   rows_ARG, segments_ARG, separator_ARG, sort_ARG, trustcache_ARG,
+   unbuffered_ARG, units_ARG, unquoted_ARG)
+
+xx(pvscan,
+   "List all physical volumes",
+   PERMITTED_READ_ONLY,
+   "pvscan " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t{-e|--exported | -n|--novolumegroup} " "\n"
+   "\t[-h|-?|--help]" "\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[-s|--short] " "\n"
+   "\t[-u|--uuid] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version]\n",
+
+   exported_ARG, ignorelockingfailure_ARG, novolumegroup_ARG, partial_ARG,
+   short_ARG, uuid_ARG)
+
+xx(segtypes,
+   "List available segment types",
+   PERMITTED_READ_ONLY,
+   "segtypes\n")
+
+xx(vgcfgbackup,
+   "Backup volume group configuration(s)",
+   PERMITTED_READ_ONLY,
+   "vgcfgbackup " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-f|--file filename] " "\n"
+   "\t[-h|-?|--help] " "\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[-v|--verbose]" "\n"
+   "\t[--version] " "\n"
+   "\t[VolumeGroupName...]\n",
+
+   file_ARG, ignorelockingfailure_ARG, partial_ARG)
+
+xx(vgcfgrestore,
+   "Restore volume group configuration",
+   0,
+   "vgcfgrestore " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-f|--file filename] " "\n"
+   "\t[-l[l]|--list [--list]]" "\n"
+   "\t[-M|--metadatatype 1|2]" "\n"
+   "\t[-h|--help]" "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose]" "\n"
+   "\t[--version] " "\n"
+   "\tVolumeGroupName",
+
+   file_ARG, list_ARG, metadatatype_ARG, test_ARG)
+
+xx(vgchange,
+   "Change volume group attributes",
+   CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+   "vgchange" "\n"
+   "\t[-A|--autobackup {y|n}] " "\n"
+   "\t[--alloc AllocationPolicy] " "\n"
+   "\t[-P|--partial] " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|--help] " "\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--ignoremonitoring]\n"
+   "\t[--monitor {y|n}]\n"
+   "\t[--[vg]metadatacopies #copies] " "\n"
+   "\t[--poll {y|n}]\n"
+   "\t[--noudevsync]\n"
+   "\t[--refresh]\n"
+   "\t[--sysinit]\n"
+   "\t[-t|--test]" "\n"
+   "\t[-u|--uuid] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version]" "\n"
+   "\t{-a|--available [e|l]{y|n}  |" "\n"
+   "\t -c|--clustered {y|n} |" "\n"
+   "\t -x|--resizeable {y|n} |" "\n"
+   "\t -l|--logicalvolume MaxLogicalVolumes |" "\n"
+   "\t -p|--maxphysicalvolumes MaxPhysicalVolumes |" "\n"
+   "\t -s|--physicalextentsize PhysicalExtentSize[bBsSkKmMgGtTpPeE] |" "\n"
+   "\t --addtag Tag |\n"
+   "\t --deltag Tag}\n"
+   "\t[VolumeGroupName...]\n",
+
+   addtag_ARG, alloc_ARG, allocation_ARG, autobackup_ARG, available_ARG,
+   clustered_ARG, deltag_ARG, ignorelockingfailure_ARG, ignoremonitoring_ARG,
+   logicalvolume_ARG, maxphysicalvolumes_ARG, monitor_ARG, noudevsync_ARG,
+   metadatacopies_ARG, vgmetadatacopies_ARG, partial_ARG,
+   physicalextentsize_ARG, poll_ARG, refresh_ARG, resizeable_ARG,
+   resizable_ARG, sysinit_ARG, test_ARG, uuid_ARG)
+
+xx(vgck,
+   "Check the consistency of volume group(s)",
+   0,
+   "vgck "
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[VolumeGroupName...]\n" )
+
+xx(vgconvert,
+   "Change volume group metadata format",
+   0,
+   "vgconvert  " "\n"
+   "\t[-d|--debug]" "\n"
+   "\t[-h|--help] " "\n"
+   "\t[--labelsector sector] " "\n"
+   "\t[-M|--metadatatype 1|2]" "\n"
+   "\t[--pvmetadatacopies #copies]" "\n"
+   "\t[--metadatasize MetadataSize[bBsSkKmMgGtTpPeE]]" "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version] " "\n"
+   "\tVolumeGroupName [VolumeGroupName...]\n",
+
+   force_ARG, test_ARG, labelsector_ARG, metadatatype_ARG, metadatacopies_ARG,
+   pvmetadatacopies_ARG, metadatasize_ARG )
+
+xx(vgcreate,
+   "Create a volume group",
+   0,
+   "vgcreate" "\n"
+   "\t[-A|--autobackup {y|n}] " "\n"
+   "\t[--addtag Tag] " "\n"
+   "\t[--alloc AllocationPolicy] " "\n"
+   "\t[-c|--clustered {y|n}] " "\n"
+   "\t[-d|--debug]" "\n"
+   "\t[-h|--help]" "\n"
+   "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n"
+   "\t[-M|--metadatatype 1|2] " "\n"
+   "\t[--[vg]metadatacopies #copies] " "\n"
+   "\t[-p|--maxphysicalvolumes MaxPhysicalVolumes] " "\n"
+   "\t[-s|--physicalextentsize PhysicalExtentSize[bBsSkKmMgGtTpPeE]] " "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose]" "\n"
+   "\t[--version] " "\n"
+   "\t[ PHYSICAL DEVICE OPTIONS ] " "\n"
+   "\tVolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]\n",
+
+   addtag_ARG, alloc_ARG, autobackup_ARG, clustered_ARG, maxlogicalvolumes_ARG,
+   maxphysicalvolumes_ARG, metadatatype_ARG, physicalextentsize_ARG, test_ARG,
+   force_ARG, yes_ARG, zero_ARG, labelsector_ARG, metadatasize_ARG,
+   pvmetadatacopies_ARG, metadatacopies_ARG, vgmetadatacopies_ARG,
+   dataalignment_ARG, dataalignmentoffset_ARG)
+
+xx(vgdisplay,
+   "Display volume group information",
+   PERMITTED_READ_ONLY,
+   "vgdisplay " "\n"
+   "\t[-A|--activevolumegroups]" "\n"
+   "\t[-c|--colon | -s|--short | -v|--verbose]" "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|--help] " "\n"
+   "\t[--ignorelockingfailure]" "\n"
+   "\t[--nosuffix]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[--version]" "\n"
+   "\t[VolumeGroupName [VolumeGroupName...]]\n"
+   "\n"
+   "vgdisplay --columns|-C\n"
+   "\t[--aligned]\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|--help] " "\n"
+   "\t[--ignorelockingfailure]" "\n"
+   "\t[--noheadings]\n"
+   "\t[--nosuffix]\n"
+   "\t[-o|--options [+]Field[,Field]]\n"
+   "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--separator Separator]\n"
+   "\t[--unbuffered]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[--verbose]" "\n"
+   "\t[--version]" "\n"
+   "\t[VolumeGroupName [VolumeGroupName...]]\n",
+
+   activevolumegroups_ARG, aligned_ARG, colon_ARG, columns_ARG,
+   ignorelockingfailure_ARG, noheadings_ARG, nosuffix_ARG, options_ARG,
+   partial_ARG, short_ARG, separator_ARG, sort_ARG, unbuffered_ARG, units_ARG)
+
+xx(vgexport,
+   "Unregister volume group(s) from the system",
+   0,
+   "vgexport " "\n"
+   "\t[-a|--all] " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|--help]" "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version] " "\n"
+   "\tVolumeGroupName [VolumeGroupName...]\n",
+
+   all_ARG, test_ARG)
+
+xx(vgextend,
+   "Add physical volumes to a volume group",
+   0,
+   "vgextend\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[--restoremissing]\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[ PHYSICAL DEVICE OPTIONS ] " "\n"
+   "\tVolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]\n",
+
+   autobackup_ARG, test_ARG,
+   force_ARG, yes_ARG, zero_ARG, labelsector_ARG, metadatatype_ARG,
+   metadatasize_ARG, pvmetadatacopies_ARG, metadatacopies_ARG,
+   metadataignore_ARG, dataalignment_ARG, dataalignmentoffset_ARG,
+   restoremissing_ARG)
+
+xx(vgimport,
+   "Register exported volume group with system",
+   0,
+   "vgimport " "\n"
+   "\t[-a|--all]\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-f|--force] " "\n"
+   "\t[-h|--help] " "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose]" "\n"
+   "\t[--version]" "\n"
+   "\tVolumeGroupName..." "\n",
+
+   all_ARG, force_ARG, test_ARG)
+
+xx(vgmerge,
+   "Merge volume groups",
+   0,
+   "vgmerge\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-l|--list]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tDestinationVolumeGroupName SourceVolumeGroupName\n",
+
+   autobackup_ARG, list_ARG, test_ARG)
+
+xx(vgmknodes,
+   "Create the special files for volume group devices in /dev",
+   0,
+   "vgmknodes\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--refresh]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\t[VolumeGroupName...]\n",
+
+   ignorelockingfailure_ARG, refresh_ARG)
+
+xx(vgreduce,
+   "Remove physical volume(s) from a volume group",
+   0,
+   "vgreduce\n"
+   "\t[-a|--all]\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--mirrorsonly]\n"
+   "\t[--removemissing]\n"
+   "\t[-f|--force]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tVolumeGroupName\n"
+   "\t[PhysicalVolumePath...]\n",
+
+   all_ARG, autobackup_ARG, force_ARG, mirrorsonly_ARG, removemissing_ARG,
+   test_ARG)
+
+xx(vgremove,
+   "Remove volume group(s)",
+   0,
+   "vgremove\n"
+   "\t[-d|--debug]\n"
+   "\t[-f|--force]\n"
+   "\t[-h|--help]\n"
+   "\t[--noudevsync]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tVolumeGroupName [VolumeGroupName...]\n",
+
+   force_ARG, noudevsync_ARG, test_ARG)
+
+xx(vgrename,
+   "Rename a volume group",
+   0,
+   "vgrename\n"
+   "\t[-A|--autobackup y|n]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[-t|--test]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n"
+   "\tOldVolumeGroupPath NewVolumeGroupPath |\n"
+   "\tOldVolumeGroupName NewVolumeGroupName\n",
+
+   autobackup_ARG, force_ARG, test_ARG)
+
+xx(vgs,
+   "Display information about volume groups",
+   PERMITTED_READ_ONLY,
+   "vgs" "\n"
+   "\t[--aligned]\n"
+   "\t[-a|--all]\n"
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--nameprefixes]\n"
+   "\t[--noheadings]\n"
+   "\t[--nosuffix]\n"
+   "\t[-o|--options [+]Field[,Field]]\n"
+   "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[--rows]\n"
+   "\t[--separator Separator]\n"
+   "\t[--trustcache]\n"
+   "\t[--unbuffered]\n"
+   "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+   "\t[--unquoted]\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]\n"
+   "\t[VolumeGroupName [VolumeGroupName...]]\n",
+
+   aligned_ARG, all_ARG, ignorelockingfailure_ARG, nameprefixes_ARG,
+   noheadings_ARG, nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG, 
+   rows_ARG, separator_ARG, sort_ARG, trustcache_ARG, unbuffered_ARG, units_ARG,
+   unquoted_ARG)
+
+xx(vgscan,
+   "Search for all volume groups",
+   PERMITTED_READ_ONLY,
+   "vgscan "
+   "\t[-d|--debug]\n"
+   "\t[-h|--help]\n"
+   "\t[--ignorelockingfailure]\n"
+   "\t[--mknodes]\n"
+   "\t[-P|--partial] " "\n"
+   "\t[-v|--verbose]\n"
+   "\t[--version]" "\n",
+
+   ignorelockingfailure_ARG, mknodes_ARG, partial_ARG)
+
+xx(vgsplit,
+   "Move physical volumes into a new or existing volume group",
+   0,
+   "vgsplit " "\n"
+   "\t[-A|--autobackup {y|n}] " "\n"
+   "\t[--alloc AllocationPolicy] " "\n"
+   "\t[-c|--clustered {y|n}] " "\n"
+   "\t[-d|--debug] " "\n"
+   "\t[-h|--help] " "\n"
+   "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n"
+   "\t[-M|--metadatatype 1|2] " "\n"
+   "\t[--[vg]metadatacopies #copies] " "\n"
+   "\t[-n|--name LogicalVolumeName]\n"
+   "\t[-p|--maxphysicalvolumes MaxPhysicalVolumes] " "\n"
+   "\t[-t|--test] " "\n"
+   "\t[-v|--verbose] " "\n"
+   "\t[--version]" "\n"
+   "\tSourceVolumeGroupName DestinationVolumeGroupName" "\n"
+   "\t[PhysicalVolumePath...]\n",
+
+   alloc_ARG, autobackup_ARG, clustered_ARG,
+   maxlogicalvolumes_ARG, maxphysicalvolumes_ARG,
+   metadatatype_ARG, vgmetadatacopies_ARG, name_ARG, test_ARG)
+
+xx(version,
+   "Display software and driver version information",
+   PERMITTED_READ_ONLY,
+   "version\n" )
+
diff --git a/tools/dmsetup.c b/tools/dmsetup.c
new file mode 100644 (file)
index 0000000..2eeee7f
--- /dev/null
@@ -0,0 +1,3412 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005-2007 NEC Corporation
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+
+#include "dm-logging.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <time.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef UDEV_SYNC_SUPPORT
+#  include <sys/types.h>
+#  include <sys/ipc.h>
+#  include <sys/sem.h>
+#  define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#  include <libudev.h>
+#endif
+
+/* FIXME Unused so far */
+#undef HAVE_SYS_STATVFS_H
+
+#ifdef HAVE_SYS_STATVFS_H
+#  include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#  include <sys/ioctl.h>
+#endif
+
+#if HAVE_TERMIOS_H
+#  include <termios.h>
+#endif
+
+#ifdef HAVE_GETOPTLONG
+#  include <getopt.h>
+#  define GETOPTLONG_FN(a, b, c, d, e) getopt_long((a), (b), (c), (d), (e))
+#  define OPTIND_INIT 0
+#else
+struct option {
+};
+extern int optind;
+extern char *optarg;
+#  define GETOPTLONG_FN(a, b, c, d, e) getopt((a), (b), (c))
+#  define OPTIND_INIT 1
+#endif
+
+#ifndef TEMP_FAILURE_RETRY
+# define TEMP_FAILURE_RETRY(expression) \
+  (__extension__                                                              \
+    ({ long int __result;                                                     \
+       do __result = (long int) (expression);                                 \
+       while (__result == -1L && errno == EINTR);                             \
+       __result; }))
+#endif
+
+#ifdef linux
+#  include "kdev_t.h"
+#else
+#  define MAJOR(x) major((x))
+#  define MINOR(x) minor((x))
+#  define MKDEV(x,y) makedev((x),(y))
+#endif
+
+#define LINE_SIZE 4096
+#define ARGS_MAX 256
+#define LOOP_TABLE_SIZE (PATH_MAX + 255)
+
+#define DEFAULT_DM_DEV_DIR "/dev/"
+
+#define DM_DEV_DIR_ENV_VAR_NAME "DM_DEV_DIR"
+#define DM_UDEV_COOKIE_ENV_VAR_NAME "DM_UDEV_COOKIE"
+
+/* FIXME Should be imported */
+#ifndef DM_MAX_TYPE_NAME
+#  define DM_MAX_TYPE_NAME 16
+#endif
+
+/* FIXME Should be elsewhere */
+#define SECTOR_SHIFT 9L
+
+#define err(msg, x...) fprintf(stderr, msg "\n", ##x)
+
+/*
+ * We have only very simple switches ATM.
+ */
+enum {
+       READ_ONLY = 0,
+       COLS_ARG,
+       EXEC_ARG,
+       FORCE_ARG,
+       GID_ARG,
+       HELP_ARG,
+       INACTIVE_ARG,
+       MAJOR_ARG,
+       MINOR_ARG,
+       MODE_ARG,
+       NAMEPREFIXES_ARG,
+       NOFLUSH_ARG,
+       NOHEADINGS_ARG,
+       NOLOCKFS_ARG,
+       NOOPENCOUNT_ARG,
+       NOTABLE_ARG,
+       UDEVCOOKIE_ARG,
+       NOUDEVRULES_ARG,
+       NOUDEVSYNC_ARG,
+       OPTIONS_ARG,
+       READAHEAD_ARG,
+       ROWS_ARG,
+       SEPARATOR_ARG,
+       SETUUID_ARG,
+       SHOWKEYS_ARG,
+       SORT_ARG,
+       TABLE_ARG,
+       TARGET_ARG,
+       TREE_ARG,
+       UID_ARG,
+       UNBUFFERED_ARG,
+       UNQUOTED_ARG,
+       UUID_ARG,
+       VERBOSE_ARG,
+       VERSION_ARG,
+       YES_ARG,
+       NUM_SWITCHES
+};
+
+typedef enum {
+       DR_TASK = 1,
+       DR_INFO = 2,
+       DR_DEPS = 4,
+       DR_TREE = 8,    /* Complete dependency tree required */
+       DR_NAME = 16
+} report_type_t;
+
+static int _switches[NUM_SWITCHES];
+static int _int_args[NUM_SWITCHES];
+static char *_string_args[NUM_SWITCHES];
+static int _num_devices;
+static char *_uuid;
+static char *_table;
+static char *_target;
+static char *_command;
+static uint32_t _read_ahead_flags;
+static uint32_t _udev_cookie;
+static int _udev_only;
+static struct dm_tree *_dtree;
+static struct dm_report *_report;
+static report_type_t _report_type;
+
+/*
+ * Commands
+ */
+
+typedef int (*command_fn) (int argc, char **argv, void *data);
+
+struct command {
+       const char *name;
+       const char *help;
+       int min_args;
+       int max_args;
+       command_fn fn;
+};
+
+static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
+                      int line)
+{
+       char ttype[LINE_SIZE], *ptr, *comment;
+       unsigned long long start, size;
+       int n;
+
+       /* trim trailing space */
+       for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr--)
+               if (!isspace((int) *ptr))
+                       break;
+       ptr++;
+       *ptr = '\0';
+
+       /* trim leading space */
+       for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++)
+               ;
+
+       if (!*ptr || *ptr == '#')
+               return 1;
+
+       if (sscanf(ptr, "%llu %llu %s %n",
+                  &start, &size, ttype, &n) < 3) {
+               err("Invalid format on line %d of table %s", line, file);
+               return 0;
+       }
+
+       ptr += n;
+       if ((comment = strchr(ptr, (int) '#')))
+               *comment = '\0';
+
+       if (!dm_task_add_target(dmt, start, size, ttype, ptr))
+               return 0;
+
+       return 1;
+}
+
+static int _parse_file(struct dm_task *dmt, const char *file)
+{
+       char *buffer = NULL;
+       size_t buffer_size = 0;
+       FILE *fp;
+       int r = 0, line = 0;
+
+       /* one-line table on cmdline */
+       if (_table)
+               return _parse_line(dmt, _table, "", ++line);
+
+       /* OK for empty stdin */
+       if (file) {
+               if (!(fp = fopen(file, "r"))) {
+                       err("Couldn't open '%s' for reading", file);
+                       return 0;
+               }
+       } else
+               fp = stdin;
+
+#ifndef HAVE_GETLINE
+       buffer_size = LINE_SIZE;
+       if (!(buffer = dm_malloc(buffer_size))) {
+               err("Failed to malloc line buffer.");
+               return 0;
+       }
+
+       while (fgets(buffer, (int) buffer_size, fp))
+#else
+       while (getline(&buffer, &buffer_size, fp) > 0)
+#endif
+               if (!_parse_line(dmt, buffer, file ? : "on stdin", ++line))
+                       goto out;
+
+       r = 1;
+
+      out:
+       memset(buffer, 0, buffer_size);
+#ifndef HAVE_GETLINE
+       dm_free(buffer);
+#else
+       free(buffer);
+#endif
+       if (file && fclose(fp))
+               fprintf(stderr, "%s: fclose failed: %s", file, strerror(errno));
+
+       return r;
+}
+
+struct dm_split_name {
+        char *subsystem;
+        char *vg_name;
+        char *lv_name;
+        char *lv_layer;
+};
+
+struct dmsetup_report_obj {
+       struct dm_task *task;
+       struct dm_info *info;
+       struct dm_task *deps_task;
+       struct dm_tree_node *tree_node;
+       struct dm_split_name *split_name;
+};
+
+static struct dm_task *_get_deps_task(int major, int minor)
+{
+       struct dm_task *dmt;
+       struct dm_info info;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+               return NULL;
+
+       if (!dm_task_set_major(dmt, major) ||
+           !dm_task_set_minor(dmt, minor))
+               goto err;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto err;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto err;
+
+       if (!dm_task_run(dmt))
+               goto err;
+
+       if (!dm_task_get_info(dmt, &info))
+               goto err;
+
+       if (!info.exists)
+               goto err;
+
+       return dmt;
+
+      err:
+       dm_task_destroy(dmt);
+       return NULL;
+}
+
+static char *_extract_uuid_prefix(const char *uuid, const int separator)
+{
+       char *ptr = NULL;
+       char *uuid_prefix = NULL;
+       size_t len;
+
+       if (uuid)
+               ptr = strchr(uuid, separator);
+
+       len = ptr ? ptr - uuid : 0;
+       if (!(uuid_prefix = dm_malloc(len + 1))) {
+               log_error("Failed to allocate memory to extract uuid prefix.");
+               return NULL;
+       }
+
+       if (uuid)
+               memcpy(uuid_prefix, uuid, len);
+
+       uuid_prefix[len] = '\0';
+
+       return uuid_prefix;
+}
+
+static struct dm_split_name *_get_split_name(const char *uuid, const char *name,
+                                            int separator)
+{
+       struct dm_split_name *split_name;
+
+       if (!(split_name = dm_malloc(sizeof(*split_name)))) {
+               log_error("Failed to allocate memory to split device name "
+                         "into components.");
+               return NULL;
+       }
+
+       split_name->subsystem = _extract_uuid_prefix(uuid, separator);
+       split_name->vg_name = split_name->lv_name =
+           split_name->lv_layer = (char *) "";
+
+       if (!strcmp(split_name->subsystem, "LVM") &&
+           (!(split_name->vg_name = dm_strdup(name)) ||
+            !dm_split_lvm_name(NULL, NULL, &split_name->vg_name,
+                               &split_name->lv_name, &split_name->lv_layer)))
+               log_error("Failed to allocate memory to split LVM name "
+                         "into components.");
+
+       return split_name;
+}
+
+static void _destroy_split_name(struct dm_split_name *split_name)
+{
+       /*
+        * lv_name and lv_layer are allocated within the same block
+        * of memory as vg_name so don't need to be freed separately.
+        */
+       if (!strcmp(split_name->subsystem, "LVM"))
+               dm_free(split_name->vg_name);
+
+       dm_free(split_name->subsystem);
+       dm_free(split_name);
+}
+
+static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
+{
+       struct dmsetup_report_obj obj;
+       int r = 0;
+
+       if (!info->exists) {
+               fprintf(stderr, "Device does not exist.\n");
+               return 0;
+       }
+
+       obj.task = dmt;
+       obj.info = info;
+       obj.deps_task = NULL;
+       obj.split_name = NULL;
+
+       if (_report_type & DR_TREE)
+               obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor);
+
+       if (_report_type & DR_DEPS)
+               obj.deps_task = _get_deps_task(info->major, info->minor);
+
+       if (_report_type & DR_NAME)
+               obj.split_name = _get_split_name(dm_task_get_uuid(dmt), dm_task_get_name(dmt), '-');
+
+       if (!dm_report_object(_report, &obj))
+               goto out;
+
+       r = 1;
+
+      out:
+       if (obj.deps_task)
+               dm_task_destroy(obj.deps_task);
+       if (obj.split_name)
+               _destroy_split_name(obj.split_name);
+       return r;
+}
+
+static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
+{
+       const char *uuid;
+       uint32_t read_ahead;
+
+       if (!info->exists) {
+               printf("Device does not exist.\n");
+               return;
+       }
+
+       printf("Name:              %s\n", dm_task_get_name(dmt));
+
+       printf("State:             %s%s\n",
+              info->suspended ? "SUSPENDED" : "ACTIVE",
+              info->read_only ? " (READ-ONLY)" : "");
+
+       /* FIXME Old value is being printed when it's being changed. */
+       if (dm_task_get_read_ahead(dmt, &read_ahead))
+               printf("Read Ahead:        %" PRIu32 "\n", read_ahead);
+
+       if (!info->live_table && !info->inactive_table)
+               printf("Tables present:    None\n");
+       else
+               printf("Tables present:    %s%s%s\n",
+                      info->live_table ? "LIVE" : "",
+                      info->live_table && info->inactive_table ? " & " : "",
+                      info->inactive_table ? "INACTIVE" : "");
+
+       if (info->open_count != -1)
+               printf("Open count:        %d\n", info->open_count);
+
+       printf("Event number:      %" PRIu32 "\n", info->event_nr);
+       printf("Major, minor:      %d, %d\n", info->major, info->minor);
+
+       if (info->target_count != -1)
+               printf("Number of targets: %d\n", info->target_count);
+
+       if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
+               printf("UUID: %s\n", uuid);
+
+       printf("\n");
+}
+
+static int _display_info(struct dm_task *dmt)
+{
+       struct dm_info info;
+
+       if (!dm_task_get_info(dmt, &info))
+               return 0;
+
+       if (!_switches[COLS_ARG])
+               _display_info_long(dmt, &info);
+       else
+               /* FIXME return code */
+               _display_info_cols(dmt, &info);
+
+       return info.exists ? 1 : 0;
+}
+
+static int _set_task_device(struct dm_task *dmt, const char *name, int optional)
+{
+       if (name) {
+               if (!dm_task_set_name(dmt, name))
+                       return 0;
+       } else if (_switches[UUID_ARG]) {
+               if (!dm_task_set_uuid(dmt, _uuid))
+                       return 0;
+       } else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]) {
+               if (!dm_task_set_major(dmt, _int_args[MAJOR_ARG]) ||
+                   !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
+                       return 0;
+       } else if (!optional) {
+               fprintf(stderr, "No device specified.\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _load(int argc, char **argv, void *data __attribute__((unused)))
+{
+       int r = 0;
+       struct dm_task *dmt;
+       const char *file = NULL;
+       const char *name = NULL;
+
+       if (_switches[NOTABLE_ARG]) {
+               err("--notable only available when creating new device\n");
+               return 0;
+       }
+
+       if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+               if (argc == 1) {
+                       err("Please specify device.\n");
+                       return 0;
+               }
+               name = argv[1];
+               argc--;
+               argv++;
+       } else if (argc > 2) {
+               err("Too many command line arguments.\n");
+               return 0;
+       }
+
+       if (argc == 2)
+               file = argv[1];
+
+       if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto out;
+
+       if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
+               goto out;
+
+       if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = 1;
+
+       if (_switches[VERBOSE_ARG])
+               r = _display_info(dmt);
+
+      out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _create(int argc, char **argv, void *data __attribute__((unused)))
+{
+       int r = 0;
+       struct dm_task *dmt;
+       const char *file = NULL;
+       uint32_t cookie = 0;
+       uint16_t udev_flags = 0;
+
+       if (argc == 3)
+               file = argv[2];
+
+       if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
+               return 0;
+
+       if (!dm_task_set_name(dmt, argv[1]))
+               goto out;
+
+       if (_switches[UUID_ARG] && !dm_task_set_uuid(dmt, _uuid))
+               goto out;
+
+       if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
+               goto out;
+
+       if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+               goto out;
+
+       if (_switches[MAJOR_ARG] && !dm_task_set_major(dmt, _int_args[MAJOR_ARG]))
+               goto out;
+
+       if (_switches[MINOR_ARG] && !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
+               goto out;
+
+       if (_switches[UID_ARG] && !dm_task_set_uid(dmt, _int_args[UID_ARG]))
+               goto out;
+
+       if (_switches[GID_ARG] && !dm_task_set_gid(dmt, _int_args[GID_ARG]))
+               goto out;
+
+       if (_switches[MODE_ARG] && !dm_task_set_mode(dmt, _int_args[MODE_ARG]))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (_switches[READAHEAD_ARG] &&
+           !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
+                                   _read_ahead_flags))
+               goto out;
+
+       if (_switches[NOTABLE_ARG])
+               dm_udev_set_sync_support(0);
+
+       if (_switches[NOUDEVRULES_ARG])
+               udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+                             DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+       if (_udev_cookie) {
+               cookie = _udev_cookie;
+               if (_udev_only)
+                       udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+       }
+
+       if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
+           !dm_task_run(dmt))
+               goto out;
+
+       r = 1;
+
+       if (!_udev_cookie)
+               (void) dm_udev_wait(cookie);
+
+       if (_switches[VERBOSE_ARG])
+               r = _display_info(dmt);
+
+       dm_task_destroy(dmt);
+
+       return r;
+
+      out:
+       if (!_udev_cookie)
+               (void) dm_udev_wait(cookie);
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _rename(int argc, char **argv, void *data __attribute__((unused)))
+{
+       int r = 0;
+       struct dm_task *dmt;
+       uint32_t cookie = 0;
+       uint16_t udev_flags = 0;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
+               return 0;
+
+       /* FIXME Kernel doesn't support uuid or device number here yet */
+       if (!_set_task_device(dmt, (argc == 3) ? argv[1] : NULL, 0))
+               goto out;
+
+       if (_switches[SETUUID_ARG]) {
+               if  (!dm_task_set_newuuid(dmt, argv[argc - 1]))
+                       goto out;
+       } else if (!dm_task_set_newname(dmt, argv[argc - 1]))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (_switches[NOUDEVRULES_ARG])
+               udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+                             DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+       if (_udev_cookie) {
+               cookie = _udev_cookie;
+               if (_udev_only)
+                       udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+       }
+
+       if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
+           !dm_task_run(dmt))
+               goto out;
+
+       r = 1;
+
+      out:
+       if (!_udev_cookie)
+               (void) dm_udev_wait(cookie);
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _message(int argc, char **argv, void *data __attribute__((unused)))
+{
+       int r = 0, i;
+       size_t sz = 1;
+       struct dm_task *dmt;
+       char *str;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
+               return 0;
+
+       if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
+               if (!_set_task_device(dmt, NULL, 0))
+                       goto out;
+       } else {
+               if (!_set_task_device(dmt, argv[1], 0))
+                       goto out;
+               argc--;
+               argv++;
+       }
+
+       if (!dm_task_set_sector(dmt, (uint64_t) atoll(argv[1])))
+               goto out;
+
+       argc -= 2;
+       argv += 2;
+
+       if (argc <= 0)
+               err("No message supplied.\n");
+
+       for (i = 0; i < argc; i++)
+               sz += strlen(argv[i]) + 1;
+
+       if (!(str = dm_zalloc(sz))) {
+               err("message string allocation failed");
+               goto out;
+       }
+
+       for (i = 0; i < argc; i++) {
+               if (i)
+                       strcat(str, " ");
+               strcat(str, argv[i]);
+       }
+
+       if (!dm_task_set_message(dmt, str))
+               goto out;
+
+       dm_free(str);
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _setgeometry(int argc, char **argv, void *data __attribute__((unused)))
+{
+       int r = 0;
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_SET_GEOMETRY)))
+               return 0;
+
+       if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
+               if (!_set_task_device(dmt, NULL, 0))
+                       goto out;
+       } else {
+               if (!_set_task_device(dmt, argv[1], 0))
+                       goto out;
+               argc--;
+               argv++;
+       }
+
+       if (!dm_task_set_geometry(dmt, argv[1], argv[2], argv[3], argv[4]))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       /* run the task */
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+
+       return r;
+}
+
+static int _splitname(int argc, char **argv, void *data __attribute__((unused)))
+{
+       struct dmsetup_report_obj obj;
+       int r = 1;
+
+       obj.task = NULL;
+       obj.info = NULL;
+       obj.deps_task = NULL;
+       obj.tree_node = NULL;
+       obj.split_name = _get_split_name((argc == 3) ? argv[2] : "LVM",
+                                        argv[1], '\0');
+
+       r = dm_report_object(_report, &obj);
+       _destroy_split_name(obj.split_name);
+
+       return r;
+}
+
+static uint32_t _get_cookie_value(const char *str_value)
+{
+       unsigned long int value;
+       char *p;
+
+       if (!(value = strtoul(str_value, &p, 0)) ||
+           *p ||
+           (value == ULONG_MAX && errno == ERANGE) ||
+           value > 0xFFFFFFFF) {
+               err("Incorrect cookie value");
+               return 0;
+       }
+       else
+               return (uint32_t) value;
+}
+
+static int _udevflags(int args, char **argv, void *data __attribute__((unused)))
+{
+       uint32_t cookie;
+       uint16_t flags;
+       int i;
+       static const char *dm_flag_names[] = {"DISABLE_DM_RULES",
+                                             "DISABLE_SUBSYSTEM_RULES",
+                                             "DISABLE_DISK_RULES",
+                                             "DISABLE_OTHER_RULES",
+                                             "LOW_PRIORITY",
+                                             "DISABLE_LIBRARY_FALLBACK",
+                                             "PRIMARY_SOURCE",
+                                              0};
+
+       if (!(cookie = _get_cookie_value(argv[1])))
+               return 0;
+
+       flags = cookie >> DM_UDEV_FLAGS_SHIFT;
+
+       for (i = 0; i < DM_UDEV_FLAGS_SHIFT; i++)
+               if (1 << i & flags) {
+                       if (i < DM_UDEV_FLAGS_SHIFT / 2 && dm_flag_names[i])
+                               printf("DM_UDEV_%s_FLAG='1'\n", dm_flag_names[i]);
+                       else if (i < DM_UDEV_FLAGS_SHIFT / 2)
+                               /*
+                                * This is just a fallback. Each new DM flag
+                                * should have its symbolic name assigned.
+                                */
+                               printf("DM_UDEV_FLAG%d='1'\n", i);
+                       else
+                               /*
+                                * We can't assign symbolic names to subsystem
+                                * flags. Their semantics vary based on the
+                                * subsystem that is currently used.
+                                */
+                               printf("DM_SUBSYSTEM_UDEV_FLAG%d='1'\n",
+                                       i - DM_UDEV_FLAGS_SHIFT / 2);
+               }
+
+       return 1;
+}
+
+static int _udevcomplete(int argc, char **argv, void *data __attribute__((unused)))
+{
+       uint32_t cookie;
+
+       if (!(cookie = _get_cookie_value(argv[1])))
+               return 0;
+
+       /*
+        * Strip flags from the cookie and use cookie magic instead.
+        * If the cookie has non-zero prefix and the base is zero then
+        * this one carries flags to control udev rules only and it is
+        * not meant to be for notification. Return with success in this
+        * situation.
+        */
+       if (!(cookie &= ~DM_UDEV_FLAGS_MASK))
+               return 1;
+
+       cookie |= DM_COOKIE_MAGIC << DM_UDEV_FLAGS_SHIFT;
+
+       return dm_udev_complete(cookie);
+}
+
+#ifndef UDEV_SYNC_SUPPORT
+static const char _cmd_not_supported[] = "Command not supported. Recompile with \"--enable-udev-sync\" to enable.";
+
+static int _udevcreatecookie(int argc, char **argv,
+                                 void *data __attribute__((unused)))
+{
+       log_error(_cmd_not_supported);
+
+       return 0;
+}
+
+static int _udevreleasecookie(int argc, char **argv,
+                               void *data __attribute__((unused)))
+{
+       log_error(_cmd_not_supported);
+
+       return 0;
+}
+
+static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       log_error(_cmd_not_supported);
+
+       return 0;
+}
+
+static int _udevcookies(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       log_error(_cmd_not_supported);
+
+       return 0;
+}
+
+#else  /* UDEV_SYNC_SUPPORT */
+static int _set_up_udev_support(const char *dev_dir)
+{
+       struct udev *udev;
+       const char *udev_dev_dir;
+       size_t udev_dev_dir_len;
+       int dirs_diff;
+       const char *env;
+
+       if (_switches[NOUDEVSYNC_ARG])
+               dm_udev_set_sync_support(0);
+
+       if (!_udev_cookie) {
+               env = getenv(DM_UDEV_COOKIE_ENV_VAR_NAME);
+               if (env && *env && (_udev_cookie = _get_cookie_value(env)))
+                       log_debug("Using udev transaction 0x%08" PRIX32
+                                 " defined by %s environment variable.",
+                                  _udev_cookie,
+                                  DM_UDEV_COOKIE_ENV_VAR_NAME);
+       }
+       else if (_switches[UDEVCOOKIE_ARG])
+               log_debug("Using udev transaction 0x%08" PRIX32
+                         " defined by --udevcookie option.",
+                         _udev_cookie);
+
+       if (!(udev = udev_new()) ||
+           !(udev_dev_dir = udev_get_dev_path(udev)) ||
+           !*udev_dev_dir) {
+               log_error("Could not get udev dev path.");
+               return 0;
+       }
+       udev_dev_dir_len = strlen(udev_dev_dir);
+
+       /*
+        * Normally, there's always a fallback action by libdevmapper if udev
+        * has not done its job correctly, e.g. the nodes were not created.
+        * If using udev transactions by specifying existing cookie value,
+        * we need to disable node creation by libdevmapper completely,
+        * disabling any fallback actions, since any synchronisation happens
+        * at the end of the transaction only. We need to do this to prevent
+        * races between udev and libdevmapper but only in case udev "dev path"
+        * is the same as "dev path" used by libdevmapper.
+        */
+
+       /* There's always a slash at the end of dev_dir. But check udev_dev_dir! */
+       if (udev_dev_dir[udev_dev_dir_len - 1] != '/')
+               dirs_diff = strncmp(dev_dir, udev_dev_dir, udev_dev_dir_len);
+       else
+               dirs_diff = strcmp(dev_dir, udev_dev_dir);
+
+       _udev_only = _udev_cookie && !dirs_diff;
+
+       if (dirs_diff) {
+               log_debug("The path %s used for creating device nodes that is "
+                         "set via DM_DEV_DIR environment variable differs from "
+                         "the path %s that is used by udev. All warnings "
+                         "about udev not working correctly while processing "
+                         "particular nodes will be suppressed. These nodes "
+                         "and symlinks will be managed in each directory "
+                         "separately.", dev_dir, udev_dev_dir);
+               dm_udev_set_checking(0);
+       }
+
+       udev_unref(udev);
+       return 1;
+}
+
+static int _udevcreatecookie(int argc, char **argv,
+                                 void *data __attribute__((unused)))
+{
+       uint32_t cookie;
+
+       if (!dm_udev_create_cookie(&cookie))
+               return 0;
+
+       if (cookie)
+               printf("0x%08" PRIX32 "\n", cookie);
+
+       return 1;
+}
+
+static int _udevreleasecookie(int argc, char **argv,
+                               void *data __attribute__((unused)))
+{
+       if (argv[1] && !(_udev_cookie = _get_cookie_value(argv[1])))
+               return 0;
+
+       if (!_udev_cookie) {
+               log_error("No udev transaction cookie given.");
+               return 0;
+       }
+
+       return dm_udev_wait(_udev_cookie);
+}
+
+static char _yes_no_prompt(const char *prompt, ...)
+{
+       int c = 0, ret = 0;
+       va_list ap;
+
+       do {
+               if (c == '\n' || !c) {
+                       va_start(ap, prompt);
+                       vprintf(prompt, ap);
+                       va_end(ap);
+               }
+
+               if ((c = getchar()) == EOF) {
+                       ret = 'n';
+                       break;
+               }
+
+               c = tolower(c);
+               if ((c == 'y') || (c == 'n'))
+                       ret = c;
+       } while (!ret || c != '\n');
+
+       if (c != '\n')
+               printf("\n");
+
+       return ret;
+}
+
+static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       int max_id, id, sid;
+       struct seminfo sinfo;
+       struct semid_ds sdata;
+       int counter = 0;
+
+       if (!_switches[YES_ARG]) {
+               log_warn("This operation will destroy all semaphores with keys "
+                        "that have a prefix %" PRIu16 " (0x%" PRIx16 ").",
+                        DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
+
+               if (_yes_no_prompt("Do you really want to continue? [y/n]: ") == 'n') {
+                       log_print("Semaphores with keys prefixed by %" PRIu16
+                                 " (0x%" PRIx16 ") NOT destroyed.",
+                                 DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
+                       return 1;
+               }
+       }
+
+       if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
+               log_sys_error("semctl", "SEM_INFO");
+               return 0;
+       }
+
+       for (id = 0; id <= max_id; id++) {
+               if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
+                       continue;
+
+               if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
+                       if (semctl(sid, 0, IPC_RMID, 0) < 0) {
+                               log_error("Could not cleanup notification semaphore "
+                                         "with semid %d and cookie value "
+                                         "%" PRIu32 " (0x%" PRIx32 ")", sid,
+                                         sdata.sem_perm.__key, sdata.sem_perm.__key);
+                               continue;
+                       }
+
+                       counter++;
+               }
+       }
+
+       log_print("%d semaphores with keys prefixed by "
+                 "%" PRIu16 " (0x%" PRIx16 ") destroyed.",
+                 counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
+
+       return 1;
+}
+
+static int _udevcookies(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       int max_id, id, sid;
+       struct seminfo sinfo;
+       struct semid_ds sdata;
+       int val;
+       char *time_str;
+
+       if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
+               log_sys_error("sem_ctl", "SEM_INFO");
+               return 0;
+       }
+
+       printf("cookie       semid      value      last_semop_time\n");
+
+       for (id = 0; id <= max_id; id++) {
+               if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
+                       continue;
+
+               if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
+                       if ((val = semctl(sid, 0, GETVAL)) < 0) {
+                               log_error("semid %d: sem_ctl failed for "
+                                         "cookie 0x%" PRIx32 ": %s",
+                                         sid, sdata.sem_perm.__key,
+                                         strerror(errno));
+                               continue;
+                       }
+
+                       time_str = ctime((const time_t *) &sdata.sem_otime);
+
+                       printf("0x%-10x %-10d %-10d %s", sdata.sem_perm.__key,
+                               sid, val, time_str ? time_str : "unknown\n");
+               }
+       }
+
+       return 1;
+}
+#endif /* UDEV_SYNC_SUPPORT */
+
+static int _version(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       char version[80];
+
+       if (dm_get_library_version(version, sizeof(version)))
+               printf("Library version:   %s\n", version);
+
+       if (!dm_driver_version(version, sizeof(version)))
+               return 0;
+
+       printf("Driver version:    %s\n", version);
+
+       return 1;
+}
+
+static int _simple(int task, const char *name, uint32_t event_nr, int display)
+{
+       uint32_t cookie = 0;
+       uint16_t udev_flags = 0;
+       int udev_wait_flag = task == DM_DEVICE_RESUME ||
+                            task == DM_DEVICE_REMOVE;
+       int r = 0;
+
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create(task)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto out;
+
+       if (event_nr && !dm_task_set_event_nr(dmt, event_nr))
+               goto out;
+
+       if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (_switches[NOLOCKFS_ARG] && !dm_task_skip_lockfs(dmt))
+               goto out;
+
+       if (_switches[READAHEAD_ARG] &&
+           !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
+                                   _read_ahead_flags))
+               goto out;
+
+       if (_switches[NOUDEVRULES_ARG])
+               udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+                             DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+       if (_udev_cookie) {
+               cookie = _udev_cookie;
+               if (_udev_only)
+                       udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+       }
+
+       if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags))
+               goto out;
+
+       r = dm_task_run(dmt);
+
+       if (r && display && _switches[VERBOSE_ARG])
+               r = _display_info(dmt);
+
+      out:
+       if (!_udev_cookie && udev_wait_flag)
+               (void) dm_udev_wait(cookie);
+
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _suspend(int argc, char **argv, void *data __attribute__((unused)))
+{
+       return _simple(DM_DEVICE_SUSPEND, argc > 1 ? argv[1] : NULL, 0, 1);
+}
+
+static int _resume(int argc, char **argv, void *data __attribute__((unused)))
+{
+       return _simple(DM_DEVICE_RESUME, argc > 1 ? argv[1] : NULL, 0, 1);
+}
+
+static int _clear(int argc, char **argv, void *data __attribute__((unused)))
+{
+       return _simple(DM_DEVICE_CLEAR, argc > 1 ? argv[1] : NULL, 0, 1);
+}
+
+static int _wait(int argc, char **argv, void *data __attribute__((unused)))
+{
+       const char *name = NULL;
+
+       if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+               if (argc == 1) {
+                       err("No device specified.");
+                       return 0;
+               }
+               name = argv[1];
+               argc--, argv++;
+       }
+
+       return _simple(DM_DEVICE_WAITEVENT, name,
+                      (argc > 1) ? (uint32_t) atoi(argv[argc - 1]) : 0, 1);
+}
+
+static int _process_all(int argc, char **argv, int silent,
+                       int (*fn) (int argc, char **argv, void *data))
+{
+       int r = 1;
+       struct dm_names *names;
+       unsigned next = 0;
+
+       struct dm_task *dmt;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+               return 0;
+
+       if (!dm_task_run(dmt)) {
+               r = 0;
+               goto out;
+       }
+
+       if (!(names = dm_task_get_names(dmt))) {
+               r = 0;
+               goto out;
+       }
+
+       if (!names->dev) {
+               if (!silent)
+                       printf("No devices found\n");
+               goto out;
+       }
+
+       do {
+               names = (struct dm_names *)((char *) names + next);
+               if (!fn(argc, argv, names))
+                       r = 0;
+               next = names->next;
+       } while (next);
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static uint64_t _get_device_size(const char *name)
+{
+       uint64_t start, length, size = UINT64_C(0);
+       struct dm_info info;
+       char *target_type, *params;
+       struct dm_task *dmt;
+       void *next = NULL;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto out;
+
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &target_type, &params);
+               size += length;
+       } while (next);
+
+      out:
+       dm_task_destroy(dmt);
+       return size;
+}
+
+static int _error_device(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data)
+{
+       struct dm_names *names = (struct dm_names *) data;
+       struct dm_task *dmt;
+       const char *name;
+       uint64_t size;
+       int r = 0;
+
+       if (data)
+               name = names->name;
+       else
+               name = argv[1];
+
+       size = _get_device_size(name);
+
+       if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto error;
+
+       if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
+               goto error;
+
+       if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+               goto error;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto error;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto error;
+
+       if (!dm_task_run(dmt))
+               goto error;
+
+       if (!_simple(DM_DEVICE_RESUME, name, 0, 0)) {
+               _simple(DM_DEVICE_CLEAR, name, 0, 0);
+               goto error;
+       }
+
+       r = 1;
+
+error:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _remove(int argc, char **argv, void *data __attribute__((unused)))
+{
+       if (_switches[FORCE_ARG] && argc > 1)
+               (void) _error_device(argc, argv, NULL);
+
+       return _simple(DM_DEVICE_REMOVE, argc > 1 ? argv[1] : NULL, 0, 0);
+}
+
+static int _count_devices(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       _num_devices++;
+
+       return 1;
+}
+
+static int _remove_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       int r;
+
+       /* Remove all closed devices */
+       r =  _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
+
+       if (!_switches[FORCE_ARG])
+               return r;
+
+       _num_devices = 0;
+       r |= _process_all(argc, argv, 1, _count_devices);
+
+       /* No devices left? */
+       if (!_num_devices)
+               return r;
+
+       r |= _process_all(argc, argv, 1, _error_device);
+       r |= _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
+
+       _num_devices = 0;
+       r |= _process_all(argc, argv, 1, _count_devices);
+       if (!_num_devices)
+               return r;
+
+       fprintf(stderr, "Unable to remove %d device(s).\n", _num_devices);
+
+       return r;
+}
+
+static void _display_dev(struct dm_task *dmt, const char *name)
+{
+       struct dm_info info;
+
+       if (dm_task_get_info(dmt, &info))
+               printf("%s\t(%u, %u)\n", name, info.major, info.minor);
+}
+
+static int _mknodes(int argc, char **argv, void *data __attribute__((unused)))
+{
+       return dm_mknodes(argc > 1 ? argv[1] : NULL);
+}
+
+static int _exec_command(const char *name)
+{
+       int n;
+       static char path[PATH_MAX];
+       static char *args[ARGS_MAX + 1];
+       static int argc = 0;
+       char *c;
+       pid_t pid;
+
+       if (argc < 0)
+               return 0;
+
+       if (!dm_mknodes(name))
+               return 0;
+
+       n = snprintf(path, sizeof(path), "%s/%s", dm_dir(), name);
+       if (n < 0 || n > (int) sizeof(path) - 1)
+               return 0;
+
+       if (!argc) {
+               c = _command;
+               while (argc < ARGS_MAX) {
+                       while (*c && isspace(*c))
+                               c++;
+                       if (!*c)
+                               break;
+                       args[argc++] = c;
+                       while (*c && !isspace(*c))
+                               c++;
+                       if (*c)
+                               *c++ = '\0';
+               }
+
+               if (!argc) {
+                       argc = -1;
+                       return 0;
+               }
+
+               if (argc == ARGS_MAX) {
+                       err("Too many args to --exec\n");
+                       argc = -1;
+                       return 0;
+               }
+
+               args[argc++] = path;
+               args[argc] = NULL;
+       }
+
+       if (!(pid = fork())) {
+               execvp(args[0], args);
+               _exit(127);
+       } else if (pid < (pid_t) 0)
+               return 0;
+
+       TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
+
+       return 1;
+}
+
+static int _status(int argc, char **argv, void *data)
+{
+       int r = 0;
+       struct dm_task *dmt;
+       void *next = NULL;
+       uint64_t start, length;
+       char *target_type = NULL;
+       char *params, *c;
+       int cmd;
+       struct dm_names *names = (struct dm_names *) data;
+       const char *name = NULL;
+       int matched = 0;
+       int ls_only = 0;
+       struct dm_info info;
+
+       if (data)
+               name = names->name;
+       else {
+               if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+                       return _process_all(argc, argv, 0, _status);
+               if (argc == 2)
+                       name = argv[1];
+       }
+
+       if (!strcmp(argv[0], "table"))
+               cmd = DM_DEVICE_TABLE;
+       else
+               cmd = DM_DEVICE_STATUS;
+
+       if (!strcmp(argv[0], "ls"))
+               ls_only = 1;
+
+       if (!(dmt = dm_task_create(cmd)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto out;
+
+       if (!name)
+               name = dm_task_get_name(dmt);
+
+       /* Fetch targets and print 'em */
+       do {
+               next = dm_get_next_target(dmt, next, &start, &length,
+                                         &target_type, &params);
+               /* Skip if target type doesn't match */
+               if (_switches[TARGET_ARG] &&
+                   (!target_type || strcmp(target_type, _target)))
+                       continue;
+               if (ls_only) {
+                       if (!_switches[EXEC_ARG] || !_command ||
+                           _switches[VERBOSE_ARG])
+                               _display_dev(dmt, name);
+                       next = NULL;
+               } else if (!_switches[EXEC_ARG] || !_command ||
+                          _switches[VERBOSE_ARG]) {
+                       if (!matched && _switches[VERBOSE_ARG])
+                               _display_info(dmt);
+                       if (data && !_switches[VERBOSE_ARG])
+                               printf("%s: ", name);
+                       if (target_type) {
+                               /* Suppress encryption key */
+                               if (!_switches[SHOWKEYS_ARG] &&
+                                   cmd == DM_DEVICE_TABLE &&
+                                   !strcmp(target_type, "crypt")) {
+                                       c = params;
+                                       while (*c && *c != ' ')
+                                               c++;
+                                       if (*c)
+                                               c++;
+                                       while (*c && *c != ' ')
+                                               *c++ = '0';
+                               }
+                               printf("%" PRIu64 " %" PRIu64 " %s %s",
+                                      start, length, target_type, params);
+                       }
+                       printf("\n");
+               }
+               matched = 1;
+       } while (next);
+
+       if (data && _switches[VERBOSE_ARG] && matched && !ls_only)
+               printf("\n");
+
+       if (matched && _switches[EXEC_ARG] && _command && !_exec_command(name))
+               goto out;
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+/* Show target names and their version numbers */
+static int _targets(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+       int r = 0;
+       struct dm_task *dmt;
+       struct dm_versions *target;
+       struct dm_versions *last_target;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+               return 0;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       target = dm_task_get_versions(dmt);
+
+       /* Fetch targets and print 'em */
+       do {
+               last_target = target;
+
+               printf("%-16s v%d.%d.%d\n", target->name, target->version[0],
+                      target->version[1], target->version[2]);
+
+               target = (struct dm_versions *)((char *) target + target->next);
+       } while (last_target != target);
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _info(int argc, char **argv, void *data)
+{
+       int r = 0;
+
+       struct dm_task *dmt;
+       struct dm_names *names = (struct dm_names *) data;
+       char *name = NULL;
+
+       if (data)
+               name = names->name;
+       else {
+               if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+                       return _process_all(argc, argv, 0, _info);
+               if (argc == 2)
+                       name = argv[1];
+       }
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       r = _display_info(dmt);
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _deps(int argc, char **argv, void *data)
+{
+       int r = 0;
+       uint32_t i;
+       struct dm_deps *deps;
+       struct dm_task *dmt;
+       struct dm_info info;
+       struct dm_names *names = (struct dm_names *) data;
+       char *name = NULL;
+
+       if (data)
+               name = names->name;
+       else {
+               if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+                       return _process_all(argc, argv, 0, _deps);
+               if (argc == 2)
+                       name = argv[1];
+       }
+
+       if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+               return 0;
+
+       if (!_set_task_device(dmt, name, 0))
+               goto out;
+
+       if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+               goto out;
+
+       if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+               goto out;
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!dm_task_get_info(dmt, &info))
+               goto out;
+
+       if (!(deps = dm_task_get_deps(dmt)))
+               goto out;
+
+       if (!info.exists) {
+               printf("Device does not exist.\n");
+               r = 1;
+               goto out;
+       }
+
+       if (_switches[VERBOSE_ARG])
+               _display_info(dmt);
+
+       if (data && !_switches[VERBOSE_ARG])
+               printf("%s: ", name);
+       printf("%d dependencies\t:", deps->count);
+
+       for (i = 0; i < deps->count; i++)
+               printf(" (%d, %d)",
+                      (int) MAJOR(deps->device[i]),
+                      (int) MINOR(deps->device[i]));
+       printf("\n");
+
+       if (data && _switches[VERBOSE_ARG])
+               printf("\n");
+
+       r = 1;
+
+      out:
+       dm_task_destroy(dmt);
+       return r;
+}
+
+static int _display_name(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data)
+{
+       struct dm_names *names = (struct dm_names *) data;
+
+       printf("%s\t(%d, %d)\n", names->name,
+              (int) MAJOR(names->dev), (int) MINOR(names->dev));
+
+       return 1;
+}
+
+/*
+ * Tree drawing code
+ */
+
+enum {
+       TR_DEVICE=0,    /* display device major:minor number */
+       TR_TABLE,
+       TR_STATUS,
+       TR_ACTIVE,
+       TR_RW,
+       TR_OPENCOUNT,
+       TR_UUID,
+       TR_COMPACT,
+       TR_TRUNCATE,
+       TR_BOTTOMUP,
+       NUM_TREEMODE,
+};
+
+static int _tree_switches[NUM_TREEMODE];
+
+#define TR_PRINT_ATTRIBUTE ( _tree_switches[TR_ACTIVE] || \
+                            _tree_switches[TR_RW] || \
+                            _tree_switches[TR_OPENCOUNT] || \
+                            _tree_switches[TR_UUID] )
+
+#define TR_PRINT_TARGETS ( _tree_switches[TR_TABLE] || \
+                          _tree_switches[TR_STATUS] )
+
+/* Compact - fewer newlines */
+#define TR_PRINT_COMPACT (_tree_switches[TR_COMPACT] && \
+                         !TR_PRINT_ATTRIBUTE && \
+                         !TR_PRINT_TARGETS)
+
+/* FIXME Get rid of this */
+#define MAX_DEPTH 100
+
+/* Drawing character definition from pstree */
+/* [pstree comment] UTF-8 defines by Johan Myreen, updated by Ben Winslow */
+#define UTF_V  "\342\224\202"  /* U+2502, Vertical line drawing char */
+#define UTF_VR "\342\224\234"  /* U+251C, Vertical and right */
+#define UTF_H  "\342\224\200"  /* U+2500, Horizontal */
+#define UTF_UR "\342\224\224"  /* U+2514, Up and right */
+#define UTF_HD "\342\224\254"  /* U+252C, Horizontal and down */
+
+#define VT_BEG "\033(0\017"    /* use graphic chars */
+#define VT_END "\033(B"        /* back to normal char set */
+#define VT_V   "x"             /* see UTF definitions above */
+#define VT_VR  "t"
+#define VT_H   "q"
+#define VT_UR  "m"
+#define VT_HD  "w"
+
+static struct {
+       const char *empty_2;    /*    */
+       const char *branch_2;   /* |- */
+       const char *vert_2;     /* |  */
+       const char *last_2;     /* `- */
+       const char *single_3;   /* --- */
+       const char *first_3;    /* -+- */
+}
+_tsym_ascii = {
+       "  ",
+       "|-",
+       "| ",
+       "`-",
+       "---",
+       "-+-"
+},
+_tsym_utf = {
+       "  ",
+       UTF_VR UTF_H,
+       UTF_V " ",
+       UTF_UR UTF_H,
+       UTF_H UTF_H UTF_H,
+       UTF_H UTF_HD UTF_H
+},
+_tsym_vt100 = {
+       "  ",
+       VT_BEG VT_VR VT_H VT_END,
+       VT_BEG VT_V VT_END " ",
+       VT_BEG VT_UR VT_H VT_END,
+       VT_BEG VT_H VT_H VT_H VT_END,
+       VT_BEG VT_H VT_HD VT_H VT_END
+},
+*_tsym = &_tsym_ascii;
+
+/*
+ * Tree drawing functions.
+ */
+/* FIXME Get rid of these statics - use dynamic struct */
+/* FIXME Explain what these vars are for */
+static int _tree_width[MAX_DEPTH], _tree_more[MAX_DEPTH];
+static int _termwidth = 80;    /* Maximum output width */
+static int _cur_x = 1;         /* Current horizontal output position */
+static char _last_char = 0;
+
+static void _out_char(const unsigned c)
+{
+       /* Only first UTF-8 char counts */
+       _cur_x += ((c & 0xc0) != 0x80);
+
+       if (!_tree_switches[TR_TRUNCATE]) {
+               putchar((int) c);
+               return;
+       }
+
+       /* Truncation? */
+       if (_cur_x <= _termwidth)
+               putchar((int) c);
+
+       if (_cur_x == _termwidth + 1 && ((c & 0xc0) != 0x80)) {
+               if (_last_char || (c & 0x80)) {
+                       putchar('.');
+                       putchar('.');
+                       putchar('.');
+               } else {
+                       _last_char = c;
+                       _cur_x--;
+               }
+       }
+}
+
+static void _out_string(const char *str)
+{
+       while (*str)
+               _out_char((unsigned char) *str++);
+}
+
+/* non-negative integers only */
+static unsigned _out_int(unsigned num)
+{
+       unsigned digits = 0;
+       unsigned divi;
+
+       if (!num) {
+               _out_char('0');
+               return 1;
+       }
+
+       /* non zero case */
+       for (divi = 1; num / divi; divi *= 10)
+               digits++;
+
+       for (divi /= 10; divi; divi /= 10)
+               _out_char('0' + (num / divi) % 10);
+
+       return digits;
+}
+
+static void _out_newline(void)
+{
+       if (_last_char && _cur_x == _termwidth)
+               putchar(_last_char);
+       _last_char = 0;
+       putchar('\n');
+       _cur_x = 1;
+}
+
+static void _out_prefix(unsigned depth)
+{
+       unsigned x, d;
+
+       for (d = 0; d < depth; d++) {
+               for (x = _tree_width[d] + 1; x > 0; x--)
+                       _out_char(' ');
+
+               _out_string(d == depth - 1 ?
+                               !_tree_more[depth] ? _tsym->last_2 : _tsym->branch_2
+                          : _tree_more[d + 1] ?
+                               _tsym->vert_2 : _tsym->empty_2);
+       }
+}
+
+/*
+ * Display tree
+ */
+static void _display_tree_attributes(struct dm_tree_node *node)
+{
+       int attr = 0;
+       const char *uuid;
+       const struct dm_info *info;
+
+       uuid = dm_tree_node_get_uuid(node);
+       info = dm_tree_node_get_info(node);
+
+       if (!info->exists)
+               return;
+
+       if (_tree_switches[TR_ACTIVE]) {
+               _out_string(attr++ ? ", " : " [");
+               _out_string(info->suspended ? "SUSPENDED" : "ACTIVE");
+       }
+
+       if (_tree_switches[TR_RW]) {
+               _out_string(attr++ ? ", " : " [");
+               _out_string(info->read_only ? "RO" : "RW");
+       }
+
+       if (_tree_switches[TR_OPENCOUNT]) {
+               _out_string(attr++ ? ", " : " [");
+               (void) _out_int((unsigned) info->open_count);
+       }
+
+       if (_tree_switches[TR_UUID]) {
+               _out_string(attr++ ? ", " : " [");
+               _out_string(uuid && *uuid ? uuid : "");
+       }
+
+       if (attr)
+               _out_char(']');
+}
+
+static void _display_tree_node(struct dm_tree_node *node, unsigned depth,
+                              unsigned first_child __attribute__((unused)),
+                              unsigned last_child, unsigned has_children)
+{
+       int offset;
+       const char *name;
+       const struct dm_info *info;
+       int first_on_line = 0;
+
+       /* Sub-tree for targets has 2 more depth */
+       if (depth + 2 > MAX_DEPTH)
+               return;
+
+       name = dm_tree_node_get_name(node);
+
+       if ((!name || !*name) && !_tree_switches[TR_DEVICE])
+               return;
+
+       /* Indicate whether there are more nodes at this depth */
+       _tree_more[depth] = !last_child;
+       _tree_width[depth] = 0;
+
+       if (_cur_x == 1)
+               first_on_line = 1;
+
+       if (!TR_PRINT_COMPACT || first_on_line)
+               _out_prefix(depth);
+
+       /* Remember the starting point for compact */
+       offset = _cur_x;
+
+       if (TR_PRINT_COMPACT && !first_on_line)
+               _out_string(_tree_more[depth] ? _tsym->first_3 : _tsym->single_3);
+
+       /* display node */
+       if (name)
+               _out_string(name);
+
+       info = dm_tree_node_get_info(node);
+
+       if (_tree_switches[TR_DEVICE]) {
+               _out_string(name ? " (" : "(");
+               (void) _out_int(info->major);
+               _out_char(':');
+               (void) _out_int(info->minor);
+               _out_char(')');
+       }
+
+       /* display additional info */
+       if (TR_PRINT_ATTRIBUTE)
+               _display_tree_attributes(node);
+
+       if (TR_PRINT_COMPACT)
+               _tree_width[depth] = _cur_x - offset;
+
+       if (!TR_PRINT_COMPACT || !has_children)
+               _out_newline();
+
+       if (TR_PRINT_TARGETS) {
+               _tree_more[depth + 1] = has_children;
+               // FIXME _display_tree_targets(name, depth + 2);
+       }
+}
+
+/*
+ * Walk the dependency tree
+ */
+static void _display_tree_walk_children(struct dm_tree_node *node,
+                                       unsigned depth)
+{
+       struct dm_tree_node *child, *next_child;
+       void *handle = NULL;
+       uint32_t inverted = _tree_switches[TR_BOTTOMUP];
+       unsigned first_child = 1;
+       unsigned has_children;
+
+       next_child = dm_tree_next_child(&handle, node, inverted);
+
+       while ((child = next_child)) {
+               next_child = dm_tree_next_child(&handle, node, inverted);
+               has_children =
+                   dm_tree_node_num_children(child, inverted) ? 1 : 0;
+
+               _display_tree_node(child, depth, first_child,
+                                  next_child ? 0U : 1U, has_children);
+
+               if (has_children)
+                       _display_tree_walk_children(child, depth + 1);
+
+               first_child = 0;
+       }
+}
+
+static int _add_dep(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data)
+{
+       struct dm_names *names = (struct dm_names *) data;
+
+       if (!dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev)))
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Create and walk dependency tree
+ */
+static int _build_whole_deptree(void)
+{
+       if (_dtree)
+               return 1;
+
+       if (!(_dtree = dm_tree_create()))
+               return 0;
+
+       if (!_process_all(0, NULL, 0, _add_dep))
+               return 0;
+
+       return 1;
+}
+
+static int _display_tree(int argc __attribute__((unused)),
+                        char **argv __attribute__((unused)),
+                        void *data __attribute__((unused)))
+{
+       if (!_build_whole_deptree())
+               return 0;
+
+       _display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
+
+       return 1;
+}
+
+/*
+ * Report device information
+ */
+
+/* dm specific display functions */
+
+static int _int32_disp(struct dm_report *rh,
+                      struct dm_pool *mem __attribute__((unused)),
+                      struct dm_report_field *field, const void *data,
+                      void *private __attribute__((unused)))
+{
+       const int32_t value = *(const int32_t *)data;
+
+       return dm_report_field_int32(rh, field, &value);
+}
+
+static int _uint32_disp(struct dm_report *rh,
+                       struct dm_pool *mem __attribute__((unused)),
+                       struct dm_report_field *field, const void *data,
+                       void *private __attribute__((unused)))
+{
+       const uint32_t value = *(const int32_t *)data;
+
+       return dm_report_field_uint32(rh, field, &value);
+}
+
+static int _dm_name_disp(struct dm_report *rh,
+                        struct dm_pool *mem __attribute__((unused)),
+                        struct dm_report_field *field, const void *data,
+                        void *private __attribute__((unused)))
+{
+       const char *name = dm_task_get_name((const struct dm_task *) data);
+
+       return dm_report_field_string(rh, field, &name);
+}
+
+static int _dm_uuid_disp(struct dm_report *rh,
+                        struct dm_pool *mem __attribute__((unused)),
+                        struct dm_report_field *field,
+                        const void *data, void *private __attribute__((unused)))
+{
+       const char *uuid = dm_task_get_uuid((const struct dm_task *) data);
+
+       if (!uuid || !*uuid)
+               uuid = "";
+
+       return dm_report_field_string(rh, field, &uuid);
+}
+
+static int _dm_read_ahead_disp(struct dm_report *rh,
+                              struct dm_pool *mem __attribute__((unused)),
+                              struct dm_report_field *field, const void *data,
+                              void *private __attribute__((unused)))
+{
+       uint32_t value;
+
+       if (!dm_task_get_read_ahead((const struct dm_task *) data, &value))
+               value = 0;
+
+       return dm_report_field_uint32(rh, field, &value);
+}
+
+static int _dm_info_status_disp(struct dm_report *rh,
+                               struct dm_pool *mem __attribute__((unused)),
+                               struct dm_report_field *field, const void *data,
+                               void *private __attribute__((unused)))
+{
+       char buf[5];
+       const char *s = buf;
+       const struct dm_info *info = data;
+
+       buf[0] = info->live_table ? 'L' : '-';
+       buf[1] = info->inactive_table ? 'I' : '-';
+       buf[2] = info->suspended ? 's' : '-';
+       buf[3] = info->read_only ? 'r' : 'w';
+       buf[4] = '\0';
+
+       return dm_report_field_string(rh, field, &s);
+}
+
+static int _dm_info_table_loaded_disp(struct dm_report *rh,
+                                     struct dm_pool *mem __attribute__((unused)),
+                                     struct dm_report_field *field,
+                                     const void *data,
+                                     void *private __attribute__((unused)))
+{
+       const struct dm_info *info = data;
+
+       if (info->live_table) {
+               if (info->inactive_table)
+                       dm_report_field_set_value(field, "Both", NULL);
+               else
+                       dm_report_field_set_value(field, "Live", NULL);
+               return 1;
+       }
+
+       if (info->inactive_table)
+               dm_report_field_set_value(field, "Inactive", NULL);
+       else
+               dm_report_field_set_value(field, "None", NULL);
+
+       return 1;
+}
+
+static int _dm_info_suspended_disp(struct dm_report *rh,
+                                  struct dm_pool *mem __attribute__((unused)),
+                                  struct dm_report_field *field,
+                                  const void *data,
+                                  void *private __attribute__((unused)))
+{
+       const struct dm_info *info = data;
+
+       if (info->suspended)
+               dm_report_field_set_value(field, "Suspended", NULL);
+       else
+               dm_report_field_set_value(field, "Active", NULL);
+
+       return 1;
+}
+
+static int _dm_info_read_only_disp(struct dm_report *rh,
+                                  struct dm_pool *mem __attribute__((unused)),
+                                  struct dm_report_field *field,
+                                  const void *data,
+                                  void *private __attribute__((unused)))
+{
+       const struct dm_info *info = data;
+
+       if (info->read_only)
+               dm_report_field_set_value(field, "Read-only", NULL);
+       else
+               dm_report_field_set_value(field, "Writeable", NULL);
+
+       return 1;
+}
+
+
+static int _dm_info_devno_disp(struct dm_report *rh, struct dm_pool *mem,
+                              struct dm_report_field *field, const void *data,
+                              void *private)
+{
+       char buf[DM_MAX_TYPE_NAME], *repstr;
+       const struct dm_info *info = data;
+
+       if (!dm_pool_begin_object(mem, 8)) {
+               log_error("dm_pool_begin_object failed");
+               return 0;
+       }
+
+       if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+                       info->major, info->minor) < 0) {
+               log_error("dm_pool_alloc failed");
+               goto out_abandon;
+       }
+
+       if (!dm_pool_grow_object(mem, buf, strlen(buf) + 1)) {
+               log_error("dm_pool_grow_object failed");
+               goto out_abandon;
+       }
+
+       repstr = dm_pool_end_object(mem);
+       dm_report_field_set_value(field, repstr, repstr);
+       return 1;
+
+      out_abandon:
+       dm_pool_abandon_object(mem);
+       return 0;
+}
+
+static int _dm_tree_names(struct dm_report *rh, struct dm_pool *mem,
+                         struct dm_report_field *field, const void *data,
+                         void *private, unsigned inverted)
+{
+       const struct dm_tree_node *node = data;
+       struct dm_tree_node *parent;
+       void *t = NULL;
+       const char *name;
+       int first_node = 1;
+       char *repstr;
+
+       if (!dm_pool_begin_object(mem, 16)) {
+               log_error("dm_pool_begin_object failed");
+               return 0;
+       }
+
+       while ((parent = dm_tree_next_child(&t, node, inverted))) {
+               name = dm_tree_node_get_name(parent);
+               if (!name || !*name)
+                       continue;
+               if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
+                       log_error("dm_pool_grow_object failed");
+                       goto out_abandon;
+               }
+               if (!dm_pool_grow_object(mem, name, 0)) {
+                       log_error("dm_pool_grow_object failed");
+                       goto out_abandon;
+               }
+               if (first_node)
+                       first_node = 0;
+       }
+
+       if (!dm_pool_grow_object(mem, "\0", 1)) {
+               log_error("dm_pool_grow_object failed");
+               goto out_abandon;
+       }
+
+       repstr = dm_pool_end_object(mem);
+       dm_report_field_set_value(field, repstr, repstr);
+       return 1;
+
+      out_abandon:
+       dm_pool_abandon_object(mem);
+       return 0;
+}
+
+static int _dm_deps_names_disp(struct dm_report *rh,
+                                     struct dm_pool *mem,
+                                     struct dm_report_field *field,
+                                     const void *data, void *private)
+{
+       return _dm_tree_names(rh, mem, field, data, private, 0);
+}
+
+static int _dm_tree_parents_names_disp(struct dm_report *rh,
+                                      struct dm_pool *mem,
+                                      struct dm_report_field *field,
+                                      const void *data, void *private)
+{
+       return _dm_tree_names(rh, mem, field, data, private, 1);
+}
+
+static int _dm_tree_parents_devs_disp(struct dm_report *rh, struct dm_pool *mem,
+                                     struct dm_report_field *field,
+                                     const void *data, void *private)
+{
+       const struct dm_tree_node *node = data;
+       struct dm_tree_node *parent;
+       void *t = NULL;
+       const struct dm_info *info;
+       int first_node = 1;
+       char buf[DM_MAX_TYPE_NAME], *repstr;
+
+       if (!dm_pool_begin_object(mem, 16)) {
+               log_error("dm_pool_begin_object failed");
+               return 0;
+       }
+
+       while ((parent = dm_tree_next_child(&t, node, 1))) {
+               info = dm_tree_node_get_info(parent);
+               if (!info->major && !info->minor)
+                       continue;
+               if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
+                       log_error("dm_pool_grow_object failed");
+                       goto out_abandon;
+               }
+               if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+                               info->major, info->minor) < 0) {
+                       log_error("dm_snprintf failed");
+                       goto out_abandon;
+               }
+               if (!dm_pool_grow_object(mem, buf, 0)) {
+                       log_error("dm_pool_grow_object failed");
+                       goto out_abandon;
+               }
+               if (first_node)
+                       first_node = 0;
+       }
+
+       if (!dm_pool_grow_object(mem, "\0", 1)) {
+               log_error("dm_pool_grow_object failed");
+               goto out_abandon;
+       }
+
+       repstr = dm_pool_end_object(mem);
+       dm_report_field_set_value(field, repstr, repstr);
+       return 1;
+
+      out_abandon:
+       dm_pool_abandon_object(mem);
+       return 0;
+}
+
+static int _dm_tree_parents_count_disp(struct dm_report *rh,
+                                      struct dm_pool *mem,
+                                      struct dm_report_field *field,
+                                      const void *data, void *private)
+{
+       const struct dm_tree_node *node = data;
+       int num_parent = dm_tree_node_num_children(node, 1);
+
+       return dm_report_field_int(rh, field, &num_parent);
+}
+
+static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem,
+                        struct dm_report_field *field, const void *data,
+                        void *private)
+{
+       const struct dm_deps *deps = data;
+       int i;
+       char buf[DM_MAX_TYPE_NAME], *repstr;
+
+       if (!dm_pool_begin_object(mem, 16)) {
+               log_error("dm_pool_begin_object failed");
+               return 0;
+       }
+
+       for (i = 0; i < deps->count; i++) {
+               if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+                      (int) MAJOR(deps->device[i]),
+                      (int) MINOR(deps->device[i])) < 0) {
+                       log_error("dm_snprintf failed");
+                       goto out_abandon;
+               }
+               if (!dm_pool_grow_object(mem, buf, 0)) {
+                       log_error("dm_pool_grow_object failed");
+                       goto out_abandon;
+               }
+               if (i + 1 < deps->count && !dm_pool_grow_object(mem, ",", 1)) {
+                       log_error("dm_pool_grow_object failed");
+                       goto out_abandon;
+               }
+       }
+
+       if (!dm_pool_grow_object(mem, "\0", 1)) {
+               log_error("dm_pool_grow_object failed");
+               goto out_abandon;
+       }
+
+       repstr = dm_pool_end_object(mem);
+       dm_report_field_set_value(field, repstr, repstr);
+       return 1;
+
+      out_abandon:
+       dm_pool_abandon_object(mem);
+       return 0;
+}
+
+static int _dm_subsystem_disp(struct dm_report *rh,
+                              struct dm_pool *mem __attribute__((unused)),
+                              struct dm_report_field *field, const void *data,
+                              void *private __attribute__((unused)))
+{
+       return dm_report_field_string(rh, field, (const char **) data);
+}
+
+static int _dm_vg_name_disp(struct dm_report *rh,
+                            struct dm_pool *mem __attribute__((unused)),
+                            struct dm_report_field *field, const void *data,
+                            void *private __attribute__((unused)))
+{
+
+       return dm_report_field_string(rh, field, (const char **) data);
+}
+
+static int _dm_lv_name_disp(struct dm_report *rh,
+                            struct dm_pool *mem __attribute__((unused)),
+                            struct dm_report_field *field, const void *data,
+                            void *private __attribute__((unused)))
+
+{
+       return dm_report_field_string(rh, field, (const char **) data);
+}
+
+static int _dm_lv_layer_name_disp(struct dm_report *rh,
+                                  struct dm_pool *mem __attribute__((unused)),
+                                  struct dm_report_field *field, const void *data,
+                                  void *private __attribute__((unused)))
+
+{
+       return dm_report_field_string(rh, field, (const char **) data);
+}
+
+static void *_task_get_obj(void *obj)
+{
+       return ((struct dmsetup_report_obj *)obj)->task;
+}
+
+static void *_info_get_obj(void *obj)
+{
+       return ((struct dmsetup_report_obj *)obj)->info;
+}
+
+static void *_deps_get_obj(void *obj)
+{
+       return dm_task_get_deps(((struct dmsetup_report_obj *)obj)->deps_task);
+}
+
+static void *_tree_get_obj(void *obj)
+{
+       return ((struct dmsetup_report_obj *)obj)->tree_node;
+}
+
+static void *_split_name_get_obj(void *obj)
+{
+       return ((struct dmsetup_report_obj *)obj)->split_name;
+}
+
+static const struct dm_report_object_type _report_types[] = {
+       { DR_TASK, "Mapped Device Name", "", _task_get_obj },
+       { DR_INFO, "Mapped Device Information", "", _info_get_obj },
+       { DR_DEPS, "Mapped Device Relationship Information", "", _deps_get_obj },
+       { DR_TREE, "Mapped Device Relationship Information", "", _tree_get_obj },
+       { DR_NAME, "Mapped Device Name Components", "", _split_name_get_obj },
+       { 0, "", "", NULL },
+};
+
+/* Column definitions */
+#define OFFSET_OF(strct, field) (((char*)&((struct strct*)0)->field) - (char*)0)
+#define STR (DM_REPORT_FIELD_TYPE_STRING)
+#define NUM (DM_REPORT_FIELD_TYPE_NUMBER)
+#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, sorttype, OFFSET_OF(strct, field), width, id, head, &_ ## func ## _disp, desc},
+#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, sorttype, 0, width, id, head, &_ ## func ## _disp, desc},
+
+static const struct dm_report_field_type _report_fields[] = {
+/* *INDENT-OFF* */
+FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
+FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
+
+/* FIXME Next one should be INFO */
+FIELD_F(TASK, NUM, "RAhead", 6, dm_read_ahead, "read_ahead", "Read ahead in sectors.")
+
+FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
+FIELD_F(INFO, STR, "Tables", 6, dm_info_table_loaded, "tables_loaded", "Which of the live and inactive table slots are filled.")
+FIELD_F(INFO, STR, "Suspended", 9, dm_info_suspended, "suspended", "Whether the device is suspended.")
+FIELD_F(INFO, STR, "Read-only", 9, dm_info_read_only, "readonly", "Whether the device is read-only or writeable.")
+FIELD_F(INFO, STR, "DevNo", 5, dm_info_devno, "devno", "Device major and minor numbers")
+FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
+FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
+FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
+FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
+FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
+
+FIELD_O(DEPS, dm_deps, NUM, "#Devs", count, 5, int32, "device_count", "Number of devices used by this one.")
+FIELD_F(TREE, STR, "DevNames", 8, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.")
+FIELD_F(DEPS, STR, "DevNos", 6, dm_deps, "devnos_used", "List of device numbers of devices used by this one.")
+
+FIELD_F(TREE, NUM, "#Refs", 5, dm_tree_parents_count, "device_ref_count", "Number of mapped devices referencing this one.")
+FIELD_F(TREE, STR, "RefNames", 8, dm_tree_parents_names, "names_using_dev", "List of names of mapped devices using this one.")
+FIELD_F(TREE, STR, "RefDevNos", 9, dm_tree_parents_devs, "devnos_using_dev", "List of device numbers of mapped devices using this one.")
+
+FIELD_O(NAME, dm_split_name, STR, "Subsys", subsystem, 6, dm_subsystem, "subsystem", "Userspace subsystem responsible for this device.")
+FIELD_O(NAME, dm_split_name, STR, "VG", vg_name, 4, dm_vg_name, "vg_name", "LVM Volume Group name.")
+FIELD_O(NAME, dm_split_name, STR, "LV", lv_name, 4, dm_lv_name, "lv_name", "LVM Logical Volume name.")
+FIELD_O(NAME, dm_split_name, STR, "LVLayer", lv_layer, 7, dm_lv_layer_name, "lv_layer", "LVM device layer.")
+
+{0, 0, 0, 0, "", "", NULL, NULL},
+/* *INDENT-ON* */
+};
+
+#undef STR
+#undef NUM
+#undef FIELD_O
+#undef FIELD_F
+
+static const char *default_report_options = "name,major,minor,attr,open,segments,events,uuid";
+static const char *splitname_report_options = "vg_name,lv_name,lv_layer";
+
+static int _report_init(struct command *c)
+{
+       char *options = (char *) default_report_options;
+       const char *keys = "";
+       const char *separator = " ";
+       int aligned = 1, headings = 1, buffered = 1, field_prefixes = 0;
+       int quoted = 1, columns_as_rows = 0;
+       uint32_t flags = 0;
+       size_t len = 0;
+       int r = 0;
+
+       if (c && !strcmp(c->name, "splitname"))
+               options = (char *) splitname_report_options;
+
+       /* emulate old dmsetup behaviour */
+       if (_switches[NOHEADINGS_ARG]) {
+               separator = ":";
+               aligned = 0;
+               headings = 0;
+       }
+
+       if (_switches[UNBUFFERED_ARG])
+               buffered = 0;
+
+       if (_switches[ROWS_ARG])
+               columns_as_rows = 1;
+
+       if (_switches[UNQUOTED_ARG])
+               quoted = 0;
+
+       if (_switches[NAMEPREFIXES_ARG]) {
+               aligned = 0;
+               field_prefixes = 1;
+       }
+
+       if (_switches[OPTIONS_ARG] && _string_args[OPTIONS_ARG]) {
+               if (*_string_args[OPTIONS_ARG] != '+')
+                       options = _string_args[OPTIONS_ARG];
+               else {
+                       len = strlen(default_report_options) +
+                             strlen(_string_args[OPTIONS_ARG]) + 1;
+                       if (!(options = dm_malloc(len))) {
+                               err("Failed to allocate option string.");
+                               return 0;
+                       }
+                       if (dm_snprintf(options, len, "%s,%s",
+                                       default_report_options,
+                                       &_string_args[OPTIONS_ARG][1]) < 0) {
+                               err("snprintf failed");
+                               goto out;
+                       }
+               }
+       }
+
+       if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
+               keys = _string_args[SORT_ARG];
+               buffered = 1;
+               if (c && (!strcmp(c->name, "status") || !strcmp(c->name, "table"))) {
+                       err("--sort is not yet supported with status and table");
+                       goto out;
+               }
+       }
+
+       if (_switches[SEPARATOR_ARG] && _string_args[SEPARATOR_ARG]) {
+               separator = _string_args[SEPARATOR_ARG];
+               aligned = 0;
+       }
+
+       if (aligned)
+               flags |= DM_REPORT_OUTPUT_ALIGNED;
+
+       if (buffered)
+               flags |= DM_REPORT_OUTPUT_BUFFERED;
+
+       if (headings)
+               flags |= DM_REPORT_OUTPUT_HEADINGS;
+
+       if (field_prefixes)
+               flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
+
+       if (!quoted)
+               flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
+
+       if (columns_as_rows)
+               flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
+
+       if (!(_report = dm_report_init(&_report_type,
+                                      _report_types, _report_fields,
+                                      options, separator, flags, keys, NULL)))
+               goto out;
+
+       if ((_report_type & DR_TREE) && !_build_whole_deptree()) {
+               err("Internal device dependency tree creation failed.");
+               goto out;
+       }
+
+       if (field_prefixes)
+               dm_report_set_output_field_name_prefix(_report, "dm_");
+
+       r = 1;
+
+out:
+       if (!strcasecmp(options, "help") || !strcmp(options, "?"))
+               r = 1;
+
+       if (len)
+               dm_free(options);
+
+       return r;
+}
+
+/*
+ * List devices
+ */
+static int _ls(int argc, char **argv, void *data)
+{
+       if ((_switches[TARGET_ARG] && _target) ||
+           (_switches[EXEC_ARG] && _command))
+               return _status(argc, argv, data);
+       else if ((_switches[TREE_ARG]))
+               return _display_tree(argc, argv, data);
+       else
+               return _process_all(argc, argv, 0, _display_name);
+}
+
+static int _help(int argc, char **argv, void *data);
+
+/*
+ * Dispatch table
+ */
+static struct command _commands[] = {
+       {"help", "[-c|-C|--columns]", 0, 0, _help},
+       {"create", "<dev_name> [-j|--major <major> -m|--minor <minor>]\n"
+         "\t                  [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
+         "\t                  [-u|uuid <uuid>]\n"
+         "\t                  [--notable | --table <table> | <table_file>]",
+        1, 2, _create},
+       {"remove", "[-f|--force] <device>", 0, 1, _remove},
+       {"remove_all", "[-f|--force]", 0, 0, _remove_all},
+       {"suspend", "[--noflush] <device>", 0, 1, _suspend},
+       {"resume", "<device>", 0, 1, _resume},
+       {"load", "<device> [<table_file>]", 0, 2, _load},
+       {"clear", "<device>", 0, 1, _clear},
+       {"reload", "<device> [<table_file>]", 0, 2, _load},
+       {"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, _rename},
+       {"message", "<device> <sector> <message>", 2, -1, _message},
+       {"ls", "[--target <target_type>] [--exec <command>] [--tree [-o options]]", 0, 0, _ls},
+       {"info", "[<device>]", 0, 1, _info},
+       {"deps", "[<device>]", 0, 1, _deps},
+       {"status", "[<device>] [--target <target_type>]", 0, 1, _status},
+       {"table", "[<device>] [--target <target_type>] [--showkeys]", 0, 1, _status},
+       {"wait", "<device> [<event_nr>]", 0, 2, _wait},
+       {"mknodes", "[<device>]", 0, 1, _mknodes},
+       {"udevcreatecookie", "", 0, 0, _udevcreatecookie},
+       {"udevreleasecookie", "[<cookie>]", 0, 1, _udevreleasecookie},
+       {"udevflags", "<cookie>", 1, 1, _udevflags},
+       {"udevcomplete", "<cookie>", 1, 1, _udevcomplete},
+       {"udevcomplete_all", "", 0, 0, _udevcomplete_all},
+       {"udevcookies", "", 0, 0, _udevcookies},
+       {"targets", "", 0, 0, _targets},
+       {"version", "", 0, 0, _version},
+       {"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, _setgeometry},
+       {"splitname", "<device> [<subsystem>]", 1, 2, _splitname},
+       {NULL, NULL, 0, 0, NULL}
+};
+
+static void _usage(FILE *out)
+{
+       int i;
+
+       fprintf(out, "Usage:\n\n");
+       fprintf(out, "dmsetup [--version] [-h|--help [-c|-C|--columns]]\n"
+               "        [-v|--verbose [-v|--verbose ...]]\n"
+               "        [-r|--readonly] [--noopencount] [--nolockfs] [--inactive]\n"
+               "        [--udevcookie] [--noudevrules] [--noudevsync] [-y|--yes]\n"
+               "        [--readahead [+]<sectors>|auto|none]\n"
+               "        [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
+               "        [--nameprefixes] [--noheadings] [--separator <separator>]\n\n");
+       for (i = 0; _commands[i].name; i++)
+               fprintf(out, "\t%s %s\n", _commands[i].name, _commands[i].help);
+       fprintf(out, "\n<device> may be device name or -u <uuid> or "
+                    "-j <major> -m <minor>\n");
+       fprintf(out, "<fields> are comma-separated.  Use 'help -c' for list.\n");
+       fprintf(out, "Table_file contents may be supplied on stdin.\n");
+       fprintf(out, "Tree options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
+                    "                  [no]device, active, open, rw and uuid.\n");
+       fprintf(out, "\n");
+}
+
+static void _losetup_usage(FILE *out)
+{
+       fprintf(out, "Usage:\n\n");
+       fprintf(out, "losetup [-d|-a] [-e encryption] "
+                    "[-o offset] [-f|loop_device] [file]\n\n");
+}
+
+static int _help(int argc __attribute__((unused)),
+                char **argv __attribute__((unused)),
+                void *data __attribute__((unused)))
+{
+       _usage(stderr);
+
+       if (_switches[COLS_ARG]) {
+               _switches[OPTIONS_ARG] = 1;
+               _string_args[OPTIONS_ARG] = (char *) "help";
+               _switches[SORT_ARG] = 0;
+
+               if (_report) {
+                       dm_report_free(_report);
+                       _report = NULL;
+               }
+               (void) _report_init(NULL);
+       }
+
+       return 1;
+}
+
+static struct command *_find_command(const char *name)
+{
+       int i;
+
+       for (i = 0; _commands[i].name; i++)
+               if (!strcmp(_commands[i].name, name))
+                       return _commands + i;
+
+       return NULL;
+}
+
+static int _process_tree_options(const char *options)
+{
+       const char *s, *end;
+       struct winsize winsz;
+       size_t len;
+
+       /* Symbol set default */
+       if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
+               _tsym = &_tsym_utf;
+       else
+               _tsym = &_tsym_ascii;
+
+       /* Default */
+       _tree_switches[TR_DEVICE] = 1;
+       _tree_switches[TR_TRUNCATE] = 1;
+
+       /* parse */
+       for (s = options; s && *s; s++) {
+               len = 0;
+               for (end = s; *end && *end != ','; end++, len++)
+                       ;
+               if (!strncmp(s, "device", len))
+                       _tree_switches[TR_DEVICE] = 1;
+               else if (!strncmp(s, "nodevice", len))
+                       _tree_switches[TR_DEVICE] = 0;
+               else if (!strncmp(s, "status", len))
+                       _tree_switches[TR_STATUS] = 1;
+               else if (!strncmp(s, "table", len))
+                       _tree_switches[TR_TABLE] = 1;
+               else if (!strncmp(s, "active", len))
+                       _tree_switches[TR_ACTIVE] = 1;
+               else if (!strncmp(s, "open", len))
+                       _tree_switches[TR_OPENCOUNT] = 1;
+               else if (!strncmp(s, "uuid", len))
+                       _tree_switches[TR_UUID] = 1;
+               else if (!strncmp(s, "rw", len))
+                       _tree_switches[TR_RW] = 1;
+               else if (!strncmp(s, "utf", len))
+                       _tsym = &_tsym_utf;
+               else if (!strncmp(s, "vt100", len))
+                       _tsym = &_tsym_vt100;
+               else if (!strncmp(s, "ascii", len))
+                       _tsym = &_tsym_ascii;
+               else if (!strncmp(s, "inverted", len))
+                       _tree_switches[TR_BOTTOMUP] = 1;
+               else if (!strncmp(s, "compact", len))
+                       _tree_switches[TR_COMPACT] = 1;
+               else if (!strncmp(s, "notrunc", len))
+                       _tree_switches[TR_TRUNCATE] = 0;
+               else {
+                       fprintf(stderr, "Tree options not recognised: %s\n", s);
+                       return 0;
+               }
+               if (!*end)
+                       break;
+               s = end;
+       }
+
+       /* Truncation doesn't work well with vt100 drawing char */
+       if (_tsym != &_tsym_vt100)
+               if (ioctl(1, (unsigned long) TIOCGWINSZ, &winsz) >= 0 && winsz.ws_col > 3)
+                       _termwidth = winsz.ws_col - 3;
+
+       return 1;
+}
+
+/*
+ * Returns the full absolute path, or NULL if the path could
+ * not be resolved.
+ */
+static char *_get_abspath(const char *path)
+{
+       char *_path;
+
+#ifdef HAVE_CANONICALIZE_FILE_NAME
+       _path = canonicalize_file_name(path);
+#else
+       /* FIXME Provide alternative */
+#endif
+       return _path;
+}
+
+static char *parse_loop_device_name(const char *dev, const char *dev_dir)
+{
+       char *buf;
+       char *device;
+
+       if (!(buf = dm_malloc(PATH_MAX)))
+               return NULL;
+
+       if (dev[0] == '/') {
+               if (!(device = _get_abspath(dev)))
+                       goto error;
+
+               if (strncmp(device, dev_dir, strlen(dev_dir)))
+                       goto error;
+
+               /* If dev_dir does not end in a slash, ensure that the
+                  following byte in the device string is "/".  */
+               if (dev_dir[strlen(dev_dir) - 1] != '/' &&
+                   device[strlen(dev_dir)] != '/')
+                       goto error;
+
+               strncpy(buf, strrchr(device, '/') + 1, (size_t) PATH_MAX);
+               dm_free(device);
+
+       } else {
+               /* check for device number */
+               if (!strncmp(dev, "loop", strlen("loop")))
+                       strncpy(buf, dev, (size_t) PATH_MAX);
+               else
+                       goto error;
+       }
+
+       return buf;
+
+error:
+       dm_free(buf);
+       return NULL;
+}
+
+/*
+ *  create a table for a mapped device using the loop target.
+ */
+static int _loop_table(char *table, size_t tlen, char *file,
+                      char *dev __attribute__((unused)), off_t off)
+{
+       struct stat fbuf;
+       off_t size, sectors;
+       int fd = -1;
+#ifdef HAVE_SYS_STATVFS_H
+       struct statvfs fsbuf;
+       off_t blksize;
+#endif
+
+       if (!_switches[READ_ONLY])
+               fd = open(file, O_RDWR);
+
+       if (fd < 0) {
+               _switches[READ_ONLY]++;
+               fd = open(file, O_RDONLY);
+       }
+
+       if (fd < 0)
+               goto error;
+
+       if (fstat(fd, &fbuf))
+               goto error;
+
+       size = (fbuf.st_size - off);
+       sectors = size >> SECTOR_SHIFT;
+
+       if (_switches[VERBOSE_ARG])
+               fprintf(stderr, "losetup: set loop size to %llukB "
+                       "(%llu sectors)\n", (long long unsigned) sectors >> 1,
+                       (long long unsigned) sectors);
+
+#ifdef HAVE_SYS_STATVFS_H
+       if (fstatvfs(fd, &fsbuf))
+               goto error;
+
+       /* FIXME Fragment size currently unused */
+       blksize = fsbuf.f_frsize;
+#endif
+
+       close(fd);
+
+       if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL,
+                       (long long unsigned)sectors, file, (long long unsigned)off) < 0)
+               return 0;
+
+       if (_switches[VERBOSE_ARG] > 1)
+               fprintf(stderr, "Table: %s\n", table);
+
+       return 1;
+
+error:
+       if (fd > -1)
+               close(fd);
+       return 0;
+}
+
+static int _process_losetup_switches(const char *base, int *argc, char ***argv,
+                                    const char *dev_dir)
+{
+       static int ind;
+       int c;
+       int encrypt_loop = 0, delete = 0, find = 0, show_all = 0;
+       char *device_name = NULL;
+       char *loop_file = NULL;
+       off_t offset = 0;
+
+#ifdef HAVE_GETOPTLONG
+       static struct option long_options[] = {
+               {0, 0, 0, 0}
+       };
+#endif
+
+       optarg = 0;
+       optind = OPTIND_INIT;
+       while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "ade:fo:v",
+                                           long_options, NULL)) != -1 ) {
+               if (c == ':' || c == '?')
+                       return 0;
+               if (c == 'a')
+                       show_all++;
+               if (c == 'd')
+                       delete++;
+               if (c == 'e')
+                       encrypt_loop++;
+               if (c == 'f')
+                       find++;
+               if (c == 'o')
+                       offset = atoi(optarg);
+               if (c == 'v')
+                       _switches[VERBOSE_ARG]++;
+       }
+
+       *argv += optind ;
+       *argc -= optind ;
+
+       if (encrypt_loop){
+               fprintf(stderr, "%s: Sorry, cryptoloop is not yet implemented "
+                               "in this version.\n", base);
+               return 0;
+       }
+
+       if (show_all) {
+               fprintf(stderr, "%s: Sorry, show all is not yet implemented "
+                               "in this version.\n", base);
+               return 0;
+       }
+
+       if (find) {
+               fprintf(stderr, "%s: Sorry, find is not yet implemented "
+                               "in this version.\n", base);
+               if (!*argc)
+                       return 0;
+       }
+
+       if (!*argc) {
+               fprintf(stderr, "%s: Please specify loop_device.\n", base);
+               _losetup_usage(stderr);
+               return 0;
+       }
+
+       if (!(device_name = parse_loop_device_name((*argv)[0], dev_dir))) {
+               fprintf(stderr, "%s: Could not parse loop_device %s\n",
+                       base, (*argv)[0]);
+               _losetup_usage(stderr);
+               return 0;
+       }
+
+       if (delete) {
+               *argc = 2;
+
+               (*argv)[1] = device_name;
+               (*argv)[0] = (char *) "remove";
+
+               return 1;
+       }
+
+       if (*argc != 2) {
+               fprintf(stderr, "%s: Too few arguments\n", base);
+               _losetup_usage(stderr);
+               dm_free(device_name);
+               return 0;
+       }
+
+       /* FIXME move these to make them available to native dmsetup */
+       if (!(loop_file = _get_abspath((*argv)[(find) ? 0 : 1]))) {
+               fprintf(stderr, "%s: Could not parse loop file name %s\n",
+                       base, (*argv)[1]);
+               _losetup_usage(stderr);
+               dm_free(device_name);
+               return 0;
+       }
+
+       /* FIXME Missing free */
+       _table = dm_malloc(LOOP_TABLE_SIZE);
+       if (!_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) {
+               fprintf(stderr, "Could not build device-mapper table for %s\n", (*argv)[0]);
+               dm_free(device_name);
+               return 0;
+       }
+       _switches[TABLE_ARG]++;
+
+       (*argv)[0] = (char *) "create";
+       (*argv)[1] = device_name ;
+
+       return 1;
+}
+
+static int _process_switches(int *argc, char ***argv, const char *dev_dir)
+{
+       char *base, *namebase, *s;
+       static int ind;
+       int c, r;
+
+#ifdef HAVE_GETOPTLONG
+       static struct option long_options[] = {
+               {"readonly", 0, &ind, READ_ONLY},
+               {"columns", 0, &ind, COLS_ARG},
+               {"exec", 1, &ind, EXEC_ARG},
+               {"force", 0, &ind, FORCE_ARG},
+               {"gid", 1, &ind, GID_ARG},
+               {"help", 0, &ind, HELP_ARG},
+               {"inactive", 0, &ind, INACTIVE_ARG},
+               {"major", 1, &ind, MAJOR_ARG},
+               {"minor", 1, &ind, MINOR_ARG},
+               {"mode", 1, &ind, MODE_ARG},
+               {"nameprefixes", 0, &ind, NAMEPREFIXES_ARG},
+               {"noflush", 0, &ind, NOFLUSH_ARG},
+               {"noheadings", 0, &ind, NOHEADINGS_ARG},
+               {"nolockfs", 0, &ind, NOLOCKFS_ARG},
+               {"noopencount", 0, &ind, NOOPENCOUNT_ARG},
+               {"notable", 0, &ind, NOTABLE_ARG},
+               {"udevcookie", 1, &ind, UDEVCOOKIE_ARG},
+               {"noudevrules", 0, &ind, NOUDEVRULES_ARG},
+               {"noudevsync", 0, &ind, NOUDEVSYNC_ARG},
+               {"options", 1, &ind, OPTIONS_ARG},
+               {"readahead", 1, &ind, READAHEAD_ARG},
+               {"rows", 0, &ind, ROWS_ARG},
+               {"separator", 1, &ind, SEPARATOR_ARG},
+               {"setuuid", 0, &ind, SETUUID_ARG},
+               {"showkeys", 0, &ind, SHOWKEYS_ARG},
+               {"sort", 1, &ind, SORT_ARG},
+               {"table", 1, &ind, TABLE_ARG},
+               {"target", 1, &ind, TARGET_ARG},
+               {"tree", 0, &ind, TREE_ARG},
+               {"uid", 1, &ind, UID_ARG},
+               {"uuid", 1, &ind, UUID_ARG},
+               {"unbuffered", 0, &ind, UNBUFFERED_ARG},
+               {"unquoted", 0, &ind, UNQUOTED_ARG},
+               {"verbose", 1, &ind, VERBOSE_ARG},
+               {"version", 0, &ind, VERSION_ARG},
+               {"yes", 0, &ind, YES_ARG},
+               {0, 0, 0, 0}
+       };
+#else
+       struct option long_options;
+#endif
+
+       /*
+        * Zero all the index counts.
+        */
+       memset(&_switches, 0, sizeof(_switches));
+       memset(&_int_args, 0, sizeof(_int_args));
+       _read_ahead_flags = 0;
+
+       namebase = strdup((*argv)[0]);
+       base = basename(namebase);
+
+       if (!strcmp(base, "devmap_name")) {
+               free(namebase);
+               _switches[COLS_ARG]++;
+               _switches[NOHEADINGS_ARG]++;
+               _switches[OPTIONS_ARG]++;
+               _switches[MAJOR_ARG]++;
+               _switches[MINOR_ARG]++;
+               _string_args[OPTIONS_ARG] = (char *) "name";
+
+               if (*argc == 3) {
+                       _int_args[MAJOR_ARG] = atoi((*argv)[1]);
+                       _int_args[MINOR_ARG] = atoi((*argv)[2]);
+                       *argc -= 2;
+                       *argv += 2;
+               } else if ((*argc == 2) &&
+                          (2 == sscanf((*argv)[1], "%i:%i",
+                                       &_int_args[MAJOR_ARG],
+                                       &_int_args[MINOR_ARG]))) {
+                       *argc -= 1;
+                       *argv += 1;
+               } else {
+                       fprintf(stderr, "Usage: devmap_name <major> <minor>\n");
+                       return 0;
+               }
+
+               (*argv)[0] = (char *) "info";
+               return 1;
+       }
+
+       if (!strcmp(base, "losetup") || !strcmp(base, "dmlosetup")){
+               r = _process_losetup_switches(base, argc, argv, dev_dir);
+               free(namebase);
+               return r;
+       }
+
+       free(namebase);
+
+       optarg = 0;
+       optind = OPTIND_INIT;
+       while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfG:hj:m:M:no:O:ru:U:vy",
+                                           long_options, NULL)) != -1) {
+               if (c == ':' || c == '?')
+                       return 0;
+               if (c == 'h' || ind == HELP_ARG)
+                       _switches[HELP_ARG]++;
+               if (c == 'c' || c == 'C' || ind == COLS_ARG)
+                       _switches[COLS_ARG]++;
+               if (c == 'f' || ind == FORCE_ARG)
+                       _switches[FORCE_ARG]++;
+               if (c == 'r' || ind == READ_ONLY)
+                       _switches[READ_ONLY]++;
+               if (c == 'j' || ind == MAJOR_ARG) {
+                       _switches[MAJOR_ARG]++;
+                       _int_args[MAJOR_ARG] = atoi(optarg);
+               }
+               if (c == 'm' || ind == MINOR_ARG) {
+                       _switches[MINOR_ARG]++;
+                       _int_args[MINOR_ARG] = atoi(optarg);
+               }
+               if (c == 'n' || ind == NOTABLE_ARG)
+                       _switches[NOTABLE_ARG]++;
+               if (c == 'o' || ind == OPTIONS_ARG) {
+                       _switches[OPTIONS_ARG]++;
+                       _string_args[OPTIONS_ARG] = optarg;
+               }
+               if (ind == SEPARATOR_ARG) {
+                       _switches[SEPARATOR_ARG]++;
+                       _string_args[SEPARATOR_ARG] = optarg;
+               }
+               if (c == 'O' || ind == SORT_ARG) {
+                       _switches[SORT_ARG]++;
+                       _string_args[SORT_ARG] = optarg;
+               }
+               if (c == 'v' || ind == VERBOSE_ARG)
+                       _switches[VERBOSE_ARG]++;
+               if (c == 'u' || ind == UUID_ARG) {
+                       _switches[UUID_ARG]++;
+                       _uuid = optarg;
+               }
+               if (c == 'y' || ind == YES_ARG)
+                       _switches[YES_ARG]++;
+               if (ind == UDEVCOOKIE_ARG) {
+                       _switches[UDEVCOOKIE_ARG]++;
+                       _udev_cookie = _get_cookie_value(optarg);
+               }
+               if (ind == NOUDEVRULES_ARG)
+                       _switches[NOUDEVRULES_ARG]++;
+               if (ind == NOUDEVSYNC_ARG)
+                       _switches[NOUDEVSYNC_ARG]++;
+               if (c == 'G' || ind == GID_ARG) {
+                       _switches[GID_ARG]++;
+                       _int_args[GID_ARG] = atoi(optarg);
+               }
+               if (c == 'U' || ind == UID_ARG) {
+                       _switches[UID_ARG]++;
+                       _int_args[UID_ARG] = atoi(optarg);
+               }
+               if (c == 'M' || ind == MODE_ARG) {
+                       _switches[MODE_ARG]++;
+                       /* FIXME Accept modes as per chmod */
+                       _int_args[MODE_ARG] = (int) strtol(optarg, NULL, 8);
+               }
+               if ((ind == EXEC_ARG)) {
+                       _switches[EXEC_ARG]++;
+                       _command = optarg;
+               }
+               if ((ind == TARGET_ARG)) {
+                       _switches[TARGET_ARG]++;
+                       _target = optarg;
+               }
+               if ((ind == INACTIVE_ARG))
+                       _switches[INACTIVE_ARG]++;
+               if ((ind == NAMEPREFIXES_ARG))
+                       _switches[NAMEPREFIXES_ARG]++;
+               if ((ind == NOFLUSH_ARG))
+                       _switches[NOFLUSH_ARG]++;
+               if ((ind == NOHEADINGS_ARG))
+                       _switches[NOHEADINGS_ARG]++;
+               if ((ind == NOLOCKFS_ARG))
+                       _switches[NOLOCKFS_ARG]++;
+               if ((ind == NOOPENCOUNT_ARG))
+                       _switches[NOOPENCOUNT_ARG]++;
+               if ((ind == READAHEAD_ARG)) {
+                       _switches[READAHEAD_ARG]++;
+                       if (!strcasecmp(optarg, "auto"))
+                               _int_args[READAHEAD_ARG] = DM_READ_AHEAD_AUTO;
+                       else if (!strcasecmp(optarg, "none"))
+                               _int_args[READAHEAD_ARG] = DM_READ_AHEAD_NONE;
+                       else {
+                               for (s = optarg; isspace(*s); s++)
+                                       ;
+                               if (*s == '+')
+                                       _read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
+                               _int_args[READAHEAD_ARG] = atoi(optarg);
+                               if (_int_args[READAHEAD_ARG] < -1) {
+                                       log_error("Negative read ahead value "
+                                                 "(%d) is not understood.",
+                                                 _int_args[READAHEAD_ARG]);
+                                       return 0;
+                               }
+                       }
+               }
+               if ((ind == ROWS_ARG))
+                       _switches[ROWS_ARG]++;
+               if ((ind == SETUUID_ARG))
+                       _switches[SETUUID_ARG]++;
+               if ((ind == SHOWKEYS_ARG))
+                       _switches[SHOWKEYS_ARG]++;
+               if ((ind == TABLE_ARG)) {
+                       _switches[TABLE_ARG]++;
+                       _table = optarg;
+               }
+               if ((ind == TREE_ARG))
+                       _switches[TREE_ARG]++;
+               if ((ind == UNQUOTED_ARG))
+                       _switches[UNQUOTED_ARG]++;
+               if ((ind == VERSION_ARG))
+                       _switches[VERSION_ARG]++;
+       }
+
+       if (_switches[VERBOSE_ARG] > 1)
+               dm_log_init_verbose(_switches[VERBOSE_ARG] - 1);
+
+       if ((_switches[MAJOR_ARG] && !_switches[MINOR_ARG]) ||
+           (!_switches[MAJOR_ARG] && _switches[MINOR_ARG])) {
+               fprintf(stderr, "Please specify both major number and "
+                               "minor number.\n");
+               return 0;
+       }
+
+       if (_switches[TREE_ARG] && !_process_tree_options(_string_args[OPTIONS_ARG]))
+               return 0;
+
+       if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) {
+               fprintf(stderr, "--table and --notable are incompatible.\n");
+               return 0;
+       }
+
+       *argv += optind;
+       *argc -= optind;
+       return 1;
+}
+
+int main(int argc, char **argv)
+{
+       struct command *c;
+       int r = 1;
+       const char *dev_dir;
+
+       (void) setlocale(LC_ALL, "");
+
+       dev_dir = getenv (DM_DEV_DIR_ENV_VAR_NAME);
+       if (dev_dir && *dev_dir) {
+               if (!dm_set_dev_dir(dev_dir)) {
+                       fprintf(stderr, "Invalid DM_DEV_DIR environment variable value.\n");
+                       goto out;
+               }
+       } else
+               dev_dir = DEFAULT_DM_DEV_DIR;
+
+       if (!_process_switches(&argc, &argv, dev_dir)) {
+               fprintf(stderr, "Couldn't process command line.\n");
+               goto out;
+       }
+
+       if (_switches[HELP_ARG]) {
+               c = _find_command("help");
+               goto doit;
+       }
+
+       if (_switches[VERSION_ARG]) {
+               c = _find_command("version");
+               goto doit;
+       }
+
+       if (argc == 0) {
+               _usage(stderr);
+               goto out;
+       }
+
+       if (!(c = _find_command(argv[0]))) {
+               fprintf(stderr, "Unknown command\n");
+               _usage(stderr);
+               goto out;
+       }
+
+       if (argc < c->min_args + 1 ||
+           (c->max_args >= 0 && argc > c->max_args + 1)) {
+               fprintf(stderr, "Incorrect number of arguments\n");
+               _usage(stderr);
+               goto out;
+       }
+
+       if (!_switches[COLS_ARG] && !strcmp(c->name, "splitname"))
+               _switches[COLS_ARG]++;
+
+       if (_switches[COLS_ARG]) {
+               if (!_report_init(c))
+                       goto out;
+               if (!_report) {
+                       if (!strcmp(c->name, "info"))
+                               r = 0;  /* info -c -o help */
+                       goto out;
+               }
+       }
+
+       #ifdef UDEV_SYNC_SUPPORT
+       if (!_set_up_udev_support(dev_dir))
+               goto out;
+       #endif
+
+      doit:
+       if (!c->fn(argc, argv, NULL)) {
+               fprintf(stderr, "Command failed\n");
+               goto out;
+       }
+
+       r = 0;
+
+out:
+       if (_report) {
+               dm_report_output(_report);
+               dm_report_free(_report);
+       }
+
+       if (_dtree)
+               dm_tree_free(_dtree);
+
+       return r;
+}
diff --git a/tools/dumpconfig.c b/tools/dumpconfig.c
new file mode 100644 (file)
index 0000000..981147c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int dumpconfig(struct cmd_context *cmd, int argc, char **argv)
+{
+       const char *file = arg_str_value(cmd, file_ARG, NULL);
+
+       if (!write_config_file(cmd->cft, file, argc, argv)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
diff --git a/tools/formats.c b/tools/formats.c
new file mode 100644 (file)
index 0000000..a04f23d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int formats(struct cmd_context *cmd, int argc __attribute__((unused)),
+           char **argv __attribute__((unused)))
+{
+       display_formats(cmd);
+
+       return ECMD_PROCESSED;
+}
diff --git a/tools/lvchange.c b/tools/lvchange.c
new file mode 100644 (file)
index 0000000..8161d22
--- /dev/null
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int lvchange_permission(struct cmd_context *cmd,
+                              struct logical_volume *lv)
+{
+       uint32_t lv_access;
+       struct lvinfo info;
+       int r = 0;
+
+       lv_access = arg_uint_value(cmd, permission_ARG, 0);
+
+       if ((lv_access & LVM_WRITE) && (lv->status & LVM_WRITE)) {
+               log_error("Logical volume \"%s\" is already writable",
+                         lv->name);
+               return 0;
+       }
+
+       if (!(lv_access & LVM_WRITE) && !(lv->status & LVM_WRITE)) {
+               log_error("Logical volume \"%s\" is already read only",
+                         lv->name);
+               return 0;
+       }
+
+       if ((lv->status & MIRRORED) && (vg_is_clustered(lv->vg)) &&
+           lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+               log_error("Cannot change permissions of mirror \"%s\" "
+                         "while active.", lv->name);
+               return 0;
+       }
+
+       if (lv_access & LVM_WRITE) {
+               lv->status |= LVM_WRITE;
+               log_verbose("Setting logical volume \"%s\" read/write",
+                           lv->name);
+       } else {
+               lv->status &= ~LVM_WRITE;
+               log_verbose("Setting logical volume \"%s\" read-only",
+                           lv->name);
+       }
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+       if (!vg_write(lv->vg))
+               return_0;
+
+       if (!suspend_lv(cmd, lv)) {
+               log_error("Failed to lock %s", lv->name);
+               vg_revert(lv->vg);
+               goto out;
+       }
+
+       if (!vg_commit(lv->vg)) {
+               if (!resume_lv(cmd, lv))
+                       stack;
+               goto_out;
+       }
+
+       log_very_verbose("Updating permissions for \"%s\" in kernel", lv->name);
+       if (!resume_lv(cmd, lv)) {
+               log_error("Problem reactivating %s", lv->name);
+               goto out;
+       }
+
+       r = 1;
+out:
+       backup(lv->vg);
+       return r;
+}
+
+static int lvchange_monitoring(struct cmd_context *cmd,
+                              struct logical_volume *lv)
+{
+       struct lvinfo info;
+
+       if (!lv_info(cmd, lv, 0, &info, 0, 0) || !info.exists) {
+               log_error("Logical volume, %s, is not active", lv->name);
+               return 0;
+       }
+
+       /* do not monitor pvmove lv's */
+       if (lv->status & PVMOVE)
+               return 1;
+
+       if ((dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) &&
+           !monitor_dev_for_events(cmd, lv, 0, dmeventd_monitor_mode()))
+               return_0;
+
+       return 1;
+}
+
+static int lvchange_background_polling(struct cmd_context *cmd,
+                                      struct logical_volume *lv)
+{
+       struct lvinfo info;
+
+       if (!lv_info(cmd, lv, 0, &info, 0, 0) || !info.exists) {
+               log_error("Logical volume, %s, is not active", lv->name);
+               return 0;
+       }
+
+       if (background_polling())
+               lv_spawn_background_polling(cmd, lv);
+
+       return 1;
+}
+
+static int lvchange_availability(struct cmd_context *cmd,
+                                struct logical_volume *lv)
+{
+       int activate;
+
+       activate = arg_uint_value(cmd, available_ARG, 0);
+
+       if (activate == CHANGE_ALN) {
+               log_verbose("Deactivating logical volume \"%s\" locally",
+                           lv->name);
+               if (!deactivate_lv_local(cmd, lv))
+                       return_0;
+       } else if (activate == CHANGE_AN) {
+               log_verbose("Deactivating logical volume \"%s\"", lv->name);
+               if (!deactivate_lv(cmd, lv))
+                       return_0;
+       } else {
+               if (lv_is_origin(lv) || (activate == CHANGE_AE)) {
+                       log_verbose("Activating logical volume \"%s\" "
+                                   "exclusively", lv->name);
+                       if (!activate_lv_excl(cmd, lv))
+                               return_0;
+               } else if (activate == CHANGE_ALY) {
+                       log_verbose("Activating logical volume \"%s\" locally",
+                                   lv->name);
+                       if (!activate_lv_local(cmd, lv))
+                               return_0;
+               } else {
+                       log_verbose("Activating logical volume \"%s\"",
+                                   lv->name);
+                       if (!activate_lv(cmd, lv))
+                               return_0;
+               }
+
+               if (background_polling())
+                       lv_spawn_background_polling(cmd, lv);
+       }
+
+       return 1;
+}
+
+static int lvchange_refresh(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       log_verbose("Refreshing logical volume \"%s\" (if active)", lv->name);
+
+       return lv_refresh(cmd, lv);
+}
+
+static int lvchange_resync(struct cmd_context *cmd,
+                             struct logical_volume *lv)
+{
+       int active = 0;
+       int monitored;
+       struct lvinfo info;
+       struct logical_volume *log_lv;
+
+       if (!(lv->status & MIRRORED)) {
+               log_error("Unable to resync %s because it is not mirrored.",
+                         lv->name);
+               return 1;
+       }
+
+       if (lv->status & PVMOVE) {
+               log_error("Unable to resync pvmove volume %s", lv->name);
+               return 0;
+       }
+
+       if (lv->status & LOCKED) {
+               log_error("Unable to resync locked volume %s", lv->name);
+               return 0;
+       }
+
+       if (lv_info(cmd, lv, 0, &info, 1, 0)) {
+               if (info.open_count) {
+                       log_error("Can't resync open logical volume \"%s\"",
+                                 lv->name);
+                       return 0;
+               }
+
+               if (info.exists) {
+                       if (!arg_count(cmd, yes_ARG) &&
+                           yes_no_prompt("Do you really want to deactivate "
+                                         "logical volume %s to resync it? [y/n]: ",
+                                         lv->name) == 'n') {
+                               log_error("Logical volume \"%s\" not resynced",
+                                         lv->name);
+                               return 0;
+                       }
+
+                       if (sigint_caught())
+                               return 0;
+
+                       active = 1;
+               }
+       }
+
+       /* Activate exclusively to ensure no nodes still have LV active */
+       monitored = dmeventd_monitor_mode();
+       init_dmeventd_monitor(0);
+
+       if (!deactivate_lv(cmd, lv)) {
+               log_error("Unable to deactivate %s for resync", lv->name);
+               return 0;
+       }
+
+       if (vg_is_clustered(lv->vg) && lv_is_active(lv)) {
+               log_error("Can't get exclusive access to clustered volume %s",
+                         lv->name);
+               return 0;
+       }
+
+       init_dmeventd_monitor(monitored);
+
+       log_lv = first_seg(lv)->log_lv;
+
+       log_very_verbose("Starting resync of %s%s%s mirror \"%s\"",
+                        (active) ? "active " : "",
+                        vg_is_clustered(lv->vg) ? "clustered " : "",
+                        (log_lv) ? "disk-logged" : "core-logged",
+                        lv->name);
+
+       /*
+        * If this mirror has a core log (i.e. !log_lv),
+        * then simply deactivating/activating will cause
+        * it to reset the sync status.  We only need to
+        * worry about persistent logs.
+        */
+       if (!log_lv && !(lv->status & MIRROR_NOTSYNCED)) {
+               if (active && !activate_lv(cmd, lv)) {
+                       log_error("Failed to reactivate %s to resynchronize "
+                                 "mirror", lv->name);
+                       return 0;
+               }
+               return 1;
+       }
+
+       lv->status &= ~MIRROR_NOTSYNCED;
+
+       if (log_lv) {
+               /* Separate mirror log so we can clear it */
+               detach_mirror_log(first_seg(lv));
+
+               if (!vg_write(lv->vg)) {
+                       log_error("Failed to write intermediate VG metadata.");
+                       if (!attach_mirror_log(first_seg(lv), log_lv))
+                               stack;
+                       if (active && !activate_lv(cmd, lv))
+                               stack;
+                       return 0;
+               }
+
+               if (!vg_commit(lv->vg)) {
+                       log_error("Failed to commit intermediate VG metadata.");
+                       if (!attach_mirror_log(first_seg(lv), log_lv))
+                               stack;
+                       if (active && !activate_lv(cmd, lv))
+                               stack;
+                       return 0;
+               }
+
+               backup(lv->vg);
+
+               if (!activate_lv(cmd, log_lv)) {
+                       log_error("Unable to activate %s for mirror log resync",
+                                 log_lv->name);
+                       return 0;
+               }
+
+               log_very_verbose("Clearing log device %s", log_lv->name);
+               if (!set_lv(cmd, log_lv, log_lv->size, 0)) {
+                       log_error("Unable to reset sync status for %s", lv->name);
+                       if (!deactivate_lv(cmd, log_lv))
+                               log_error("Failed to deactivate log LV after "
+                                         "wiping failed");
+                       return 0;
+               }
+
+               if (!deactivate_lv(cmd, log_lv)) {
+                       log_error("Unable to deactivate log LV %s after wiping "
+                                 "for resync", log_lv->name);
+                       return 0;
+               }
+
+               /* Put mirror log back in place */
+               if (!attach_mirror_log(first_seg(lv), log_lv))
+                       stack;
+       }
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+       if (!vg_write(lv->vg) || !vg_commit(lv->vg)) {
+               log_error("Failed to update metadata on disk.");
+               return 0;
+       }
+
+       if (active && !activate_lv(cmd, lv)) {
+               log_error("Failed to reactivate %s after resync", lv->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int lvchange_alloc(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       int want_contiguous = 0;
+       alloc_policy_t alloc;
+
+       want_contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n");
+       alloc = want_contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT;
+       alloc = arg_uint_value(cmd, alloc_ARG, alloc);
+
+       if (alloc == lv->alloc) {
+               log_error("Allocation policy of logical volume \"%s\" is "
+                         "already %s", lv->name, get_alloc_string(alloc));
+               return 0;
+       }
+
+       lv->alloc = alloc;
+
+       /* FIXME If contiguous, check existing extents already are */
+
+       log_verbose("Setting contiguous allocation policy for \"%s\" to %s",
+                   lv->name, get_alloc_string(alloc));
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+       /* No need to suspend LV for this change */
+       if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+               return_0;
+
+       backup(lv->vg);
+
+       return 1;
+}
+
+static int lvchange_readahead(struct cmd_context *cmd,
+                             struct logical_volume *lv)
+{
+       unsigned read_ahead = 0;
+       unsigned pagesize = (unsigned) lvm_getpagesize() >> SECTOR_SHIFT;
+       int r = 0;
+
+       read_ahead = arg_uint_value(cmd, readahead_ARG, 0);
+
+       if (read_ahead != DM_READ_AHEAD_AUTO &&
+           (lv->vg->fid->fmt->features & FMT_RESTRICTED_READAHEAD) &&
+           (read_ahead < 2 || read_ahead > 120)) {
+               log_error("Metadata only supports readahead values between 2 and 120.");
+               return 0;
+       }
+
+       if (read_ahead != DM_READ_AHEAD_AUTO &&
+           read_ahead != DM_READ_AHEAD_NONE && read_ahead % pagesize) {
+               if (read_ahead < pagesize)
+                       read_ahead = pagesize;
+               else
+                       read_ahead = (read_ahead / pagesize) * pagesize;
+               log_warn("WARNING: Overriding readahead to %u sectors, a multiple "
+                           "of %uK page size.", read_ahead, pagesize >> 1);
+       }
+
+       if (lv->read_ahead == read_ahead) {
+               if (read_ahead == DM_READ_AHEAD_AUTO)
+                       log_error("Read ahead is already auto for \"%s\"", lv->name);
+               else
+                       log_error("Read ahead is already %u for \"%s\"",
+                                 read_ahead, lv->name);
+               return 0;
+       }
+
+       lv->read_ahead = read_ahead;
+
+       log_verbose("Setting read ahead to %u for \"%s\"", read_ahead,
+                   lv->name);
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+       if (!vg_write(lv->vg))
+               return_0;
+
+       if (!suspend_lv(cmd, lv)) {
+               log_error("Failed to lock %s", lv->name);
+               vg_revert(lv->vg);
+               goto out;
+       }
+
+       if (!vg_commit(lv->vg)) {
+               if (!resume_lv(cmd, lv))
+                       stack;
+               goto_out;
+       }
+
+       log_very_verbose("Updating permissions for \"%s\" in kernel", lv->name);
+       if (!resume_lv(cmd, lv)) {
+               log_error("Problem reactivating %s", lv->name);
+               goto out;
+       }
+
+       r = 1;
+out:
+       backup(lv->vg);
+       return r;
+}
+
+static int lvchange_persistent(struct cmd_context *cmd,
+                              struct logical_volume *lv)
+{
+       struct lvinfo info;
+       int active = 0;
+
+       if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "n")) {
+               if (!(lv->status & FIXED_MINOR)) {
+                       log_error("Minor number is already not persistent "
+                                 "for \"%s\"", lv->name);
+                       return 0;
+               }
+               lv->status &= ~FIXED_MINOR;
+               lv->minor = -1;
+               lv->major = -1;
+               log_verbose("Disabling persistent device number for \"%s\"",
+                           lv->name);
+       } else {
+               if (!arg_count(cmd, minor_ARG) && lv->minor < 0) {
+                       log_error("Minor number must be specified with -My");
+                       return 0;
+               }
+               if (!arg_count(cmd, major_ARG) && lv->major < 0) {
+                       log_error("Major number must be specified with -My");
+                       return 0;
+               }
+               if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists)
+                       active = 1;
+               if (active && !arg_count(cmd, force_ARG) &&
+                   yes_no_prompt("Logical volume %s will be "
+                                 "deactivated temporarily. "
+                                 "Continue? [y/n]: ", lv->name) == 'n') {
+                       log_error("%s device number not changed.",
+                                 lv->name);
+                       return 0;
+               }
+
+               if (sigint_caught())
+                       return 0;
+
+               log_verbose("Ensuring %s is inactive.", lv->name);
+               if (!deactivate_lv(cmd, lv)) {
+                       log_error("%s: deactivation failed", lv->name);
+                       return 0;
+               }
+               lv->status |= FIXED_MINOR;
+               lv->minor = arg_int_value(cmd, minor_ARG, lv->minor);
+               lv->major = arg_int_value(cmd, major_ARG, lv->major);
+               log_verbose("Setting persistent device number to (%d, %d) "
+                           "for \"%s\"", lv->major, lv->minor, lv->name);
+
+       }
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+       if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+               return_0;
+
+       backup(lv->vg);
+
+       if (active) {
+               log_verbose("Re-activating logical volume \"%s\"", lv->name);
+               if (!activate_lv(cmd, lv)) {
+                       log_error("%s: reactivation failed", lv->name);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv,
+                       int arg)
+{
+       const char *tag;
+       struct arg_value_group_list *current_group;
+
+       dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+               if (!grouped_arg_is_set(current_group->arg_values, arg))
+                       continue;
+
+               if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) {
+                       log_error("Failed to get tag");
+                       return 0;
+               }
+
+               if (!lv_change_tag(lv, tag, arg == addtag_ARG))
+                       return_0;
+       }
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+       /* No need to suspend LV for this change */
+       if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+               return_0;
+
+       backup(lv->vg);
+
+       return 1;
+}
+
+static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
+                          void *handle __attribute__((unused)))
+{
+       int doit = 0, docmds = 0;
+       int dmeventd_mode, archived = 0;
+       struct logical_volume *origin;
+
+       if (!(lv->vg->status & LVM_WRITE) &&
+           (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) ||
+            arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) ||
+            arg_count(cmd, alloc_ARG))) {
+               log_error("Only -a permitted with read-only volume "
+                         "group \"%s\"", lv->vg->name);
+               return EINVALID_CMD_LINE;
+       }
+
+       if (lv_is_origin(lv) &&
+           (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) ||
+            arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) ||
+            arg_count(cmd, alloc_ARG))) {
+               log_error("Can't change logical volume \"%s\" under snapshot",
+                         lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv)) &&
+           arg_count(cmd, available_ARG)) {
+               log_error("Can't change snapshot logical volume \"%s\"",
+                         lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv->status & PVMOVE) {
+               log_error("Unable to change pvmove LV %s", lv->name);
+               if (arg_count(cmd, available_ARG))
+                       log_error("Use 'pvmove --abort' to abandon a pvmove");
+               return ECMD_FAILED;
+       }
+
+       if (lv->status & MIRROR_LOG) {
+               log_error("Unable to change mirror log LV %s directly", lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv->status & MIRROR_IMAGE) {
+               log_error("Unable to change mirror image LV %s directly",
+                         lv->name);
+               return ECMD_FAILED;
+       }
+
+       /* If LV is sparse, activate origin instead */
+       if (arg_count(cmd, available_ARG) && lv_is_cow(lv) &&
+           lv_is_virtual_origin(origin = origin_from_cow(lv)))
+               lv = origin;
+
+       if (!(lv_is_visible(lv)) && !lv_is_virtual_origin(lv)) {
+               log_error("Unable to change internal LV %s directly",
+                         lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (!get_activation_monitoring_mode(cmd, lv->vg, &dmeventd_mode))
+               return ECMD_FAILED;
+
+       init_dmeventd_monitor(dmeventd_mode);
+
+       /*
+        * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified".
+        * If --poll is explicitly provided use it; otherwise polling
+        * should only be started if the LV is not already active. So:
+        * 1) change the activation code to say if the LV was actually activated
+        * 2) make polling of an LV tightly coupled with LV activation
+        *
+        * Do not initiate any polling if --sysinit option is used.
+        */
+       init_background_polling(arg_count(cmd, sysinit_ARG) ? 0 :
+                                               arg_int_value(cmd, poll_ARG,
+                                               DEFAULT_BACKGROUND_POLLING));
+
+       /* access permission change */
+       if (arg_count(cmd, permission_ARG)) {
+               if (!archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               archived = 1;
+               doit += lvchange_permission(cmd, lv);
+               docmds++;
+       }
+
+       /* allocation policy change */
+       if (arg_count(cmd, contiguous_ARG) || arg_count(cmd, alloc_ARG)) {
+               if (!archived && !archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               archived = 1;
+               doit += lvchange_alloc(cmd, lv);
+               docmds++;
+       }
+
+       /* read ahead sector change */
+       if (arg_count(cmd, readahead_ARG)) {
+               if (!archived && !archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               archived = 1;
+               doit += lvchange_readahead(cmd, lv);
+               docmds++;
+       }
+
+       /* persistent device number change */
+       if (arg_count(cmd, persistent_ARG)) {
+               if (!archived && !archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               archived = 1;
+               doit += lvchange_persistent(cmd, lv);
+               docmds++;
+               if (sigint_caught()) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       /* add tag */
+       if (arg_count(cmd, addtag_ARG)) {
+               if (!archived && !archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               archived = 1;
+               doit += lvchange_tag(cmd, lv, addtag_ARG);
+               docmds++;
+       }
+
+       /* del tag */
+       if (arg_count(cmd, deltag_ARG)) {
+               if (!archived && !archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               archived = 1;
+               doit += lvchange_tag(cmd, lv, deltag_ARG);
+               docmds++;
+       }
+
+       if (doit)
+               log_print("Logical volume \"%s\" changed", lv->name);
+
+       if (arg_count(cmd, resync_ARG))
+               if (!lvchange_resync(cmd, lv)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+       /* availability change */
+       if (arg_count(cmd, available_ARG)) {
+               if (!lvchange_availability(cmd, lv)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       if (arg_count(cmd, refresh_ARG))
+               if (!lvchange_refresh(cmd, lv)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+       if (!arg_count(cmd, available_ARG) &&
+           !arg_count(cmd, refresh_ARG) &&
+           arg_count(cmd, monitor_ARG)) {
+               if (!lvchange_monitoring(cmd, lv)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       if (!arg_count(cmd, available_ARG) &&
+           !arg_count(cmd, refresh_ARG) &&
+           arg_count(cmd, poll_ARG)) {
+               if (!lvchange_background_polling(cmd, lv)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       if (doit != docmds) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int lvchange(struct cmd_context *cmd, int argc, char **argv)
+{
+       int update = /* options other than -a, --refresh, --monitor or --poll */
+               arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) ||
+               arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) ||
+               arg_count(cmd, addtag_ARG) || arg_count(cmd, deltag_ARG) ||
+               arg_count(cmd, resync_ARG) || arg_count(cmd, alloc_ARG);
+
+       if (!update &&
+            !arg_count(cmd, available_ARG) && !arg_count(cmd, refresh_ARG) &&
+            !arg_count(cmd, monitor_ARG) && !arg_count(cmd, poll_ARG) &&
+            /* for persistent_ARG */
+           !arg_count(cmd, minor_ARG) && !arg_count(cmd, major_ARG)) {
+               log_error("Need 1 or more of -a, -C, -j, -m, -M, -p, -r, "
+                         "--resync, --refresh, --alloc, --addtag, --deltag, "
+                         "--monitor or --poll");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, available_ARG) && arg_count(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) {
+               log_error("Only -a permitted with --ignorelockingfailure and --sysinit");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (!update)
+               cmd->handles_missing_pvs = 1;
+
+       if (!argc) {
+               log_error("Please give logical volume path(s)");
+               return EINVALID_CMD_LINE;
+       }
+
+       if ((arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) &&
+           !arg_count(cmd, persistent_ARG)) {
+               log_error("--major and --minor require -My");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, minor_ARG) && argc != 1) {
+               log_error("Only give one logical volume when specifying minor");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, contiguous_ARG) && arg_count(cmd, alloc_ARG)) {
+               log_error("Only one of --alloc and --contiguous permitted");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, poll_ARG) && arg_count(cmd, sysinit_ARG)) {
+               log_error("Only one of --poll and --sysinit permitted");
+               return EINVALID_CMD_LINE;
+       }
+
+       return process_each_lv(cmd, argc, argv,
+                              update ? READ_FOR_UPDATE : 0, NULL,
+                              &lvchange_single);
+}
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
new file mode 100644 (file)
index 0000000..7c7826b
--- /dev/null
@@ -0,0 +1,1763 @@
+/*
+ * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "polldaemon.h"
+#include "lv_alloc.h"
+#include "metadata.h"
+
+struct lvconvert_params {
+       int snapshot;
+       int merge;
+       int zero;
+
+       const char *origin;
+       const char *lv_name;
+       const char *lv_split_name;
+       const char *lv_name_full;
+       const char *vg_name;
+       int wait_completion;
+       int need_polling;
+
+       uint32_t chunk_size;
+       uint32_t region_size;
+
+       uint32_t mirrors;
+       sign_t mirrors_sign;
+       uint32_t keep_mimages;
+       uint32_t stripes;
+       uint32_t stripe_size;
+
+       struct segment_type *segtype;
+
+       alloc_policy_t alloc;
+
+       int pv_count;
+       char **pvs;
+       struct dm_list *pvh;
+       struct dm_list *failed_pvs;
+
+       struct logical_volume *lv_to_poll;
+};
+
+static int _lvconvert_name_params(struct lvconvert_params *lp,
+                                 struct cmd_context *cmd,
+                                 int *pargc, char ***pargv)
+{
+       char *ptr;
+       const char *vg_name = NULL;
+
+       if (lp->merge)
+               return 1;
+
+       if (lp->snapshot) {
+               if (!*pargc) {
+                       log_error("Please specify a logical volume to act as "
+                                 "the snapshot origin.");
+                       return 0;
+               }
+
+               lp->origin = *pargv[0];
+               (*pargv)++, (*pargc)--;
+               if (!(lp->vg_name = extract_vgname(cmd, lp->origin))) {
+                       log_error("The origin name should include the "
+                                 "volume group.");
+                       return 0;
+               }
+
+               /* Strip the volume group from the origin */
+               if ((ptr = strrchr(lp->origin, (int) '/')))
+                       lp->origin = ptr + 1;
+       }
+
+       if (!*pargc) {
+               log_error("Please provide logical volume path");
+               return 0;
+       }
+
+       lp->lv_name = lp->lv_name_full = (*pargv)[0];
+       (*pargv)++, (*pargc)--;
+
+       if (strchr(lp->lv_name_full, '/') &&
+           (vg_name = extract_vgname(cmd, lp->lv_name_full)) &&
+           lp->vg_name && strcmp(vg_name, lp->vg_name)) {
+               log_error("Please use a single volume group name "
+                         "(\"%s\" or \"%s\")", vg_name, lp->vg_name);
+               return 0;
+       }
+
+       if (!lp->vg_name)
+               lp->vg_name = vg_name;
+
+       if (!validate_name(lp->vg_name)) {
+               log_error("Please provide a valid volume group name");
+               return 0;
+       }
+
+       if ((ptr = strrchr(lp->lv_name_full, '/')))
+               lp->lv_name = ptr + 1;
+
+       if (!apply_lvname_restrictions(lp->lv_name))
+               return_0;
+
+       if (*pargc && lp->snapshot) {
+               log_error("Too many arguments provided for snapshots");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
+                       int argc, char **argv)
+{
+       int region_size;
+       int pagesize = lvm_getpagesize();
+
+       memset(lp, 0, sizeof(*lp));
+
+       if ((arg_count(cmd, snapshot_ARG) || arg_count(cmd, merge_ARG)) &&
+           (arg_count(cmd, mirrorlog_ARG) || arg_count(cmd, mirrors_ARG) ||
+            arg_count(cmd, repair_ARG))) {
+               log_error("--snapshot or --merge argument cannot be mixed "
+                         "with --mirrors, --repair or --log");
+               return 0;
+       }
+
+       if (!arg_count(cmd, background_ARG))
+               lp->wait_completion = 1;
+
+       if (arg_count(cmd, snapshot_ARG))
+               lp->snapshot = 1;
+
+       if (arg_count(cmd, snapshot_ARG) && arg_count(cmd, merge_ARG)) {
+               log_error("--snapshot and --merge are mutually exclusive");
+               return 0;
+       }
+
+       if (arg_count(cmd, splitmirrors_ARG) && arg_count(cmd, mirrors_ARG)) {
+               log_error("--mirrors and --splitmirrors are "
+                         "mutually exclusive");
+               return 0;
+       }
+
+       /*
+        * The '--splitmirrors n' argument is equivalent to '--mirrors -n'
+        * (note the minus sign), except that it signifies the additional
+        * intent to keep the mimage that is detached, rather than
+        * discarding it.
+        */
+       if (arg_count(cmd, splitmirrors_ARG)) {
+               if (!arg_count(cmd, name_ARG)) {
+                       log_error("Please name the new logical volume using '--name'");
+                       return 0;
+               }
+
+               lp->lv_split_name = arg_value(cmd, name_ARG);
+               if (!apply_lvname_restrictions(lp->lv_split_name))
+                       return_0;
+
+               lp->keep_mimages = 1;
+               if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+                       log_error("Argument to --splitmirrors"
+                                 " cannot be negative");
+                       return 0;
+               }
+               lp->mirrors = arg_uint_value(cmd, splitmirrors_ARG, 0);
+               lp->mirrors_sign = SIGN_MINUS;
+       } else if (arg_count(cmd, name_ARG)) {
+               log_error("The 'name' argument is only valid"
+                         " with --splitmirrors");
+               return 0;
+       }
+
+       if (arg_count(cmd, merge_ARG))
+               lp->merge = 1;
+
+       if (arg_count(cmd, mirrors_ARG)) {
+               /*
+                * --splitmirrors has been chosen as the mechanism for
+                * specifying the intent of detaching and keeping a mimage
+                * versus an additional qualifying argument being added here.
+                */
+               lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0);
+               lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0);
+       }
+
+       lp->alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
+
+       /* There are three types of lvconvert. */
+       if (lp->merge) {        /* Snapshot merge */
+               if (arg_count(cmd, regionsize_ARG) || arg_count(cmd, chunksize_ARG) ||
+                   arg_count(cmd, zero_ARG) || arg_count(cmd, regionsize_ARG) ||
+                   arg_count(cmd, stripes_long_ARG) || arg_count(cmd, stripesize_ARG)) {
+                       log_error("Only --background and --interval are valid "
+                                 "arguments for snapshot merge");
+                       return 0;
+               }
+
+               if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
+                       return_0;
+
+       } else if (lp->snapshot) {      /* Snapshot creation from pre-existing cow */
+               if (arg_count(cmd, regionsize_ARG)) {
+                       log_error("--regionsize is only available with mirrors");
+                       return 0;
+               }
+
+               if (arg_count(cmd, stripesize_ARG) || arg_count(cmd, stripes_long_ARG)) {
+                       log_error("--stripes and --stripesize are only available with striped mirrors");
+                       return 0;
+               }
+
+               if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative chunk size is invalid");
+                       return 0;
+               }
+               lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8);
+               if (lp->chunk_size < 8 || lp->chunk_size > 1024 ||
+                   (lp->chunk_size & (lp->chunk_size - 1))) {
+                       log_error("Chunk size must be a power of 2 in the "
+                                 "range 4K to 512K");
+                       return 0;
+               }
+               log_verbose("Setting chunksize to %d sectors.", lp->chunk_size);
+
+               if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
+                       return_0;
+
+               lp->zero = strcmp(arg_str_value(cmd, zero_ARG,
+                                               (lp->segtype->flags &
+                                                SEG_CANNOT_BE_ZEROED) ?
+                                               "n" : "y"), "n");
+
+       } else {        /* Mirrors */
+               if (arg_count(cmd, chunksize_ARG)) {
+                       log_error("--chunksize is only available with "
+                                 "snapshots");
+                       return 0;
+               }
+
+               if (arg_count(cmd, zero_ARG)) {
+                       log_error("--zero is only available with snapshots");
+                       return 0;
+               }
+
+               /*
+                * --regionsize is only valid if converting an LV into a mirror.
+                * Checked when we know the state of the LV being converted.
+                */
+
+               if (arg_count(cmd, regionsize_ARG)) {
+                       if (arg_sign_value(cmd, regionsize_ARG, 0) ==
+                                   SIGN_MINUS) {
+                               log_error("Negative regionsize is invalid");
+                               return 0;
+                       }
+                       lp->region_size = arg_uint_value(cmd, regionsize_ARG, 0);
+               } else {
+                       region_size = 2 * find_config_tree_int(cmd,
+                                               "activation/mirror_region_size",
+                                               DEFAULT_MIRROR_REGION_SIZE);
+                       if (region_size < 0) {
+                               log_error("Negative regionsize in "
+                                         "configuration file is invalid");
+                               return 0;
+                       }
+                       lp->region_size = region_size;
+               }
+
+               if (lp->region_size % (pagesize >> SECTOR_SHIFT)) {
+                       log_error("Region size (%" PRIu32 ") must be "
+                                 "a multiple of machine memory "
+                                 "page size (%d)",
+                                 lp->region_size, pagesize >> SECTOR_SHIFT);
+                       return 0;
+               }
+
+               if (lp->region_size & (lp->region_size - 1)) {
+                       log_error("Region size (%" PRIu32
+                                 ") must be a power of 2", lp->region_size);
+                       return 0;
+               }
+
+               if (!lp->region_size) {
+                       log_error("Non-zero region size must be supplied.");
+                       return 0;
+               }
+
+               /* Default is never striped, regardless of existing LV configuration. */
+               if (!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size)) {
+                       stack;
+                       return 0;
+               }
+
+               if (!(lp->segtype = get_segtype_from_string(cmd, "mirror")))
+                       return_0;
+       }
+
+       if (activation() && lp->segtype->ops->target_present &&
+           !lp->segtype->ops->target_present(cmd, NULL, NULL)) {
+               log_error("%s: Required device-mapper target(s) not "
+                         "detected in your kernel", lp->segtype->name);
+               return 0;
+       }
+
+       if (!_lvconvert_name_params(lp, cmd, &argc, &argv))
+               return_0;
+
+       lp->pv_count = argc;
+       lp->pvs = argv;
+       lp->failed_pvs = NULL;
+
+       return 1;
+}
+
+static struct volume_group *_get_lvconvert_vg(struct cmd_context *cmd,
+                                             const char *name,
+                                             const char *uuid __attribute__((unused)))
+{
+       dev_close_all();
+
+       if (name && !strchr(name, '/'))
+               return vg_read_for_update(cmd, name, NULL, 0);
+
+       /* 'name' is the full LV name; must extract_vgname() */
+       return vg_read_for_update(cmd, extract_vgname(cmd, name),
+                                 NULL, 0);
+}
+
+static struct logical_volume *_get_lvconvert_lv(struct cmd_context *cmd __attribute__((unused)),
+                                               struct volume_group *vg,
+                                               const char *name,
+                                               const char *uuid,
+                                               uint32_t lv_type __attribute__((unused)))
+{
+       struct logical_volume *lv = find_lv(vg, name);
+
+       if (!lv || (uuid && strcmp(uuid, (char *)&lv->lvid)))
+               return NULL;
+
+       return lv;
+}
+
+static int _finish_lvconvert_mirror(struct cmd_context *cmd,
+                                   struct volume_group *vg,
+                                   struct logical_volume *lv,
+                                   struct dm_list *lvs_changed __attribute__((unused)))
+{
+       int r = 0;
+
+       if (!(lv->status & CONVERTING))
+               return 1;
+
+       if (!collapse_mirrored_lv(lv)) {
+               log_error("Failed to remove temporary sync layer.");
+               return 0;
+       }
+
+       lv->status &= ~CONVERTING;
+
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+       if (!vg_write(vg))
+               return_0;
+
+       if (!suspend_lv(cmd, lv)) {
+               log_error("Failed to lock %s", lv->name);
+               vg_revert(vg);
+               goto out;
+       }
+
+       if (!vg_commit(vg)) {
+               resume_lv(cmd, lv);
+               goto_out;
+       }
+
+       log_very_verbose("Updating \"%s\" in kernel", lv->name);
+
+       if (!resume_lv(cmd, lv)) {
+               log_error("Problem reactivating %s", lv->name);
+               goto out;
+       }
+
+       r = 1;
+       log_print("Logical volume %s converted.", lv->name);
+out:
+       backup(vg);
+       return r;
+}
+
+static int _finish_lvconvert_merge(struct cmd_context *cmd,
+                                  struct volume_group *vg,
+                                  struct logical_volume *lv,
+                                  struct dm_list *lvs_changed __attribute__((unused)))
+{
+       struct lv_segment *snap_seg = find_merging_cow(lv);
+       if (!snap_seg) {
+               log_error("Logical volume %s has no merging snapshot.", lv->name);
+               return 0;
+       }
+
+       log_print("Merge of snapshot into logical volume %s has finished.", lv->name);
+       if (!lv_remove_single(cmd, snap_seg->cow, DONT_PROMPT)) {
+               log_error("Could not remove snapshot %s merged into %s.",
+                         snap_seg->cow->name, lv->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static progress_t _poll_merge_progress(struct cmd_context *cmd,
+                                      struct logical_volume *lv,
+                                      const char *name __attribute__((unused)),
+                                      struct daemon_parms *parms)
+{
+       percent_t percent = PERCENT_0;
+
+       if (!lv_snapshot_percent(lv, &percent)) {
+               log_error("%s: Failed query for merging percentage. Aborting merge.", lv->name);
+               return PROGRESS_CHECK_FAILED;
+       } else if (percent == PERCENT_INVALID) {
+               log_error("%s: Merging snapshot invalidated. Aborting merge.", lv->name);
+               return PROGRESS_CHECK_FAILED;
+       }
+
+       if (parms->progress_display)
+               log_print("%s: %s: %.1f%%", lv->name, parms->progress_title,
+                         percent_to_float(percent));
+       else
+               log_verbose("%s: %s: %.1f%%", lv->name, parms->progress_title,
+                           percent_to_float(percent));
+
+       if (percent == PERCENT_0)
+               return PROGRESS_FINISHED_ALL;
+
+       return PROGRESS_UNFINISHED;
+}
+
+static struct poll_functions _lvconvert_mirror_fns = {
+       .get_copy_vg = _get_lvconvert_vg,
+       .get_copy_lv = _get_lvconvert_lv,
+       .poll_progress = poll_mirror_progress,
+       .finish_copy = _finish_lvconvert_mirror,
+};
+
+static struct poll_functions _lvconvert_merge_fns = {
+       .get_copy_vg = _get_lvconvert_vg,
+       .get_copy_lv = _get_lvconvert_lv,
+       .poll_progress = _poll_merge_progress,
+       .finish_copy = _finish_lvconvert_merge,
+};
+
+int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv,
+                  unsigned background)
+{
+       /*
+        * FIXME allocate an "object key" structure with split
+        * out members (vg_name, lv_name, uuid, etc) and pass that
+        * around the lvconvert and polldaemon code
+        * - will avoid needless work, e.g. extract_vgname()
+        * - unfortunately there are enough overloaded "name" dragons in
+        *   the polldaemon, lvconvert, pvmove code that a comprehensive
+        *   audit/rework is needed
+        */
+       int len = strlen(lv->vg->name) + strlen(lv->name) + 2;
+       char *uuid = alloca(sizeof(lv->lvid));
+       char *lv_full_name = alloca(len);
+
+       if (!uuid || !lv_full_name)
+               return_0;
+
+       if (!dm_snprintf(lv_full_name, len, "%s/%s", lv->vg->name, lv->name))
+               return_0;
+
+       memcpy(uuid, &lv->lvid, sizeof(lv->lvid));
+
+       if (!lv_is_merging_origin(lv))
+               return poll_daemon(cmd, lv_full_name, uuid, background, 0,
+                                  &_lvconvert_mirror_fns, "Converted");
+       else
+               return poll_daemon(cmd, lv_full_name, uuid, background, 0,
+                                  &_lvconvert_merge_fns, "Merged");
+}
+
+static int _insert_lvconvert_layer(struct cmd_context *cmd,
+                                  struct logical_volume *lv)
+{
+       char *format, *layer_name;
+       size_t len;
+       int i;
+
+       /*
+        * We would like to give the same number for this layer
+        * and the newly added mimage.
+        * However, LV name of newly added mimage is determined *after*
+        * the LV name of this layer is determined.
+        *
+        * So, use generate_lv_name() to generate mimage name first
+        * and take the number from it.
+        */
+
+       len = strlen(lv->name) + 32;
+       if (!(format = alloca(len)) ||
+           !(layer_name = alloca(len)) ||
+           dm_snprintf(format, len, "%s_mimage_%%d", lv->name) < 0) {
+               log_error("lvconvert: layer name allocation failed.");
+               return 0;
+       }
+
+       if (!generate_lv_name(lv->vg, format, layer_name, len) ||
+           sscanf(layer_name, format, &i) != 1) {
+               log_error("lvconvert: layer name generation failed.");
+               return 0;
+       }
+
+       if (dm_snprintf(layer_name, len, MIRROR_SYNC_LAYER "_%d", i) < 0) {
+               log_error("layer name allocation failed.");
+               return 0;
+       }
+
+       if (!insert_layer_for_lv(cmd, lv, 0, layer_name)) {
+               log_error("Failed to insert resync layer");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _area_missing(struct lv_segment *lvseg, int s)
+{
+       if (seg_type(lvseg, s) == AREA_LV) {
+               if (seg_lv(lvseg, s)->status & PARTIAL_LV)
+                       return 1;
+       } else if ((seg_type(lvseg, s) == AREA_PV) &&
+                  (is_missing_pv(seg_pv(lvseg, s))))
+               return 1;
+
+       return 0;
+}
+
+/* FIXME we want to handle mirror stacks here... */
+static int _failed_mirrors_count(struct logical_volume *lv)
+{
+       struct lv_segment *lvseg;
+       int ret = 0;
+       int s;
+
+       dm_list_iterate_items(lvseg, &lv->segments) {
+               if (!seg_is_mirrored(lvseg))
+                       return -1;
+               for (s = 0; s < lvseg->area_count; s++)
+                       if (_area_missing(lvseg, s))
+                               ret++;
+       }
+
+       return ret;
+}
+
+static struct dm_list *_failed_pv_list(struct volume_group *vg)
+{
+       struct dm_list *failed_pvs;
+       struct pv_list *pvl, *new_pvl;
+
+       if (!(failed_pvs = dm_pool_alloc(vg->vgmem, sizeof(*failed_pvs)))) {
+               log_error("Allocation of list of failed_pvs failed.");
+               return_NULL;
+       }
+
+       dm_list_init(failed_pvs);
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (!is_missing_pv(pvl->pv))
+                       continue;
+
+               /* 
+                * Finally, --repair will remove empty PVs.
+                * But we only want remove these which are output of repair,
+                * Do not count these which are already empty here.
+                * FIXME: code should traverse PV in LV not in whole VG.
+                * FIXME: layer violation? should it depend on vgreduce --removemising?
+                */
+               if (pvl->pv->pe_alloc_count == 0)
+                       continue;
+
+               if (!(new_pvl = dm_pool_alloc(vg->vgmem, sizeof(*new_pvl)))) {
+                       log_error("Allocation of failed_pvs list entry failed.");
+                       return_NULL;
+               }
+               new_pvl->pv = pvl->pv;
+               dm_list_add(failed_pvs, &new_pvl->list);
+       }
+
+       return failed_pvs;
+}
+
+static int _is_partial_lv(struct logical_volume *lv,
+                         void *baton __attribute__((unused)))
+{
+       return lv->status & PARTIAL_LV;
+}
+
+/*
+ * Walk down the stacked mirror LV to the original mirror LV.
+ */
+static struct logical_volume *_original_lv(struct logical_volume *lv)
+{
+       struct logical_volume *next_lv = lv, *tmp_lv;
+
+       while ((tmp_lv = find_temporary_mirror(next_lv)))
+               next_lv = tmp_lv;
+
+       return next_lv;
+}
+
+static void _lvconvert_mirrors_repair_ask(struct cmd_context *cmd,
+                                         int failed_log, int failed_mirrors,
+                                         int *replace_log, int *replace_mirrors)
+{
+       const char *leg_policy = NULL, *log_policy = NULL;
+
+       int force = arg_count(cmd, force_ARG);
+       int yes = arg_count(cmd, yes_ARG);
+
+       *replace_log = *replace_mirrors = 1;
+
+       if (arg_count(cmd, use_policies_ARG)) {
+               leg_policy = find_config_tree_str(cmd,
+                                       "activation/mirror_image_fault_policy", NULL);
+               if (!leg_policy)
+                       leg_policy = find_config_tree_str(cmd,
+                                       "activation/mirror_device_fault_policy",
+                                       DEFAULT_MIRROR_DEVICE_FAULT_POLICY);
+               log_policy = find_config_tree_str(cmd,
+                                       "activation/mirror_log_fault_policy",
+                                       DEFAULT_MIRROR_LOG_FAULT_POLICY);
+               *replace_mirrors = strcmp(leg_policy, "remove");
+               *replace_log = strcmp(log_policy, "remove");
+               return;
+       }
+
+       if (yes)
+               return;
+
+       if (force != PROMPT) {
+               *replace_log = *replace_mirrors = 0;
+               return;
+       }
+
+       if (failed_log &&
+           yes_no_prompt("Attempt to replace failed mirror log? [y/n]: ") == 'n') {
+               *replace_log = 0;
+       }
+
+       if (failed_mirrors &&
+           yes_no_prompt("Attempt to replace failed mirror images "
+                         "(requires full device resync)? [y/n]: ") == 'n') {
+               *replace_mirrors = 0;
+       }
+}
+
+/*
+ * _get_log_count
+ * @lv: the mirror LV
+ *
+ * Get the number of on-disk copies of the log.
+ *  0  = 'core'
+ *  1  = 'disk'
+ *  2+ = 'mirrored'
+ */
+static int _get_log_count(struct logical_volume *lv)
+{
+       struct logical_volume *log_lv;
+
+       log_lv = first_seg(_original_lv(lv))->log_lv;
+       if (!log_lv)
+               return 0;
+
+       return lv_mirror_count(log_lv);
+}
+
+static int _lv_update_mirrored_log(struct logical_volume *lv,
+                                  struct dm_list *operable_pvs,
+                                  int log_count)
+{
+       int old_log_count;
+       struct logical_volume *log_lv;
+
+       /*
+        * When log_count is 0, mirrored log doesn't need to be
+        * updated here but it will be removed later.
+        */
+       if (!log_count)
+               return 1;
+
+       log_lv = first_seg(_original_lv(lv))->log_lv;
+       if (!log_lv || !(log_lv->status & MIRRORED))
+               return 1;
+
+       old_log_count = _get_log_count(lv);
+       if (old_log_count == log_count)
+               return 1;
+
+       /* Reducing redundancy of the log */
+       return remove_mirror_images(log_lv, log_count,
+                                   is_mirror_image_removable,
+                                   operable_pvs, 0U);
+}
+
+static int _lv_update_log_type(struct cmd_context *cmd,
+                              struct lvconvert_params *lp,
+                              struct logical_volume *lv,
+                              struct dm_list *operable_pvs,
+                              int log_count)
+{
+       uint32_t region_size;
+       int old_log_count;
+       struct logical_volume *original_lv;
+       struct logical_volume *log_lv;
+
+       old_log_count = _get_log_count(lv);
+       if (old_log_count == log_count)
+               return 1;
+
+       original_lv = _original_lv(lv);
+       region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+                                                 lv->le_count,
+                                                 lp->region_size);
+
+       /* Add a log where there is none */
+       if (!old_log_count) {
+               if (!add_mirror_log(cmd, original_lv, log_count,
+                                   region_size, operable_pvs, lp->alloc))
+                       return_0;
+               return 1;
+       }
+
+       /* Remove an existing log completely */
+       if (!log_count) {
+               if (!remove_mirror_log(cmd, original_lv, operable_pvs,
+                                      arg_count(cmd, yes_ARG) ||
+                                      arg_count(cmd, force_ARG)))
+                       return_0;
+               return 1;
+       }
+
+       log_lv = first_seg(original_lv)->log_lv;
+
+       /* Adding redundancy to the log */
+       if (old_log_count < log_count) {
+               log_error("Adding log redundancy not supported yet.");
+               log_error("Try converting the log to 'core' first.");
+               return_0;
+       }
+
+       /* Reducing redundancy of the log */
+       return remove_mirror_images(log_lv, log_count, is_mirror_image_removable, operable_pvs, 1U);
+}
+
+/*
+ * Reomove missing and empty PVs from VG, if are also in provided list
+ */
+static void _remove_missing_empty_pv(struct volume_group *vg, struct dm_list *remove_pvs)
+{
+       struct pv_list *pvl, *pvl_vg, *pvlt;
+       int removed = 0;
+
+       if (!remove_pvs)
+               return;
+
+       dm_list_iterate_items(pvl, remove_pvs) {
+               dm_list_iterate_items_safe(pvl_vg, pvlt, &vg->pvs) {
+                       if (!id_equal(&pvl->pv->id, &pvl_vg->pv->id) ||
+                           !is_missing_pv(pvl_vg->pv) ||
+                           pvl_vg->pv->pe_alloc_count != 0)
+                               continue;
+
+                       /* FIXME: duplication of vgreduce code, move this to library */
+                       vg->free_count -= pvl_vg->pv->pe_count;
+                       vg->extent_count -= pvl_vg->pv->pe_count;
+                       del_pvl_from_vgs(vg, pvl_vg);
+
+                       removed++;
+               }
+       }
+
+       if (removed) {
+               if (!vg_write(vg) || !vg_commit(vg)) {
+                       stack;
+                       return;
+               }
+               log_warn("%d missing and now unallocated Physical Volumes removed from VG.", removed);
+       }
+}
+
+/*
+ * _lvconvert_mirrors_parse_params
+ *
+ * This function performs the following:
+ *  1) Gets the old values of mimage and log counts
+ *  2) Parses the CLI args to find the new desired values
+ *  3) Adjusts 'lp->mirrors' to the appropriate absolute value.
+ *     (Remember, 'lp->mirrors' is specified in terms of the number of "copies"
+ *      vs. the number of mimages.  It can also be a relative value.)
+ *  4) Sets 'lp->need_polling' if collapsing
+ *  5) Validates other mirror params
+ *
+ * Returns: 1 on success, 0 on error
+ */
+static int _lvconvert_mirrors_parse_params(struct cmd_context *cmd,
+                                          struct logical_volume *lv,
+                                          struct lvconvert_params *lp,
+                                          uint32_t *old_mimage_count,
+                                          uint32_t *old_log_count,
+                                          uint32_t *new_mimage_count,
+                                          uint32_t *new_log_count)
+{
+       int repair = arg_count(cmd, repair_ARG);
+       const char *mirrorlog;
+       *old_mimage_count = lv_mirror_count(lv);
+       *old_log_count = _get_log_count(lv);
+
+       /*
+        * Collapsing a stack of mirrors:
+        *
+        * If called with no argument, try collapsing the resync layers
+        */
+       if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) &&
+           !arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_ARG) &&
+           !arg_count(cmd, splitmirrors_ARG) && !repair) {
+               *new_mimage_count = *old_mimage_count;
+               *new_log_count = *old_log_count;
+
+               if (find_temporary_mirror(lv) || (lv->status & CONVERTING))
+                       lp->need_polling = 1;
+               return 1;
+       }
+
+       if ((arg_count(cmd, mirrors_ARG) && repair) ||
+           (arg_count(cmd, mirrorlog_ARG) && repair) ||
+           (arg_count(cmd, corelog_ARG) && repair)) {
+               log_error("--repair cannot be used with --mirrors, --mirrorlog,"
+                         " or --corelog");
+               return 0;
+       }
+
+       if (arg_count(cmd, mirrorlog_ARG) && arg_count(cmd, corelog_ARG)) {
+               log_error("--mirrorlog and --corelog are incompatible");
+               return 0;
+       }
+
+       /*
+        * Adjusting mimage count?
+        */
+       if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, splitmirrors_ARG))
+               lp->mirrors = *old_mimage_count;
+       else if (lp->mirrors_sign == SIGN_PLUS)
+               lp->mirrors = *old_mimage_count + lp->mirrors;
+       else if (lp->mirrors_sign == SIGN_MINUS)
+               lp->mirrors = (*old_mimage_count > lp->mirrors) ?
+                       *old_mimage_count - lp->mirrors: 0;
+       else
+               lp->mirrors += 1;
+
+       *new_mimage_count = lp->mirrors;
+
+       /* Too many mimages? */
+       if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) {
+               log_error("Only up to %d images in mirror supported currently.",
+                         DEFAULT_MIRROR_MAX_IMAGES);
+               return 0;
+       }
+
+       /* Did the user try to subtract more legs than available? */
+       if (lp->mirrors < 1) {
+               log_error("Unable to reduce images by specified amount - only %d in %s",
+                         *old_mimage_count, lv->name);
+               return 0;
+       }
+
+       /*
+        * FIXME: It would be nice to say what we are adjusting to, but
+        * I really don't know whether to specify the # of copies or mimages.
+        */
+       if (*old_mimage_count != *new_mimage_count)
+               log_verbose("Adjusting mirror image count of %s", lv->name);
+
+       /*
+        * Adjust log type
+        *
+        * If we are converting from a mirror to another mirror or simply
+        * changing the log type, we start by assuming they want the log
+        * type the same and then parse the given args.  OTOH, If we are
+        * converting from linear to mirror, then we start from the default
+        * position that the user would like a 'disk' log.
+        */
+       *new_log_count = (*old_mimage_count > 1) ? *old_log_count : 1;
+       if (!arg_count(cmd, corelog_ARG) && !arg_count(cmd, mirrorlog_ARG))
+               return 1;
+
+       if (arg_count(cmd, corelog_ARG))
+               *new_log_count = 0;
+
+       mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
+                                 !*new_log_count ? "core" : DEFAULT_MIRRORLOG);
+
+       if (!strcmp("mirrored", mirrorlog))
+               *new_log_count = 2;
+       else if (!strcmp("disk", mirrorlog))
+               *new_log_count = 1;
+       else if (!strcmp("core", mirrorlog))
+               *new_log_count = 0;
+       else {
+               log_error("Unknown mirrorlog type: %s", mirrorlog);
+               return 0;
+       }
+
+       /*
+        * No mirrored logs for cluster mirrors until
+        * log daemon is multi-threaded.
+        */
+       if ((*new_log_count == 2) && vg_is_clustered(lv->vg)) {
+               log_error("Log type, \"mirrored\", is unavailable to cluster mirrors");
+               return 0;
+       }
+
+       log_verbose("Setting logging type to %s", mirrorlog);
+
+       /*
+        * Region size must not change on existing mirrors
+        */
+       if (arg_count(cmd, regionsize_ARG) && (lv->status & MIRRORED) &&
+           (lp->region_size != first_seg(lv)->region_size)) {
+               log_error("Mirror log region size cannot be changed on "
+                         "an existing mirror.");
+               return 0;
+       }
+
+       /*
+        * For the most part, we cannot handle multi-segment mirrors. Bail out
+        * early if we have encountered one.
+        */
+       if ((lv->status & MIRRORED) && dm_list_size(&lv->segments) != 1) {
+               log_error("Logical volume %s has multiple "
+                         "mirror segments.", lv->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _reload_lv(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+       if (!vg_write(lv->vg))
+               return_0;
+
+       if (!suspend_lv(cmd, lv)) {
+               log_error("Failed to lock %s", lv->name);
+               vg_revert(lv->vg);
+               return 0;
+       }
+
+       if (!vg_commit(lv->vg)) {
+               if (!resume_lv(cmd, lv))
+                       stack;
+               return_0;
+       }
+
+       log_very_verbose("Updating \"%s\" in kernel", lv->name);
+
+       if (!resume_lv(cmd, lv)) {
+               log_error("Problem reactivating %s", lv->name);
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * _lvconvert_mirrors_aux
+ *
+ * Add/remove mirror images and adjust log type.  'operable_pvs'
+ * are the set of PVs open to removal or allocation - depending
+ * on the operation being performed.
+ */
+static int _lvconvert_mirrors_aux(struct cmd_context *cmd,
+                                 struct logical_volume *lv,
+                                 struct lvconvert_params *lp,
+                                 struct dm_list *operable_pvs,
+                                 uint32_t new_mimage_count,
+                                 uint32_t new_log_count)
+{
+       uint32_t region_size;
+       struct lv_segment *seg;
+       struct logical_volume *layer_lv;
+       uint32_t old_mimage_count = lv_mirror_count(lv);
+       uint32_t old_log_count = _get_log_count(lv);
+
+       if ((lp->mirrors == 1) && !(lv->status & MIRRORED)) {
+               log_error("Logical volume %s is already not mirrored.",
+                         lv->name);
+               return 1;
+       }
+
+       region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+                                                 lv->le_count,
+                                                 lp->region_size);
+
+       if (!operable_pvs)
+               operable_pvs = lp->pvh;
+
+       seg = first_seg(lv);
+
+       /*
+        * Up-convert from linear to mirror
+        */
+       if (!(lv->status & MIRRORED)) {
+               /* FIXME Share code with lvcreate */
+
+               /*
+                * FIXME should we give not only lp->pvh, but also all PVs
+                * currently taken by the mirror? Would make more sense from
+                * user perspective.
+                */
+               if (!lv_add_mirrors(cmd, lv, new_mimage_count - 1, lp->stripes,
+                                   lp->stripe_size, region_size, new_log_count, operable_pvs,
+                                   lp->alloc, MIRROR_BY_LV)) {
+                       stack;
+                       return 0;
+               }
+               if (lp->wait_completion)
+                       lp->need_polling = 1;
+
+               goto out;
+       }
+
+       /*
+        * Up-convert m-way mirror to n-way mirror
+        */
+       if (new_mimage_count > old_mimage_count) {
+               if (lv->status & MIRROR_NOTSYNCED) {
+                       log_error("Can't add mirror to out-of-sync mirrored "
+                                 "LV: use lvchange --resync first.");
+                       return 0;
+               }
+
+               /*
+                * We allow snapshots of mirrors, but for now, we
+                * do not allow up converting mirrors that are under
+                * snapshots.  The layering logic is somewhat complex,
+                * and preliminary test show that the conversion can't
+                * seem to get the correct %'age of completion.
+                */
+               if (lv_is_origin(lv)) {
+                       log_error("Can't add additional mirror images to "
+                                 "mirrors that are under snapshots");
+                       return 0;
+               }
+
+               /*
+                * Is there already a convert in progress?  We do not
+                * currently allow more than one.
+                */
+               if (find_temporary_mirror(lv) || (lv->status & CONVERTING)) {
+                       log_error("%s is already being converted.  Unable to start another conversion.",
+                                 lv->name);
+                       return 0;
+               }
+
+               /*
+                * Is there already a convert in progress?  We do not
+                * currently allow more than one.
+                */
+               if (find_temporary_mirror(lv) || (lv->status & CONVERTING)) {
+                       log_error("%s is already being converted.  Unable to start another conversion.",
+                                 lv->name);
+                       return 0;
+               }
+
+               /*
+                * Log addition/removal should be done before the layer
+                * insertion to make the end result consistent with
+                * linear-to-mirror conversion.
+                */
+               if (!_lv_update_log_type(cmd, lp, lv,
+                                        operable_pvs, new_log_count)) {
+                       stack;
+                       return 0;
+               }
+
+               /* Insert a temporary layer for syncing,
+                * only if the original lv is using disk log. */
+               if (seg->log_lv && !_insert_lvconvert_layer(cmd, lv)) {
+                       log_error("Failed to insert resync layer");
+                       return 0;
+               }
+
+               /* FIXME: can't have multiple mlogs. force corelog. */
+               if (!lv_add_mirrors(cmd, lv,
+                                   new_mimage_count - old_mimage_count, lp->stripes, lp->stripe_size,
+                                   region_size, 0U, operable_pvs, lp->alloc,
+                                   MIRROR_BY_LV)) {
+                       layer_lv = seg_lv(first_seg(lv), 0);
+                       if (!remove_layer_from_lv(lv, layer_lv) ||
+                           !deactivate_lv(cmd, layer_lv) ||
+                           !lv_remove(layer_lv) || !vg_write(lv->vg) ||
+                           !vg_commit(lv->vg)) {
+                               log_error("ABORTING: Failed to remove "
+                                         "temporary mirror layer %s.",
+                                         layer_lv->name);
+                               log_error("Manual cleanup with vgcfgrestore "
+                                         "and dmsetup may be required.");
+                               return 0;
+                       }
+                       stack;
+                       return 0;
+               }
+               if (seg->log_lv)
+                       lv->status |= CONVERTING;
+               lp->need_polling = 1;
+
+               goto out_skip_log_convert;
+       }
+
+       /*
+        * Down-convert (reduce # of mimages).
+        */
+       if (new_mimage_count < old_mimage_count) {
+               uint32_t nmc = old_mimage_count - new_mimage_count;
+               uint32_t nlc = (!new_log_count || lp->mirrors == 1) ? 1U : 0U;
+
+               /* FIXME: Why did nlc used to be calculated that way? */
+
+               /* Reduce number of mirrors */
+               if (lp->keep_mimages) {
+                       if (!lv_split_mirror_images(lv, lp->lv_split_name,
+                                                   nmc, operable_pvs))
+                               return 0;
+               } else if (!lv_remove_mirrors(cmd, lv, nmc, nlc,
+                                             is_mirror_image_removable, operable_pvs, 0))
+                       return_0;
+
+               goto out; /* Just in case someone puts code between */
+       }
+
+out:
+       /*
+        * Converting the log type
+        */
+       if ((lv->status & MIRRORED) && (old_log_count != new_log_count)) {
+               if (!_lv_update_log_type(cmd, lp, lv,
+                                        operable_pvs, new_log_count)) {
+                       stack;
+                       return 0;
+               }
+       }
+
+out_skip_log_convert:
+
+       if (!_reload_lv(cmd, lv))
+               return 0;
+
+       return 1;
+}
+
+/*
+ * _lvconvert_mirrors_repair
+ *
+ * This function operates in two phases.  First, all of the bad
+ * devices are removed from the mirror.  Then, if desired by the
+ * user, the devices are replaced.
+ *
+ * 'old_mimage_count' and 'old_log_count' are there so we know
+ * what to convert to after the removal of devices.
+ */
+static int _lvconvert_mirrors_repair(struct cmd_context *cmd,
+                                    struct logical_volume *lv,
+                                    struct lvconvert_params *lp,
+                                    uint32_t old_mimage_count,
+                                    uint32_t old_log_count)
+{
+       int failed_log = 0;
+       int failed_mirrors = 0;
+       int replace_log = 0;
+       int replace_mirrors = 0;
+       uint32_t new_log_count, log_count;
+       struct logical_volume *log_lv;
+
+       cmd->handles_missing_pvs = 1;
+       cmd->partial_activation = 1;
+       lp->need_polling = 0;
+
+       lv_check_transient(lv); /* TODO check this in lib for all commands? */
+
+       if (!(lv->status & PARTIAL_LV)) {
+               log_error("%s is consistent. Nothing to repair.", lv->name);
+               return 1;
+       }
+
+       /*
+        * Count the failed mimages - negative if 'lv' is not a mirror
+        */
+       if ((failed_mirrors = _failed_mirrors_count(lv)) < 0)
+               return_0;
+
+       lp->mirrors = old_mimage_count - failed_mirrors;
+
+       if (lp->mirrors != old_mimage_count)
+               log_error("Mirror status: %d of %d images failed.",
+                         failed_mirrors, old_mimage_count);
+
+       /*
+        * Count the failed log devices
+        */
+       new_log_count = old_log_count;
+       log_lv = first_seg(lv)->log_lv;
+       if (log_lv) {
+               new_log_count = lv_mirror_count(log_lv);
+               if (log_lv->status & PARTIAL_LV) {
+                       failed_log = 1;
+                       if (log_lv->status & MIRRORED)
+                               new_log_count -= _failed_mirrors_count(log_lv);
+                       else
+                               new_log_count = 0;
+               }
+       }
+       if (old_log_count != new_log_count)
+               log_error("Mirror log status: %d of %d images failed%s",
+                         old_log_count - new_log_count, old_log_count,
+                         (!new_log_count) ? " - switching to core" : "");
+
+       /*
+        * Find out our policies
+        */
+       _lvconvert_mirrors_repair_ask(cmd, failed_log, failed_mirrors,
+                                     &replace_log, &replace_mirrors);
+
+       /*
+        * First phase - remove faulty devices
+        */
+       if (!(lp->failed_pvs = _failed_pv_list(lv->vg)))
+               return_0;
+
+       log_count = new_log_count;
+
+       /*
+        * We must adjust the log first, or the entire mirror
+        * will get stuck during a suspend.
+        */
+       if (!_lv_update_mirrored_log(lv, lp->failed_pvs, log_count))
+               return 0;
+
+       if (lp->mirrors == 1)
+               log_count = 0;
+
+       if (failed_mirrors) {
+               if (!lv_remove_mirrors(cmd, lv, failed_mirrors,
+                                      log_count ? 0U : 1U,
+                                      _is_partial_lv, NULL, 0))
+                       return 0;
+       }
+
+       if (!_lv_update_log_type(cmd, lp, lv, lp->failed_pvs,
+                                log_count))
+               return 0;
+
+       if (!_reload_lv(cmd, lv))
+               return 0;
+
+       /*
+        * Second phase - replace faulty devices
+        */
+
+       if (replace_mirrors)
+               lp->mirrors = old_mimage_count;
+
+       /*
+        * It does not make sense to replace the log if the volume is no longer
+        * a mirror.
+        */
+       if (!replace_mirrors && lp->mirrors == 1)
+               replace_log = 0;
+
+       log_count = replace_log ? old_log_count : new_log_count;
+
+       while (replace_mirrors || replace_log) {
+               log_warn("Trying to up-convert to %d images, %d logs.", lp->mirrors, log_count);
+               if (_lvconvert_mirrors_aux(cmd, lv, lp, NULL,
+                                          lp->mirrors, log_count))
+                       break;
+               else {
+                       if (lp->mirrors > 2)
+                               -- lp->mirrors;
+                       else if (log_count > 0)
+                               -- log_count;
+                       else
+                               break; /* nowhere to go, anymore... */
+               }
+       }
+
+       if (replace_mirrors && lp->mirrors != old_mimage_count)
+               log_warn("WARNING: Failed to replace %d of %d images in volume %s",
+                        old_mimage_count - lp->mirrors, old_mimage_count, lv->name);
+       if (replace_log && log_count != old_log_count)
+               log_warn("WARNING: Failed to replace %d of %d logs in volume %s",
+                        old_log_count - log_count, old_log_count, lv->name);
+
+       /* if (!arg_count(cmd, use_policies_ARG) && (lp->mirrors != old_mimage_count
+                                                 || log_count != old_log_count))
+                                                 return 0; */
+
+       return 1;
+}
+
+/*
+ * _lvconvert_mirrors
+ *
+ * Determine what is being done.  Are we doing a conversion, repair, or
+ * collapsing a stack?  Once determined, call helper functions.
+ */
+static int _lvconvert_mirrors(struct cmd_context *cmd,
+                             struct logical_volume *lv,
+                             struct lvconvert_params *lp)
+{
+       int repair = arg_count(cmd, repair_ARG);
+       uint32_t old_mimage_count;
+       uint32_t old_log_count;
+       uint32_t new_mimage_count;
+       uint32_t new_log_count;
+
+       /* Adjust mimage and/or log count */
+       if (!_lvconvert_mirrors_parse_params(cmd, lv, lp,
+                                            &old_mimage_count, &old_log_count,
+                                            &new_mimage_count, &new_log_count))
+               return 0;
+
+        if (((old_mimage_count < new_mimage_count && old_log_count > new_log_count) ||
+             (old_mimage_count > new_mimage_count && old_log_count < new_log_count)) &&
+            lp->pv_count) {
+               log_error("Cannot both allocate and free extents when specifying physical"
+                         " volumes to use.");
+               log_error("Please specify the operation in two steps.");
+               return 0;
+        }
+
+       /* Nothing to do?  (Probably finishing collapse.) */
+       if ((old_mimage_count == new_mimage_count) &&
+           (old_log_count == new_log_count) && !repair)
+               return 1;
+
+       if (repair)
+               return _lvconvert_mirrors_repair(cmd, lv, lp,
+                                                old_mimage_count,
+                                                old_log_count);
+
+       if (!_lvconvert_mirrors_aux(cmd, lv, lp, NULL,
+                                   new_mimage_count, new_log_count))
+               return 0;
+
+       if (!lp->need_polling)
+               log_print("Logical volume %s converted.", lv->name);
+
+       backup(lv->vg);
+       return 1;
+}
+
+static int lvconvert_snapshot(struct cmd_context *cmd,
+                             struct logical_volume *lv,
+                             struct lvconvert_params *lp)
+{
+       struct logical_volume *org;
+       int r = 0;
+
+       if (!(org = find_lv(lv->vg, lp->origin))) {
+               log_error("Couldn't find origin volume '%s'.", lp->origin);
+               return 0;
+       }
+
+       if (org == lv) {
+               log_error("Unable to use \"%s\" as both snapshot and origin.",
+                         lv->name);
+               return 0;
+       }
+
+       if (org->status & (LOCKED|PVMOVE|MIRRORED) || lv_is_cow(org)) {
+               log_error("Unable to create a snapshot of a %s LV.",
+                         org->status & LOCKED ? "locked" :
+                         org->status & PVMOVE ? "pvmove" :
+                         org->status & MIRRORED ? "mirrored" :
+                         "snapshot");
+               return 0;
+       }
+
+       if (!lp->zero || !(lv->status & LVM_WRITE))
+               log_warn("WARNING: \"%s\" not zeroed", lv->name);
+       else if (!set_lv(cmd, lv, UINT64_C(0), 0)) {
+               log_error("Aborting. Failed to wipe snapshot "
+                         "exception store.");
+               return 0;
+       }
+
+       if (!deactivate_lv(cmd, lv)) {
+               log_error("Couldn't deactivate LV %s.", lv->name);
+               return 0;
+       }
+
+       if (!vg_add_snapshot(org, lv, NULL, org->le_count, lp->chunk_size)) {
+               log_error("Couldn't create snapshot.");
+               return 0;
+       }
+
+       /* store vg on disk(s) */
+       if (!vg_write(lv->vg))
+               return_0;
+
+       if (!suspend_lv(cmd, org)) {
+               log_error("Failed to suspend origin %s", org->name);
+               vg_revert(lv->vg);
+               goto out;
+       }
+
+       if (!vg_commit(lv->vg))
+               goto_out;
+
+       if (!resume_lv(cmd, org)) {
+               log_error("Problem reactivating origin %s", org->name);
+               goto out;
+       }
+
+       log_print("Logical volume %s converted to snapshot.", lv->name);
+       r = 1;
+out:
+       backup(lv->vg);
+       return r;
+}
+
+static int lvconvert_merge(struct cmd_context *cmd,
+                          struct logical_volume *lv,
+                          struct lvconvert_params *lp)
+{
+       int r = 0;
+       int merge_on_activate = 0;
+       struct logical_volume *origin = origin_from_cow(lv);
+       struct lv_segment *cow_seg = find_cow(lv);
+       struct lvinfo info;
+
+       /* Check if merge is possible */
+       if (lv_is_merging_cow(lv)) {
+               log_error("Snapshot %s is already merging", lv->name);
+               return 0;
+       }
+       if (lv_is_merging_origin(origin)) {
+               log_error("Snapshot %s is already merging into the origin",
+                         find_merging_cow(origin)->cow->name);
+               return 0;
+       }
+
+       /*
+        * Prevent merge with open device(s) as it would likely lead
+        * to application/filesystem failure.  Merge on origin's next
+        * activation if either the origin or snapshot LV are currently
+        * open.
+        *
+        * FIXME testing open_count is racey; snapshot-merge target's
+        * constructor and DM should prevent appropriate devices from
+        * being open.
+        */
+       if (lv_info(cmd, origin, 0, &info, 1, 0)) {
+               if (info.open_count) {
+                       log_error("Can't merge over open origin volume");
+                       merge_on_activate = 1;
+               }
+       }
+       if (lv_info(cmd, lv, 0, &info, 1, 0)) {
+               if (info.open_count) {
+                       log_print("Can't merge when snapshot is open");
+                       merge_on_activate = 1;
+               }
+       }
+
+       init_snapshot_merge(cow_seg, origin);
+
+       /* store vg on disk(s) */
+       if (!vg_write(lv->vg))
+               return_0;
+
+       if (merge_on_activate) {
+               /* commit vg but skip starting the merge */
+               if (!vg_commit(lv->vg))
+                       return_0;
+               r = 1;
+               log_print("Merging of snapshot %s will start "
+                         "next activation.", lv->name);
+               goto out;
+       }
+
+       /* Perform merge */
+       if (!suspend_lv(cmd, origin)) {
+               log_error("Failed to suspend origin %s", origin->name);
+               vg_revert(lv->vg);
+               goto out;
+       }
+
+       if (!vg_commit(lv->vg)) {
+               if (!resume_lv(cmd, origin))
+                       stack;
+               goto_out;
+       }
+
+       if (!resume_lv(cmd, origin)) {
+               log_error("Failed to reactivate origin %s", origin->name);
+               goto out;
+       }
+
+       lp->need_polling = 1;
+       lp->lv_to_poll = origin;
+
+       r = 1;
+       log_print("Merging of volume %s started.", lv->name);
+out:
+       backup(lv->vg);
+       return r;
+}
+
+static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv,
+                            void *handle)
+{
+       struct lvconvert_params *lp = handle;
+
+       if (lv->status & LOCKED) {
+               log_error("Cannot convert locked LV %s", lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv_is_cow(lv) && !lp->merge) {
+               log_error("Can't convert snapshot logical volume \"%s\"",
+                         lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv->status & PVMOVE) {
+               log_error("Unable to convert pvmove LV %s", lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (arg_count(cmd, repair_ARG) && !(lv->status & MIRRORED)) {
+               if (arg_count(cmd, use_policies_ARG))
+                       return ECMD_PROCESSED; /* nothing to be done here */
+               log_error("Can't repair non-mirrored LV \"%s\".", lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lp->merge) {
+               if (!lv_is_cow(lv)) {
+                       log_error("Logical volume \"%s\" is not a snapshot",
+                                 lv->name);
+                       return ECMD_FAILED;
+               }
+               if (!archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               if (!lvconvert_merge(cmd, lv, lp)) {
+                       log_error("Unable to merge LV \"%s\" into its origin.", lv->name);
+                       return ECMD_FAILED;
+               }
+       } else if (lp->snapshot) {
+               if (lv->status & MIRRORED) {
+                       log_error("Unable to convert mirrored LV \"%s\" into a snapshot.", lv->name);
+                       return ECMD_FAILED;
+               }
+               if (!archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               if (!lvconvert_snapshot(cmd, lv, lp)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       } else if (arg_count(cmd, mirrors_ARG) ||
+                  arg_count(cmd, splitmirrors_ARG) ||
+                  (lv->status & MIRRORED)) {
+               if (!archive(lv->vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               if (!_lvconvert_mirrors(cmd, lv, lp)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               /* If repairing and using policies, remove missing PVs from VG */
+               if (arg_count(cmd, repair_ARG) && arg_count(cmd, use_policies_ARG))
+                       _remove_missing_empty_pv(lv->vg, lp->failed_pvs);
+       }
+
+       return ECMD_PROCESSED;
+}
+
+/*
+ * FIXME move to toollib along with the rest of the drop/reacquire
+ * VG locking that is used by lvconvert_merge_single()
+ */
+static struct logical_volume *get_vg_lock_and_logical_volume(struct cmd_context *cmd,
+                                                            const char *vg_name,
+                                                            const char *lv_name)
+{
+       /*
+        * Returns NULL if the requested LV doesn't exist;
+        * otherwise the caller must free_vg(lv->vg)
+        * - it is also up to the caller to unlock_vg() as needed
+        */
+       struct volume_group *vg;
+       struct logical_volume* lv = NULL;
+
+       vg = _get_lvconvert_vg(cmd, vg_name, NULL);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               return_NULL;
+       }
+
+       if (!(lv = _get_lvconvert_lv(cmd, vg, lv_name, NULL, 0))) {
+               log_error("Can't find LV %s in VG %s", lv_name, vg_name);
+               unlock_and_free_vg(cmd, vg, vg_name);
+               return NULL;
+       }
+
+       return lv;
+}
+
+static int poll_logical_volume(struct cmd_context *cmd, struct logical_volume *lv,
+                              int wait_completion)
+{
+       struct lvinfo info;
+
+       if (!lv_info(cmd, lv, 0, &info, 1, 0) || !info.exists) {
+               log_print("Conversion starts after activation.");
+               return ECMD_PROCESSED;
+       }
+       return lvconvert_poll(cmd, lv, wait_completion ? 0 : 1U);
+}
+
+static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp)
+{
+       struct logical_volume *lv = NULL;
+       int ret = ECMD_FAILED;
+       int saved_ignore_suspended_devices = ignore_suspended_devices();
+
+       if (arg_count(cmd, repair_ARG)) {
+               init_ignore_suspended_devices(1);
+               cmd->handles_missing_pvs = 1;
+       }
+
+       lv = get_vg_lock_and_logical_volume(cmd, lp->vg_name, lp->lv_name);
+       if (!lv)
+               goto_out;
+
+       /*
+        * lp->pvh holds the list of PVs available for allocation or removal
+        */
+       if (lp->pv_count) {
+               if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, lp->pv_count,
+                                             lp->pvs, 0)))
+                       goto_bad;
+       } else
+               lp->pvh = &lv->vg->pvs;
+
+       lp->lv_to_poll = lv;
+       ret = _lvconvert_single(cmd, lv, lp);
+bad:
+       unlock_vg(cmd, lp->vg_name);
+
+       if (ret == ECMD_PROCESSED && lp->need_polling)
+               ret = poll_logical_volume(cmd, lp->lv_to_poll,
+                                         lp->wait_completion);
+
+       free_vg(lv->vg);
+out:
+       init_ignore_suspended_devices(saved_ignore_suspended_devices);
+       return ret;
+}
+
+static int lvconvert_merge_single(struct cmd_context *cmd, struct logical_volume *lv,
+                                 void *handle)
+{
+       struct lvconvert_params *lp = handle;
+       const char *vg_name = NULL;
+       struct logical_volume *refreshed_lv = NULL;
+       int ret;
+
+       /*
+        * FIXME can't trust lv's VG to be current given that caller
+        * is process_each_lv() -- poll_logical_volume() may have
+        * already updated the VG's metadata in an earlier iteration.
+        * - preemptively drop the VG lock, as is needed for
+        *   poll_logical_volume(), refresh LV (and VG in the process).
+        */
+       vg_name = lv->vg->name;
+       unlock_vg(cmd, vg_name);
+       refreshed_lv = get_vg_lock_and_logical_volume(cmd, vg_name, lv->name);
+       if (!refreshed_lv) {
+               log_error("ABORTING: Can't reread LV %s/%s", vg_name, lv->name);
+               return ECMD_FAILED;
+       }
+
+       lp->lv_to_poll = refreshed_lv;
+       ret = _lvconvert_single(cmd, refreshed_lv, lp);
+
+       if (ret == ECMD_PROCESSED && lp->need_polling) {
+               /*
+                * Must drop VG lock, because lvconvert_poll() needs it,
+                * then reacquire it after polling completes
+                */
+               unlock_vg(cmd, vg_name);
+
+               ret = poll_logical_volume(cmd, lp->lv_to_poll,
+                                         lp->wait_completion);
+
+               /* use LCK_VG_WRITE to match lvconvert()'s READ_FOR_UPDATE */
+               if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
+                       log_error("ABORTING: Can't relock VG for %s "
+                                 "after polling finished", vg_name);
+                       ret = ECMD_FAILED;
+               }
+       }
+
+       free_vg(refreshed_lv->vg);
+
+       return ret;
+}
+
+int lvconvert(struct cmd_context * cmd, int argc, char **argv)
+{
+       struct lvconvert_params lp;
+
+       if (!_read_params(&lp, cmd, argc, argv)) {
+               stack;
+               return EINVALID_CMD_LINE;
+       }
+
+       if (lp.merge) {
+               if (!argc) {
+                       log_error("Please provide logical volume path");
+                       return EINVALID_CMD_LINE;
+               }
+               return process_each_lv(cmd, argc, argv, READ_FOR_UPDATE, &lp,
+                                      &lvconvert_merge_single);
+       }
+
+       return lvconvert_single(cmd, &lp);
+}
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
new file mode 100644 (file)
index 0000000..13b5926
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "lv_alloc.h"
+
+#include <fcntl.h>
+
+struct lvcreate_cmdline_params {
+       percent_type_t percent;
+       uint64_t size;
+       char **pvs;
+       int pv_count;
+};
+
+static int _lvcreate_name_params(struct lvcreate_params *lp,
+                                struct cmd_context *cmd,
+                                int *pargc, char ***pargv)
+{
+       int argc = *pargc;
+       char **argv = *pargv, *ptr;
+       char *vg_name;
+
+       lp->lv_name = arg_str_value(cmd, name_ARG, NULL);
+
+       if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) {
+               if (!argc) {
+                       log_error("Please specify a logical volume to act as "
+                                 "the snapshot origin.");
+                       return 0;
+               }
+
+               lp->origin = argv[0];
+               (*pargv)++, (*pargc)--;
+               if (!(lp->vg_name = extract_vgname(cmd, lp->origin))) {
+                       log_error("The origin name should include the "
+                                 "volume group.");
+                       return 0;
+               }
+
+               /* Strip the volume group from the origin */
+               if ((ptr = strrchr(lp->origin, (int) '/')))
+                       lp->origin = ptr + 1;
+
+       } else {
+               /*
+                * If VG not on command line, try -n arg and then
+                * environment.
+                */
+               if (!argc) {
+                       if (!(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
+                               log_error("Please provide a volume group name");
+                               return 0;
+                       }
+
+               } else {
+                       vg_name = skip_dev_dir(cmd, argv[0], NULL);
+                       if (strrchr(vg_name, '/')) {
+                               log_error("Volume group name expected "
+                                         "(no slash)");
+                               return 0;
+                       }
+
+                       /*
+                        * Ensure lv_name doesn't contain a
+                        * different VG.
+                        */
+                       if (lp->lv_name && strchr(lp->lv_name, '/')) {
+                               if (!(lp->vg_name =
+                                     extract_vgname(cmd, lp->lv_name)))
+                                       return 0;
+
+                               if (strcmp(lp->vg_name, vg_name)) {
+                                       log_error("Inconsistent volume group "
+                                                 "names "
+                                                 "given: \"%s\" and \"%s\"",
+                                                 lp->vg_name, vg_name);
+                                       return 0;
+                               }
+                       }
+
+                       lp->vg_name = vg_name;
+                       (*pargv)++, (*pargc)--;
+               }
+       }
+
+       if (!validate_name(lp->vg_name)) {
+               log_error("Volume group name %s has invalid characters",
+                         lp->vg_name);
+               return 0;
+       }
+
+       if (lp->lv_name) {
+               if ((ptr = strrchr(lp->lv_name, '/')))
+                       lp->lv_name = ptr + 1;
+
+               if (!apply_lvname_restrictions(lp->lv_name))
+                       return_0;
+
+               if (!validate_name(lp->lv_name)) {
+                       log_error("Logical volume name \"%s\" is invalid",
+                                 lp->lv_name);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Update extents parameters based on other parameters which affect the size
+ * calcuation.
+ * NOTE: We must do this here because of the percent_t typedef and because we
+ * need the vg.
+ */
+static int _update_extents_params(struct volume_group *vg,
+                                 struct lvcreate_params *lp,
+                                 struct lvcreate_cmdline_params *lcp)
+{
+       uint32_t pv_extent_count;
+       struct logical_volume *origin = NULL;
+
+       if (lcp->size &&
+           !(lp->extents = extents_from_size(vg->cmd, lcp->size,
+                                              vg->extent_size)))
+               return_0;
+
+       if (lp->voriginsize &&
+           !(lp->voriginextents = extents_from_size(vg->cmd, lp->voriginsize,
+                                                     vg->extent_size)))
+               return_0;
+
+       /*
+        * Create the pv list before we parse lcp->percent - might be
+        * PERCENT_PVSs
+        */
+       if (lcp->pv_count) {
+               if (!(lp->pvh = create_pv_list(vg->cmd->mem, vg,
+                                          lcp->pv_count, lcp->pvs, 1)))
+                       return_0;
+       } else
+               lp->pvh = &vg->pvs;
+
+       switch(lcp->percent) {
+               case PERCENT_VG:
+                       lp->extents = lp->extents * vg->extent_count / 100;
+                       break;
+               case PERCENT_FREE:
+                       lp->extents = lp->extents * vg->free_count / 100;
+                       break;
+               case PERCENT_PVS:
+                       if (!lcp->pv_count)
+                               lp->extents = lp->extents * vg->extent_count / 100;
+                       else {
+                               pv_extent_count = pv_list_extents_free(lp->pvh);
+                               lp->extents = lp->extents * pv_extent_count / 100;
+                       }
+                       break;
+               case PERCENT_LV:
+                       log_error("Please express size as %%VG, %%PVS, or "
+                                 "%%FREE.");
+                       return 0;
+               case PERCENT_ORIGIN:
+                       if (lp->snapshot && lp->origin &&
+                           !(origin = find_lv(vg, lp->origin))) {
+                               log_error("Couldn't find origin volume '%s'.",
+                                         lp->origin);
+                               return 0;
+                       }
+                       if (!origin) {
+                               log_error(INTERNAL_ERROR "Couldn't find origin volume.");
+                               return 0;
+                       }
+                       lp->extents = lp->extents * origin->le_count / 100;
+                       break;
+               case PERCENT_NONE:
+                       break;
+       }
+       return 1;
+}
+
+static int _read_size_params(struct lvcreate_params *lp,
+                            struct lvcreate_cmdline_params *lcp,
+                            struct cmd_context *cmd)
+{
+       if (arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) != 1) {
+               log_error("Please specify either size or extents (not both)");
+               return 0;
+       }
+
+       if (arg_count(cmd, extents_ARG)) {
+               if (arg_sign_value(cmd, extents_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative number of extents is invalid");
+                       return 0;
+               }
+               lp->extents = arg_uint_value(cmd, extents_ARG, 0);
+               lcp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
+       }
+
+       /* Size returned in kilobyte units; held in sectors */
+       if (arg_count(cmd, size_ARG)) {
+               if (arg_sign_value(cmd, size_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative size is invalid");
+                       return 0;
+               }
+               lcp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
+               lcp->percent = PERCENT_NONE;
+       }
+
+       /* Size returned in kilobyte units; held in sectors */
+       if (arg_count(cmd, virtualsize_ARG)) {
+               if (arg_sign_value(cmd, virtualsize_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative virtual origin size is invalid");
+                       return 0;
+               }
+               lp->voriginsize = arg_uint64_value(cmd, virtualsize_ARG,
+                                                  UINT64_C(0));
+               if (!lp->voriginsize) {
+                       log_error("Virtual origin size may not be zero");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Generic mirror parameter checks.
+ * FIXME: Should eventually be moved into lvm library.
+ */
+static int _validate_mirror_params(const struct cmd_context *cmd __attribute__((unused)),
+                                  const struct lvcreate_params *lp)
+{
+       int pagesize = lvm_getpagesize();
+
+       if (lp->region_size & (lp->region_size - 1)) {
+               log_error("Region size (%" PRIu32 ") must be a power of 2",
+                         lp->region_size);
+               return 0;
+       }
+
+       if (lp->region_size % (pagesize >> SECTOR_SHIFT)) {
+               log_error("Region size (%" PRIu32 ") must be a multiple of "
+                         "machine memory page size (%d)",
+                         lp->region_size, pagesize >> SECTOR_SHIFT);
+               return 0;
+       }
+
+       if (!lp->region_size) {
+               log_error("Non-zero region size must be supplied.");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _read_mirror_params(struct lvcreate_params *lp,
+                              struct cmd_context *cmd)
+{
+       int region_size;
+       const char *mirrorlog;
+       int corelog = arg_count(cmd, corelog_ARG);
+
+       mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
+                                 corelog ? "core" : DEFAULT_MIRRORLOG);
+
+       if (strcmp("core", mirrorlog) && corelog) {
+               log_error("Please use only one of --mirrorlog or --corelog");
+               return 0;
+       }
+
+       if (!strcmp("mirrored", mirrorlog)) {
+               lp->log_count = 2;
+       } else if (!strcmp("disk", mirrorlog)) {
+               lp->log_count = 1;
+       } else if (!strcmp("core", mirrorlog))
+               lp->log_count = 0;
+       else {
+               log_error("Unknown mirrorlog type: %s", mirrorlog);
+               return 0;
+       }
+
+       log_verbose("Setting logging type to %s", mirrorlog);
+
+       lp->nosync = arg_is_set(cmd, nosync_ARG);
+
+       if (arg_count(cmd, regionsize_ARG)) {
+               if (arg_sign_value(cmd, regionsize_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative regionsize is invalid");
+                       return 0;
+               }
+               lp->region_size = arg_uint_value(cmd, regionsize_ARG, 0);
+       } else {
+               region_size = 2 * find_config_tree_int(cmd,
+                                       "activation/mirror_region_size",
+                                       DEFAULT_MIRROR_REGION_SIZE);
+               if (region_size < 0) {
+                       log_error("Negative regionsize in configuration file "
+                                 "is invalid");
+                       return 0;
+               }
+               lp->region_size = region_size;
+       }
+
+       if (!_validate_mirror_params(cmd, lp))
+               return 0;
+
+       return 1;
+}
+
+static int _lvcreate_params(struct lvcreate_params *lp,
+                           struct lvcreate_cmdline_params *lcp,
+                           struct cmd_context *cmd,
+                           int argc, char **argv)
+{
+       int contiguous;
+       unsigned pagesize;
+       struct arg_value_group_list *current_group;
+       const char *tag;
+
+       memset(lp, 0, sizeof(*lp));
+       memset(lcp, 0, sizeof(*lcp));
+       dm_list_init(&lp->tags);
+
+       /*
+        * Check selected options are compatible and determine segtype
+        */
+       lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "striped"));
+
+       if (arg_count(cmd, snapshot_ARG) || seg_is_snapshot(lp) ||
+           arg_count(cmd, virtualsize_ARG))
+               lp->snapshot = 1;
+
+       lp->mirrors = 1;
+
+       /* Default to 2 mirrored areas if --type mirror */
+       if (seg_is_mirrored(lp))
+               lp->mirrors = 2;
+
+       if (arg_count(cmd, mirrors_ARG)) {
+               lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1;
+               if (lp->mirrors == 1)
+                       log_print("Redundant mirrors argument: default is 0");
+               if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+                       log_error("Mirrors argument may not be negative");
+                       return 0;
+               }
+       }
+
+       if (lp->snapshot) {
+               if (arg_count(cmd, zero_ARG)) {
+                       log_error("-Z is incompatible with snapshots");
+                       return 0;
+               }
+               if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative chunk size is invalid");
+                       return 0;
+               }
+               lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8);
+               if (lp->chunk_size < 8 || lp->chunk_size > 1024 ||
+                   (lp->chunk_size & (lp->chunk_size - 1))) {
+                       log_error("Chunk size must be a power of 2 in the "
+                                 "range 4K to 512K");
+                       return 0;
+               }
+               log_verbose("Setting chunksize to %d sectors.", lp->chunk_size);
+
+               if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
+                       return_0;
+       } else {
+               if (arg_count(cmd, chunksize_ARG)) {
+                       log_error("-c is only available with snapshots");
+                       return 0;
+               }
+       }
+
+       if (lp->mirrors > 1) {
+               if (lp->snapshot) {
+                       log_error("mirrors and snapshots are currently "
+                                 "incompatible");
+                       return 0;
+               }
+
+               if (!(lp->segtype = get_segtype_from_string(cmd, "striped")))
+                       return_0;
+       } else {
+               if (arg_count(cmd, corelog_ARG)) {
+                       log_error("--corelog is only available with mirrors");
+                       return 0;
+               }
+
+               if (arg_count(cmd, mirrorlog_ARG)) {
+                       log_error("--mirrorlog is only available with mirrors");
+                       return 0;
+               }
+
+               if (arg_count(cmd, nosync_ARG)) {
+                       log_error("--nosync is only available with mirrors");
+                       return 0;
+               }
+       }
+
+       if (activation() && lp->segtype->ops->target_present &&
+           !lp->segtype->ops->target_present(cmd, NULL, NULL)) {
+               log_error("%s: Required device-mapper target(s) not "
+                         "detected in your kernel", lp->segtype->name);
+               return 0;
+       }
+
+       if (!get_activation_monitoring_mode(cmd, NULL,
+                                           &lp->activation_monitoring))
+               return_0;
+
+       if (!_lvcreate_name_params(lp, cmd, &argc, &argv) ||
+           !_read_size_params(lp, lcp, cmd) ||
+           !get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) ||
+           !_read_mirror_params(lp, cmd))
+               return_0;
+
+       /*
+        * Should we zero the lv.
+        */
+       lp->zero = strcmp(arg_str_value(cmd, zero_ARG,
+               (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n");
+
+       /*
+        * Alloc policy
+        */
+       contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n");
+
+       lp->alloc = contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT;
+
+       lp->alloc = arg_uint_value(cmd, alloc_ARG, lp->alloc);
+
+       if (contiguous && (lp->alloc != ALLOC_CONTIGUOUS)) {
+               log_error("Conflicting contiguous and alloc arguments");
+               return 0;
+       }
+
+       if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) {
+               log_error("Only up to %d images in mirror supported currently.",
+                         DEFAULT_MIRROR_MAX_IMAGES);
+               return 0;
+       }
+
+       /*
+        * Read ahead.
+        */
+       lp->read_ahead = arg_uint_value(cmd, readahead_ARG,
+                                       cmd->default_settings.read_ahead);
+       pagesize = lvm_getpagesize() >> SECTOR_SHIFT;
+       if (lp->read_ahead != DM_READ_AHEAD_AUTO &&
+           lp->read_ahead != DM_READ_AHEAD_NONE &&
+           lp->read_ahead % pagesize) {
+               if (lp->read_ahead < pagesize)
+                       lp->read_ahead = pagesize;
+               else
+                       lp->read_ahead = (lp->read_ahead / pagesize) * pagesize;
+               log_warn("WARNING: Overriding readahead to %u sectors, a multiple "
+                           "of %uK page size.", lp->read_ahead, pagesize >> 1);
+       }
+
+       /*
+        * Permissions.
+        */
+       lp->permission = arg_uint_value(cmd, permission_ARG,
+                                       LVM_READ | LVM_WRITE);
+
+       /* Must not zero read only volume */
+       if (!(lp->permission & LVM_WRITE))
+               lp->zero = 0;
+
+       lp->minor = arg_int_value(cmd, minor_ARG, -1);
+       lp->major = arg_int_value(cmd, major_ARG, -1);
+
+       /* Persistent minor */
+       if (arg_count(cmd, persistent_ARG)) {
+               if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "y")) {
+                       if (lp->minor == -1) {
+                               log_error("Please specify minor number with "
+                                         "--minor when using -My");
+                               return 0;
+                       }
+                       if (lp->major == -1) {
+                               log_error("Please specify major number with "
+                                         "--major when using -My");
+                               return 0;
+                       }
+               } else {
+                       if ((lp->minor != -1) || (lp->major != -1)) {
+                               log_error("--major and --minor incompatible "
+                                         "with -Mn");
+                               return 0;
+                       }
+               }
+       } else if (arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) {
+               log_error("--major and --minor require -My");
+               return 0;
+       }
+
+       dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+               if (!grouped_arg_is_set(current_group->arg_values, addtag_ARG))
+                       continue;
+
+               if (!(tag = grouped_arg_str_value(current_group->arg_values, addtag_ARG, NULL))) {
+                       log_error("Failed to get tag");
+                       return 0;
+               }
+
+               if (!str_list_add(cmd->mem, &lp->tags, tag)) {
+                       log_error("Unable to allocate memory for tag %s", tag);
+                       return 0;
+               }
+        }
+
+       lcp->pv_count = argc;
+       lcp->pvs = argv;
+
+       return 1;
+}
+
+int lvcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+       int r = ECMD_PROCESSED;
+       struct lvcreate_params lp;
+       struct lvcreate_cmdline_params lcp;
+       struct volume_group *vg;
+
+       memset(&lp, 0, sizeof(lp));
+
+       if (!_lvcreate_params(&lp, &lcp, cmd, argc, argv))
+               return EINVALID_CMD_LINE;
+
+       log_verbose("Finding volume group \"%s\"", lp.vg_name);
+       vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!_update_extents_params(vg, &lp, &lcp)) {
+               r = ECMD_FAILED;
+               goto_out;
+       }
+
+       if (!lv_create_single(vg, &lp)) {
+               stack;
+               r = ECMD_FAILED;
+       }
+out:
+       unlock_and_free_vg(cmd, vg, lp.vg_name);
+       return r;
+}
diff --git a/tools/lvdisplay.c b/tools/lvdisplay.c
new file mode 100644 (file)
index 0000000..f5531cb
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int _lvdisplay_single(struct cmd_context *cmd, struct logical_volume *lv,
+                            void *handle)
+{
+       if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
+               return ECMD_PROCESSED;
+
+       if (arg_count(cmd, colon_ARG))
+               lvdisplay_colons(lv);
+       else {
+               lvdisplay_full(cmd, lv, handle);
+               if (arg_count(cmd, maps_ARG))
+                       lvdisplay_segments(lv);
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int lvdisplay(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (arg_count(cmd, columns_ARG)) {
+               if (arg_count(cmd, colon_ARG) || arg_count(cmd, maps_ARG)) {
+                       log_error("Incompatible options selected");
+                       return EINVALID_CMD_LINE;
+               }
+               return lvs(cmd, argc, argv);
+       } else if (arg_count(cmd, aligned_ARG) ||
+                  arg_count(cmd, noheadings_ARG) ||
+                  arg_count(cmd, options_ARG) ||
+                  arg_count(cmd, separator_ARG) ||
+                  arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) {
+               log_error("Incompatible options selected");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, colon_ARG) && arg_count(cmd, verbose_ARG)) {
+               log_error("Options -v and -c are incompatible");
+               return EINVALID_CMD_LINE;
+       }
+
+       return process_each_lv(cmd, argc, argv, 0, NULL,
+                              &_lvdisplay_single);
+}
diff --git a/tools/lvextend.c b/tools/lvextend.c
new file mode 100644 (file)
index 0000000..9b59f0f
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int lvextend(struct cmd_context *cmd, int argc, char **argv)
+{
+       return lvresize(cmd, argc, argv);
+}
diff --git a/tools/lvm-static.c b/tools/lvm-static.c
new file mode 100644 (file)
index 0000000..1be4c24
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+
+int main(int argc, char **argv)
+{
+       init_is_static(1);
+       return lvm2_main(argc, argv);
+}
+
+int lvm_shell(struct cmd_context *cmd __attribute__((unused)),
+             struct cmdline_context *cmdline __attribute__((unused)))
+{
+       return 0;
+}
diff --git a/tools/lvm.c b/tools/lvm.c
new file mode 100644 (file)
index 0000000..cec9f80
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+
+int main(int argc, char **argv)
+{
+       return lvm2_main(argc, argv);
+}
+
+#ifdef READLINE_SUPPORT
+
+#  include <readline/readline.h>
+#  include <readline/history.h>
+#  ifndef HAVE_RL_COMPLETION_MATCHES
+#    define rl_completion_matches(a, b) completion_matches((char *)a, b)
+#  endif
+
+static struct cmdline_context *_cmdline;
+
+/* List matching commands */
+static char *_list_cmds(const char *text, int state)
+{
+       static int i = 0;
+       static size_t len = 0;
+
+       /* Initialise if this is a new completion attempt */
+       if (!state) {
+               i = 0;
+               len = strlen(text);
+       }
+
+       while (i < _cmdline->num_commands)
+               if (!strncmp(text, _cmdline->commands[i++].name, len))
+                       return strdup(_cmdline->commands[i - 1].name);
+
+       return NULL;
+}
+
+/* List matching arguments */
+static char *_list_args(const char *text, int state)
+{
+       static int match_no = 0;
+       static size_t len = 0;
+       static struct command *com;
+
+       /* Initialise if this is a new completion attempt */
+       if (!state) {
+               char *s = rl_line_buffer;
+               int j = 0;
+
+               match_no = 0;
+               com = NULL;
+               len = strlen(text);
+
+               /* Find start of first word in line buffer */
+               while (isspace(*s))
+                       s++;
+
+               /* Look for word in list of commands */
+               for (j = 0; j < _cmdline->num_commands; j++) {
+                       const char *p;
+                       char *q = s;
+
+                       p = _cmdline->commands[j].name;
+                       while (*p == *q) {
+                               p++;
+                               q++;
+                       }
+                       if ((!*p) && *q == ' ') {
+                               com = _cmdline->commands + j;
+                               break;
+                       }
+               }
+       }
+
+       if (!com)
+               return NULL;
+
+       /* Short form arguments */
+       if (len < 3) {
+               while (match_no < com->num_args) {
+                       char s[3];
+                       char c;
+                       if (!(c = (_cmdline->arg_props +
+                                  com->valid_args[match_no++])->short_arg))
+                               continue;
+
+                       sprintf(s, "-%c", c);
+                       if (!strncmp(text, s, len))
+                               return strdup(s);
+               }
+       }
+
+       /* Long form arguments */
+       if (match_no < com->num_args)
+               match_no = com->num_args;
+
+       while (match_no - com->num_args < com->num_args) {
+               const char *l;
+               l = (_cmdline->arg_props +
+                    com->valid_args[match_no++ - com->num_args])->long_arg;
+               if (*(l + 2) && !strncmp(text, l, len))
+                       return strdup(l);
+       }
+
+       return NULL;
+}
+
+/* Custom completion function */
+static char **_completion(const char *text, int start_pos,
+                         int end_pos __attribute__((unused)))
+{
+       char **match_list = NULL;
+       int p = 0;
+
+       while (isspace((int) *(rl_line_buffer + p)))
+               p++;
+
+       /* First word should be one of our commands */
+       if (start_pos == p)
+               match_list = rl_completion_matches(text, _list_cmds);
+
+       else if (*text == '-')
+               match_list = rl_completion_matches(text, _list_args);
+       /* else other args */
+
+       /* No further completion */
+       rl_attempted_completion_over = 1;
+       return match_list;
+}
+
+static int _hist_file(char *buffer, size_t size)
+{
+       char *e = getenv("HOME");
+
+       if (dm_snprintf(buffer, size, "%s/.lvm_history", e) < 0) {
+               log_error("$HOME/.lvm_history: path too long");
+               return 0;
+       }
+
+       return 1;
+}
+
+static void _read_history(struct cmd_context *cmd)
+{
+       char hist_file[PATH_MAX];
+
+       if (!_hist_file(hist_file, sizeof(hist_file)))
+               return;
+
+       if (read_history(hist_file))
+               log_very_verbose("Couldn't read history from %s.", hist_file);
+
+       stifle_history(find_config_tree_int(cmd, "shell/history_size",
+                                      DEFAULT_MAX_HISTORY));
+
+}
+
+static void _write_history(void)
+{
+       char hist_file[PATH_MAX];
+
+       if (!_hist_file(hist_file, sizeof(hist_file)))
+               return;
+
+       if (write_history(hist_file))
+               log_very_verbose("Couldn't write history to %s.", hist_file);
+}
+
+int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
+{
+       int argc, ret;
+       char *input = NULL, *args[MAX_ARGS], **argv;
+
+       rl_readline_name = "lvm";
+       rl_attempted_completion_function = (CPPFunction *) _completion;
+
+       _read_history(cmd);
+
+       _cmdline = cmdline;
+
+       _cmdline->interactive = 1;
+       while (1) {
+               free(input);
+               input = readline("lvm> ");
+
+               /* EOF */
+               if (!input) {
+                       printf("\n");
+                       break;
+               }
+
+               /* empty line */
+               if (!*input)
+                       continue;
+
+               add_history(input);
+
+               argv = args;
+
+               if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+                       log_error("Too many arguments, sorry.");
+                       continue;
+               }
+
+               if (!argc)
+                       continue;
+
+               if (!strcmp(argv[0], "lvm")) {
+                       argv++;
+                       argc--;
+               }
+
+               if (!argc)
+                       continue;
+
+               if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
+                       remove_history(history_length - 1);
+                       log_error("Exiting.");
+                       break;
+               }
+
+               ret = lvm_run_command(cmd, argc, argv);
+               if (ret == ENO_SUCH_CMD)
+                       log_error("No such command '%s'.  Try 'help'.",
+                                 argv[0]);
+
+                if ((ret != ECMD_PROCESSED) && !error_message_produced()) {
+                       log_debug(INTERNAL_ERROR "Failed command did not use log_error");
+                       log_error("Command failed with status code %d.", ret);
+               }
+               _write_history();
+       }
+
+       free(input);
+       return 0;
+}
+
+#endif /* READLINE_SUPPORT */
diff --git a/tools/lvm2cmd-static.c b/tools/lvm2cmd-static.c
new file mode 100644 (file)
index 0000000..af14ec5
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lvm2cmdline.h"
+#include "lvm2cmd.h"
+
+void *lvm2_init(void)
+{
+       return cmdlib_lvm2_init(1);
+}
diff --git a/tools/lvm2cmd.c b/tools/lvm2cmd.c
new file mode 100644 (file)
index 0000000..1e1e059
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2006-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "lvm2cmdline.h"
+#include "lvm2cmd.h"
+
+void *lvm2_init(void)
+{
+       return cmdlib_lvm2_init(0);
+}
+
+int lvm_shell(struct cmd_context *cmd __attribute__((unused)),
+             struct cmdline_context *cmdline __attribute__((unused)))
+{
+       return 0;
+}
diff --git a/tools/lvm2cmd.h b/tools/lvm2cmd.h
new file mode 100644 (file)
index 0000000..66651d8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_CMDLIB_H
+#define _LVM_CMDLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _LVM_LOG_H
+typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
+                               int dm_errno, const char *message);
+
+#endif
+
+#define LVM2_LOG_SUPPRESS      0
+
+/* Logging levels */
+#define LVM2_LOG_FATAL         2
+#define LVM2_LOG_ERROR         3
+#define LVM2_LOG_PRINT         4
+#define LVM2_LOG_VERBOSE       5
+#define LVM2_LOG_VERY_VERBOSE  6
+#define LVM2_LOG_DEBUG         7
+
+/*
+ * Define external function to replace the built-in logging function.
+ * It receives output line-by-line.
+ *
+ * level is the logging level (see above)
+ * file & line refer to the source code where the message originates.
+ */
+void lvm2_log_fn(lvm2_log_fn_t log_fn);
+
+/*
+ * Initialise library.
+ * Returns a handle so repeated use of lvm2_run is more efficient.
+ */ 
+void *lvm2_init(void);
+
+/*
+ * Set log level (as above) if using built-in logging function. 
+ * Default is LVM2_LOG_PRINT.  Use LVM2_LOG_SUPPRESS to suppress output.
+ */
+void lvm2_log_level(void *handle, int level);
+
+/*
+ * Run an LVM2 command. 
+ * Use NULL handle if the call is a one-off and you don't want to bother 
+ * calling lvm2_init/lvm2_exit.
+ */
+int lvm2_run(void *handle, const char *cmdline);
+
+/* Release handle */
+void lvm2_exit(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/tools/lvm2cmdline.h b/tools/lvm2cmdline.h
new file mode 100644 (file)
index 0000000..5c4889e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_CMDLINE_H
+#define _LVM_CMDLINE_H
+
+struct cmd_context;
+
+struct cmdline_context {
+        struct arg_props *arg_props;
+        struct command *commands;
+        int num_commands;
+        int commands_size;
+        int interactive;
+};
+
+int lvm2_main(int argc, char **argv);
+
+void *cmdlib_lvm2_init(unsigned static_compile);
+void lvm_fin(struct cmd_context *cmd);
+
+struct cmd_context *init_lvm(void);
+void lvm_register_commands(void);
+int lvm_split(char *str, int *argc, char **argv, int max);
+int lvm_run_command(struct cmd_context *cmd, int argc, char **argv);
+int lvm_return_code(int ret);
+int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline);
+
+#endif
diff --git a/tools/lvmchange.c b/tools/lvmchange.c
new file mode 100644 (file)
index 0000000..ff77e95
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int lvmchange(struct cmd_context *cmd __attribute__((unused)),
+             int argc __attribute__((unused)), char **argv __attribute__((unused)))
+{
+       log_error("With LVM2 and the device mapper, this program is obsolete.");
+       return ECMD_FAILED;
+}
diff --git a/tools/lvmcmdlib.c b/tools/lvmcmdlib.c
new file mode 100644 (file)
index 0000000..6b2bc9d
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+#include "label.h"
+#include "memlock.h"
+#include "lvm-version.h"
+
+#include "lvm2cmd.h"
+
+#include <signal.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <sys/resource.h>
+
+void *cmdlib_lvm2_init(unsigned static_compile)
+{
+       struct cmd_context *cmd;
+
+       lvm_register_commands();
+
+       init_is_static(static_compile);
+       if (!(cmd = init_lvm()))
+               return NULL;
+
+       return (void *) cmd;
+}
+
+int lvm2_run(void *handle, const char *cmdline)
+{
+       int argc, ret, oneoff = 0;
+       char *args[MAX_ARGS], **argv, *cmdcopy = NULL;
+       struct cmd_context *cmd;
+
+       argv = args;
+
+       if (!handle) {
+               oneoff = 1;
+               if (!(handle = lvm2_init())) {
+                       log_error("Handle initialisation failed.");
+                       return ECMD_FAILED;
+               }
+       }
+
+       cmd = (struct cmd_context *) handle;
+
+       cmd->argv = argv;
+
+       if (!(cmdcopy = dm_strdup(cmdline))) {
+               log_error("Cmdline copy failed.");
+               ret = ECMD_FAILED;
+               goto out;
+       }
+
+       if (lvm_split(cmdcopy, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+               log_error("Too many arguments.  Limit is %d.", MAX_ARGS);
+               ret = EINVALID_CMD_LINE;
+               goto out;
+       }
+
+       if (!argc) {
+               log_error("No command supplied");
+               ret = EINVALID_CMD_LINE;
+               goto out;
+       }
+
+       /* FIXME Temporary - move to libdevmapper */
+       ret = ECMD_PROCESSED;
+       if (!strcmp(cmdline, "_memlock_inc"))
+               memlock_inc_daemon(cmd);
+       else if (!strcmp(cmdline, "_memlock_dec"))
+               memlock_dec_daemon(cmd);
+       else
+               ret = lvm_run_command(cmd, argc, argv);
+
+      out:
+       dm_free(cmdcopy);
+
+       if (oneoff)
+               lvm2_exit(handle);
+
+       return ret;
+}
+
+void lvm2_log_level(void *handle, int level)
+{
+       struct cmd_context *cmd = (struct cmd_context *) handle;
+
+       cmd->default_settings.verbose = level - VERBOSE_BASE_LEVEL;
+}
+
+void lvm2_log_fn(lvm2_log_fn_t log_fn)
+{
+       init_log_fn(log_fn);
+}
+
+void lvm2_exit(void *handle)
+{
+       struct cmd_context *cmd = (struct cmd_context *) handle;
+
+       lvm_fin(cmd);
+}
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
new file mode 100644 (file)
index 0000000..836dbaa
--- /dev/null
@@ -0,0 +1,1495 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+#include "label.h"
+#include "lvm-version.h"
+
+#include "stub.h"
+#include "lvm2cmd.h"
+#include "last-path-component.h"
+
+#include <signal.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <sys/resource.h>
+
+#ifdef HAVE_GETOPTLONG
+#  include <getopt.h>
+#  define GETOPTLONG_FN(a, b, c, d, e) getopt_long((a), (b), (c), (d), (e))
+#  define OPTIND_INIT 0
+#else
+struct option {
+};
+extern int optind;
+extern char *optarg;
+#  define GETOPTLONG_FN(a, b, c, d, e) getopt((a), (b), (c))
+#  define OPTIND_INIT 1
+#endif
+
+#ifdef UDEV_SYNC_SUPPORT
+#  define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#  include <libudev.h>
+#endif
+
+/*
+ * Table of valid switches
+ */
+static struct arg_props _arg_props[ARG_COUNT + 1] = {
+#define arg(a, b, c, d, e) {b, "", "--" c, d, e},
+#include "args.h"
+#undef arg
+};
+
+static struct cmdline_context _cmdline;
+
+/* Command line args */
+unsigned arg_count(const struct cmd_context *cmd, int a)
+{
+       return cmd->arg_values[a].count;
+}
+
+unsigned grouped_arg_count(const struct arg_values *av, int a)
+{
+       return av[a].count;
+}
+
+unsigned arg_is_set(const struct cmd_context *cmd, int a)
+{
+       return arg_count(cmd, a) ? 1 : 0;
+}
+
+unsigned grouped_arg_is_set(const struct arg_values *av, int a)
+{
+       return grouped_arg_count(av, a) ? 1 : 0;
+}
+
+const char *arg_value(struct cmd_context *cmd, int a)
+{
+       return cmd->arg_values[a].value;
+}
+
+const char *arg_str_value(struct cmd_context *cmd, int a, const char *def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].value : def;
+}
+
+const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def)
+{
+       return grouped_arg_count(av, a) ? av[a].value : def;
+}
+
+int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].i_value : def;
+}
+
+uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].ui_value : def;
+}
+
+int64_t arg_int64_value(struct cmd_context *cmd, int a, const int64_t def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].i64_value : def;
+}
+
+uint64_t arg_uint64_value(struct cmd_context *cmd, int a, const uint64_t def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].ui64_value : def;
+}
+
+/* No longer used.
+const void *arg_ptr_value(struct cmd_context *cmd, int a, const void *def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].ptr : def;
+}
+*/
+
+sign_t arg_sign_value(struct cmd_context *cmd, int a, const sign_t def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].sign : def;
+}
+
+percent_type_t arg_percent_value(struct cmd_context *cmd, int a, const percent_type_t def)
+{
+       return arg_count(cmd, a) ? cmd->arg_values[a].percent : def;
+}
+
+int arg_count_increment(struct cmd_context *cmd, int a)
+{
+       return cmd->arg_values[a].count++;
+}
+
+int yes_no_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       av->sign = SIGN_NONE;
+       av->percent = PERCENT_NONE;
+
+       if (!strcmp(av->value, "y")) {
+               av->i_value = 1;
+               av->ui_value = 1;
+       }
+
+       else if (!strcmp(av->value, "n")) {
+               av->i_value = 0;
+               av->ui_value = 0;
+       }
+
+       else
+               return 0;
+
+       return 1;
+}
+
+int yes_no_excl_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       av->sign = SIGN_NONE;
+       av->percent = PERCENT_NONE;
+
+       if (!strcmp(av->value, "e") || !strcmp(av->value, "ey") ||
+           !strcmp(av->value, "ye")) {
+               av->i_value = CHANGE_AE;
+               av->ui_value = CHANGE_AE;
+       }
+
+       else if (!strcmp(av->value, "y")) {
+               av->i_value = CHANGE_AY;
+               av->ui_value = CHANGE_AY;
+       }
+
+       else if (!strcmp(av->value, "n") || !strcmp(av->value, "en") ||
+                !strcmp(av->value, "ne")) {
+               av->i_value = CHANGE_AN;
+               av->ui_value = CHANGE_AN;
+       }
+
+       else if (!strcmp(av->value, "ln") || !strcmp(av->value, "nl")) {
+               av->i_value = CHANGE_ALN;
+               av->ui_value = CHANGE_ALN;
+       }
+
+       else if (!strcmp(av->value, "ly") || !strcmp(av->value, "yl")) {
+               av->i_value = CHANGE_ALY;
+               av->ui_value = CHANGE_ALY;
+       }
+
+       else
+               return 0;
+
+       return 1;
+}
+
+int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+       return get_format_by_name(cmd, av->value) ? 1 : 0;
+}
+
+static int _get_int_arg(struct arg_values *av, char **ptr)
+{
+       char *val;
+       long v;
+
+       av->percent = PERCENT_NONE;
+
+       val = av->value;
+       switch (*val) {
+       case '+':
+               av->sign = SIGN_PLUS;
+               val++;
+               break;
+       case '-':
+               av->sign = SIGN_MINUS;
+               val++;
+               break;
+       default:
+               av->sign = SIGN_NONE;
+       }
+
+       if (!isdigit(*val))
+               return 0;
+
+       v = strtol(val, ptr, 10);
+
+       if (*ptr == val)
+               return 0;
+
+       av->i_value = (int32_t) v;
+       av->ui_value = (uint32_t) v;
+       av->i64_value = (int64_t) v;
+       av->ui64_value = (uint64_t) v;
+
+       return 1;
+}
+
+/* Size stored in sectors */
+static int _size_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av, int factor)
+{
+       char *ptr;
+       int i;
+       static const char *suffixes = "kmgtpebs";
+       char *val;
+       double v;
+       uint64_t v_tmp, adjustment;
+
+       av->percent = PERCENT_NONE;
+
+       val = av->value;
+       switch (*val) {
+       case '+':
+               av->sign = SIGN_PLUS;
+               val++;
+               break;
+       case '-':
+               av->sign = SIGN_MINUS;
+               val++;
+               break;
+       default:
+               av->sign = SIGN_NONE;
+       }
+
+       if (!isdigit(*val))
+               return 0;
+
+       v = strtod(val, &ptr);
+
+       if (ptr == val)
+               return 0;
+
+       if (*ptr) {
+               for (i = strlen(suffixes) - 1; i >= 0; i--)
+                       if (suffixes[i] == tolower((int) *ptr))
+                               break;
+
+               if (i < 0) {
+                       return 0;
+               } else if (i == 7) {
+                       /* sectors */
+                       v = v;
+               } else if (i == 6) {
+                       /* bytes */
+                       v_tmp = (uint64_t) v;
+                       adjustment = v_tmp % 512;
+                       if (adjustment) {
+                               v_tmp += (512 - adjustment);
+                               log_error("Size is not a multiple of 512. "
+                                         "Try using %"PRIu64" or %"PRIu64".",
+                                         v_tmp - 512, v_tmp);
+                               return 0;
+                       }
+                       v /= 512;
+               } else {
+                       /* all other units: kmgtpe */
+                       while (i-- > 0)
+                               v *= 1024;
+                       v *= 2;
+               }
+       } else
+               v *= factor;
+
+       av->i_value = (int32_t) v;
+       av->ui_value = (uint32_t) v;
+       av->i64_value = (int64_t) v;
+       av->ui64_value = (uint64_t) v;
+
+       return 1;
+}
+
+int size_kb_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+       return _size_arg(cmd, av, 2);
+}
+
+int size_mb_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+       return _size_arg(cmd, av, 2048);
+}
+
+int int_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       char *ptr;
+
+       if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
+               return 0;
+
+       return 1;
+}
+
+int int_arg_with_sign(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       char *ptr;
+
+       if (!_get_int_arg(av, &ptr) || (*ptr))
+               return 0;
+
+       return 1;
+}
+
+int int_arg_with_sign_and_percent(struct cmd_context *cmd __attribute__((unused)),
+                                 struct arg_values *av)
+{
+       char *ptr;
+
+       if (!_get_int_arg(av, &ptr))
+               return 0;
+
+       if (!*ptr)
+               return 1;
+
+       if (*ptr++ != '%')
+               return 0;
+
+       if (!strcasecmp(ptr, "V") || !strcasecmp(ptr, "VG"))
+               av->percent = PERCENT_VG;
+       else if (!strcasecmp(ptr, "L") || !strcasecmp(ptr, "LV"))
+               av->percent = PERCENT_LV;
+       else if (!strcasecmp(ptr, "P") || !strcasecmp(ptr, "PV") ||
+                !strcasecmp(ptr, "PVS"))
+               av->percent = PERCENT_PVS;
+       else if (!strcasecmp(ptr, "F") || !strcasecmp(ptr, "FR") ||
+                !strcasecmp(ptr, "FREE"))
+               av->percent = PERCENT_FREE;
+       else if (!strcasecmp(ptr, "O") || !strcasecmp(ptr, "OR") ||
+                !strcasecmp(ptr, "ORIGIN"))
+               av->percent = PERCENT_ORIGIN;
+       else
+               return 0;
+
+       return 1;
+}
+
+int minor_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       char *ptr;
+
+       if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
+               return 0;
+
+       if (av->i_value > 255) {
+               log_error("Minor number outside range 0-255");
+               return 0;
+       }
+
+       return 1;
+}
+
+int major_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       char *ptr;
+
+       if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
+               return 0;
+
+       if (av->i_value > 255) {
+               log_error("Major number outside range 0-255");
+               return 0;
+       }
+
+       /* FIXME Also Check against /proc/devices */
+
+       return 1;
+}
+
+int string_arg(struct cmd_context *cmd __attribute__((unused)),
+              struct arg_values *av __attribute__((unused)))
+{
+       return 1;
+}
+
+int tag_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       char *pos = av->value;
+
+       if (*pos == '@')
+               pos++;
+
+       if (!validate_tag(pos))
+               return 0;
+
+       av->value = pos;
+
+       return 1;
+}
+
+int permission_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       av->sign = SIGN_NONE;
+
+       if ((!strcmp(av->value, "rw")) || (!strcmp(av->value, "wr")))
+               av->ui_value = LVM_READ | LVM_WRITE;
+
+       else if (!strcmp(av->value, "r"))
+               av->ui_value = LVM_READ;
+
+       else
+               return 0;
+
+       return 1;
+}
+
+int alloc_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       alloc_policy_t alloc;
+
+       av->sign = SIGN_NONE;
+
+       alloc = get_alloc_from_string(av->value);
+       if (alloc == ALLOC_INVALID)
+               return 0;
+
+       av->ui_value = (uint32_t) alloc;
+
+       return 1;
+}
+
+int segtype_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+       return get_segtype_from_string(cmd, av->value) ? 1 : 0;
+}
+
+/*
+ * Positive integer, zero or "auto".
+ */
+int readahead_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+       if (!strcasecmp(av->value, "auto")) {
+               av->ui_value = DM_READ_AHEAD_AUTO;
+               return 1;
+       }
+
+       if (!strcasecmp(av->value, "none")) {
+               av->ui_value = DM_READ_AHEAD_NONE;
+               return 1;
+       }
+
+       if (!_size_arg(cmd, av, 1))
+               return 0;
+
+       if (av->sign == SIGN_MINUS)
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Non-zero, positive integer, "all", or "unmanaged"
+ */
+int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+       if (!strncmp(cmd->command->name, "vg", 2)) {
+               if (!strcasecmp(av->value, "all")) {
+                       av->ui_value = VGMETADATACOPIES_ALL;
+                       return 1;
+               }
+
+               if (!strcasecmp(av->value, "unmanaged")) {
+                       av->ui_value = VGMETADATACOPIES_UNMANAGED;
+                       return 1;
+               }
+       }
+
+       return int_arg(cmd, av);
+}
+
+static void __alloc(int size)
+{
+       if (!(_cmdline.commands = dm_realloc(_cmdline.commands, sizeof(*_cmdline.commands) * size))) {
+               log_fatal("Couldn't allocate memory.");
+               exit(ECMD_FAILED);
+       }
+
+       _cmdline.commands_size = size;
+}
+
+static void _alloc_command(void)
+{
+       if (!_cmdline.commands_size)
+               __alloc(32);
+
+       if (_cmdline.commands_size <= _cmdline.num_commands)
+               __alloc(2 * _cmdline.commands_size);
+}
+
+static void _create_new_command(const char *name, command_fn command,
+                               unsigned flags,
+                               const char *desc, const char *usagestr,
+                               int nargs, int *args)
+{
+       struct command *nc;
+
+       _alloc_command();
+
+       nc = _cmdline.commands + _cmdline.num_commands++;
+
+       nc->name = name;
+       nc->desc = desc;
+       nc->usage = usagestr;
+       nc->fn = command;
+       nc->flags = flags;
+       nc->num_args = nargs;
+       nc->valid_args = args;
+}
+
+static void _register_command(const char *name, command_fn fn, const char *desc,
+                             unsigned flags, const char *usagestr, ...)
+{
+       int nargs = 0, i;
+       int *args;
+       va_list ap;
+
+       /* count how many arguments we have */
+       va_start(ap, usagestr);
+       while (va_arg(ap, int) >= 0)
+                nargs++;
+       va_end(ap);
+
+       /* allocate space for them */
+       if (!(args = dm_malloc(sizeof(*args) * nargs))) {
+               log_fatal("Out of memory.");
+               exit(ECMD_FAILED);
+       }
+
+       /* fill them in */
+       va_start(ap, usagestr);
+       for (i = 0; i < nargs; i++)
+               args[i] = va_arg(ap, int);
+       va_end(ap);
+
+       /* enter the command in the register */
+       _create_new_command(name, fn, flags, desc, usagestr, nargs, args);
+}
+
+void lvm_register_commands(void)
+{
+#define xx(a, b, c, d...) _register_command(# a, a, b, c, ## d, \
+                                           driverloaded_ARG, \
+                                           debug_ARG, help_ARG, help2_ARG, \
+                                           version_ARG, verbose_ARG, \
+                                           quiet_ARG, config_ARG, -1);
+#include "commands.h"
+#undef xx
+}
+
+static struct command *_find_command(const char *name)
+{
+       int i;
+       const char *base;
+
+       base = last_path_component(name);
+
+       for (i = 0; i < _cmdline.num_commands; i++) {
+               if (!strcmp(base, _cmdline.commands[i].name))
+                       break;
+       }
+
+       if (i >= _cmdline.num_commands)
+               return 0;
+
+       return _cmdline.commands + i;
+}
+
+static void _short_usage(const char *name)
+{
+       log_error("Run `%s --help' for more information.", name);
+}
+
+static int _usage(const char *name)
+{
+       struct command *com = _find_command(name);
+
+       if (!com) {
+               log_print("%s: no such command.", name);
+               return 0;
+       }
+
+       log_print("%s: %s\n\n%s", com->name, com->desc, com->usage);
+       return 1;
+}
+
+/*
+ * Sets up the short and long argument.  If there
+ * is no short argument then the index of the
+ * argument in the the_args array is set as the
+ * long opt value.  Yuck.  Of course this means we
+ * can't have more than 'a' long arguments.
+ */
+static void _add_getopt_arg(int arg, char **ptr, struct option **o)
+{
+       struct arg_props *a = _cmdline.arg_props + arg;
+
+       if (a->short_arg) {
+               *(*ptr)++ = a->short_arg;
+
+               if (a->fn)
+                       *(*ptr)++ = ':';
+       }
+#ifdef HAVE_GETOPTLONG
+       if (*(a->long_arg + 2)) {
+               (*o)->name = a->long_arg + 2;
+               (*o)->has_arg = a->fn ? 1 : 0;
+               (*o)->flag = NULL;
+               if (a->short_arg)
+                       (*o)->val = a->short_arg;
+               else
+                       (*o)->val = arg;
+               (*o)++;
+       }
+#endif
+}
+
+static int _find_arg(struct command *com, int opt)
+{
+       struct arg_props *a;
+       int i, arg;
+
+       for (i = 0; i < com->num_args; i++) {
+               arg = com->valid_args[i];
+               a = _cmdline.arg_props + arg;
+
+               /*
+                * opt should equal either the
+                * short arg, or the index into
+                * the_args.
+                */
+               if ((a->short_arg && (opt == a->short_arg)) ||
+                   (!a->short_arg && (opt == arg)))
+                       return arg;
+       }
+
+       return -1;
+}
+
+static int _process_command_line(struct cmd_context *cmd, int *argc,
+                                char ***argv)
+{
+       int i, opt, arg;
+       char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str;
+       struct option opts[ARG_COUNT + 1], *o = opts;
+       struct arg_props *a;
+       struct arg_values *av;
+       struct arg_value_group_list *current_group = NULL;
+
+       if (!(cmd->arg_values = dm_pool_zalloc(cmd->mem, sizeof(*cmd->arg_values) * ARG_COUNT))) {
+               log_fatal("Unable to allocate memory for command line arguments.");
+               return 0;
+       }
+
+       /* fill in the short and long opts */
+       for (i = 0; i < cmd->command->num_args; i++)
+               _add_getopt_arg(cmd->command->valid_args[i], &ptr, &o);
+
+       *ptr = '\0';
+       memset(o, 0, sizeof(*o));
+
+       /* initialise getopt_long & scan for command line switches */
+       optarg = 0;
+       optind = OPTIND_INIT;
+       while ((opt = GETOPTLONG_FN(*argc, *argv, str, opts, NULL)) >= 0) {
+
+               if (opt == '?')
+                       return 0;
+
+               if ((arg = _find_arg(cmd->command, opt)) < 0) {
+                       log_fatal("Unrecognised option.");
+                       return 0;
+               }
+
+               a = _cmdline.arg_props + arg;
+
+               av = &cmd->arg_values[arg];
+
+               if (a->flags & ARG_GROUPABLE) {
+                       /* Start a new group of arguments the first time or if a non-countable argument is repeated. */
+                       if (!current_group || (current_group->arg_values[arg].count && !(a->flags & ARG_COUNTABLE))) {
+                               /* FIXME Reduce size including only groupable args */
+                               if (!(current_group = dm_pool_zalloc(cmd->mem, sizeof(struct arg_value_group_list) + sizeof(*cmd->arg_values) * ARG_COUNT))) {
+                                       log_fatal("Unable to allocate memory for command line arguments.");
+                                       return 0;
+                               }
+
+                               dm_list_add(&cmd->arg_value_groups, &current_group->list);
+                       }
+                       /* Maintain total argument count as well as count within each group */
+                       av->count++;
+                       av = &current_group->arg_values[arg];
+               }
+
+               if (av->count && !(a->flags & ARG_COUNTABLE)) {
+                       log_error("Option%s%c%s%s may not be repeated.",
+                                 a->short_arg ? " -" : "",
+                                 a->short_arg ? : ' ',
+                                 (a->short_arg && a->long_arg) ?
+                                 "/" : "", a->long_arg ? : "");
+                       return 0;
+               }
+
+               if (a->fn) {
+                       if (!optarg) {
+                               log_error("Option requires argument.");
+                               return 0;
+                       }
+
+                       av->value = optarg;
+
+                       if (!a->fn(cmd, av)) {
+                               log_error("Invalid argument for %s: %s", a->long_arg, optarg);
+                               return 0;
+                       }
+               }
+
+               av->count++;
+       }
+
+       *argc -= optind;
+       *argv += optind;
+       return 1;
+}
+
+static int _merge_synonym(struct cmd_context *cmd, int oldarg, int newarg)
+{
+       const struct arg_values *old;
+       struct arg_values *new;
+
+       if (arg_count(cmd, oldarg) && arg_count(cmd, newarg)) {
+               log_error("%s and %s are synonyms.  Please only supply one.",
+                         _cmdline.arg_props[oldarg].long_arg, _cmdline.arg_props[newarg].long_arg);
+               return 0;
+       }
+
+       if (!arg_count(cmd, oldarg))
+               return 1;
+
+       old = cmd->arg_values + oldarg;
+       new = cmd->arg_values + newarg;
+
+       new->count = old->count;
+       new->value = old->value;
+       new->i_value = old->i_value;
+       new->ui_value = old->ui_value;
+       new->i64_value = old->i64_value;
+       new->ui64_value = old->ui64_value;
+       new->sign = old->sign;
+
+       return 1;
+}
+
+int version(struct cmd_context *cmd __attribute__((unused)),
+           int argc __attribute__((unused)),
+           char **argv __attribute__((unused)))
+{
+       char vsn[80];
+
+       log_print("LVM version:     %s", LVM_VERSION);
+       if (library_version(vsn, sizeof(vsn)))
+               log_print("Library version: %s", vsn);
+       if (driver_version(vsn, sizeof(vsn)))
+               log_print("Driver version:  %s", vsn);
+
+       return ECMD_PROCESSED;
+}
+
+static int _get_settings(struct cmd_context *cmd)
+{
+       cmd->current_settings = cmd->default_settings;
+
+       if (arg_count(cmd, debug_ARG))
+               cmd->current_settings.debug = _LOG_FATAL +
+                   (arg_count(cmd, debug_ARG) - 1);
+
+       if (arg_count(cmd, verbose_ARG))
+               cmd->current_settings.verbose = arg_count(cmd, verbose_ARG);
+
+       if (arg_count(cmd, quiet_ARG)) {
+               cmd->current_settings.debug = 0;
+               cmd->current_settings.verbose = 0;
+       }
+
+       if (arg_count(cmd, test_ARG))
+               cmd->current_settings.test = arg_count(cmd, test_ARG);
+
+       if (arg_count(cmd, driverloaded_ARG)) {
+               cmd->current_settings.activation =
+                   arg_int_value(cmd, driverloaded_ARG,
+                                 cmd->default_settings.activation);
+       }
+
+       cmd->current_settings.archive = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.archive);
+       cmd->current_settings.backup = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.backup);
+       cmd->current_settings.cache_vgmetadata = cmd->command->flags & CACHE_VGMETADATA ? 1 : 0;
+       cmd->partial_activation = 0;
+
+       if (arg_count(cmd, partial_ARG)) {
+               cmd->partial_activation = 1;
+               log_print("Partial mode. Incomplete logical volumes will be processed.");
+       }
+
+       if (arg_count(cmd, ignorelockingfailure_ARG) || arg_count(cmd, sysinit_ARG))
+               init_ignorelockingfailure(1);
+       else
+               init_ignorelockingfailure(0);
+
+       if (arg_count(cmd, nosuffix_ARG))
+               cmd->current_settings.suffix = 0;
+
+       if (arg_count(cmd, units_ARG))
+               if (!(cmd->current_settings.unit_factor =
+                     units_to_bytes(arg_str_value(cmd, units_ARG, ""),
+                                    &cmd->current_settings.unit_type))) {
+                       log_error("Invalid units specification");
+                       return EINVALID_CMD_LINE;
+               }
+
+       if (arg_count(cmd, trustcache_ARG)) {
+               if (arg_count(cmd, all_ARG)) {
+                       log_error("--trustcache is incompatible with --all");
+                       return EINVALID_CMD_LINE;
+               }
+               init_trust_cache(1);
+               log_warn("WARNING: Cache file of PVs will be trusted.  "
+                         "New devices holding PVs may get ignored.");
+       } else
+               init_trust_cache(0);
+
+       if (arg_count(cmd, noudevsync_ARG))
+               cmd->current_settings.udev_sync = 0;
+
+       /* Handle synonyms */
+       if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) ||
+           !_merge_synonym(cmd, allocation_ARG, allocatable_ARG) ||
+           !_merge_synonym(cmd, allocation_ARG, resizeable_ARG) ||
+           !_merge_synonym(cmd, virtualoriginsize_ARG, virtualsize_ARG))
+               return EINVALID_CMD_LINE;
+
+       if ((!strncmp(cmd->command->name, "pv", 2) &&
+           !_merge_synonym(cmd, metadatacopies_ARG, pvmetadatacopies_ARG)) ||
+           (!strncmp(cmd->command->name, "vg", 2) &&
+            !_merge_synonym(cmd, metadatacopies_ARG, vgmetadatacopies_ARG)))
+               return EINVALID_CMD_LINE;
+
+       /* Zero indicates success */
+       return 0;
+}
+
+static int _process_common_commands(struct cmd_context *cmd)
+{
+       if (arg_count(cmd, help_ARG) || arg_count(cmd, help2_ARG)) {
+               _usage(cmd->command->name);
+               return ECMD_PROCESSED;
+       }
+
+       if (arg_count(cmd, version_ARG)) {
+               return version(cmd, 0, (char **) NULL);
+       }
+
+       /* Zero indicates it's OK to continue processing this command */
+       return 0;
+}
+
+static void _display_help(void)
+{
+       int i;
+
+       log_error("Available lvm commands:");
+       log_error("Use 'lvm help <command>' for more information");
+       log_error(" ");
+
+       for (i = 0; i < _cmdline.num_commands; i++) {
+               struct command *com = _cmdline.commands + i;
+
+               log_error("%-16.16s%s", com->name, com->desc);
+       }
+}
+
+int help(struct cmd_context *cmd __attribute__((unused)), int argc, char **argv)
+{
+       int ret = ECMD_PROCESSED;
+
+       if (!argc)
+               _display_help();
+       else {
+               int i;
+               for (i = 0; i < argc; i++)
+                       if (!_usage(argv[i]))
+                               ret = EINVALID_CMD_LINE;
+       }
+
+       return ret;
+}
+
+static void _apply_settings(struct cmd_context *cmd)
+{
+       init_debug(cmd->current_settings.debug);
+       init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
+       init_test(cmd->current_settings.test);
+       init_full_scan_done(0);
+       init_mirror_in_sync(0);
+
+       init_msg_prefix(cmd->default_settings.msg_prefix);
+       init_cmd_name(cmd->default_settings.cmd_name);
+
+       archive_enable(cmd, cmd->current_settings.archive);
+       backup_enable(cmd, cmd->current_settings.backup);
+
+       set_activation(cmd->current_settings.activation);
+
+       cmd->fmt = get_format_by_name(cmd, arg_str_value(cmd, metadatatype_ARG,
+                                     cmd->current_settings.fmt_name));
+
+       cmd->handles_missing_pvs = 0;
+}
+
+static int _set_udev_checking(struct cmd_context *cmd)
+{
+#ifdef UDEV_SYNC_SUPPORT
+       struct udev *udev;
+       const char *udev_dev_dir;
+       size_t udev_dev_dir_len;
+       int dirs_diff;
+
+       if (!(udev = udev_new()) ||
+           !(udev_dev_dir = udev_get_dev_path(udev)) ||
+           !*udev_dev_dir) {
+               log_error("Could not get udev dev path.");
+               return 0;
+       }
+       udev_dev_dir_len = strlen(udev_dev_dir);
+
+       /* There's always a slash at the end of dev_dir. But check udev_dev_dir! */
+       if (udev_dev_dir[udev_dev_dir_len - 1] != '/')
+               dirs_diff = strncmp(cmd->dev_dir, udev_dev_dir,
+                                   udev_dev_dir_len);
+       else
+               dirs_diff = strcmp(cmd->dev_dir, udev_dev_dir);
+
+       if (dirs_diff) {
+               log_debug("The path %s used for creating device nodes and "
+                         "symlinks that is set in the configuration differs "
+                         "from the path %s that is used by udev. All warnings "
+                         "about udev not working correctly while processing "
+                         "particular nodes and symlinks will be suppressed. "
+                         "These nodes and symlinks will be managed in each "
+                         "directory separately.",
+                          cmd->dev_dir, udev_dev_dir);
+               dm_udev_set_checking(0);
+               init_udev_checking(0);
+       }
+
+       udev_unref(udev);
+#endif
+       return 1;
+}
+
+static const char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv)
+{
+       int i, space;
+
+       /*
+        * Build up the complete command line, used as a
+        * description for backups.
+        */
+       if (!dm_pool_begin_object(cmd->mem, 128))
+               goto_bad;
+
+       for (i = 0; i < argc; i++) {
+               space = strchr(argv[i], ' ') ? 1 : 0;
+
+               if (space && !dm_pool_grow_object(cmd->mem, "'", 1))
+                       goto_bad;
+
+               if (!dm_pool_grow_object(cmd->mem, argv[i], strlen(argv[i])))
+                       goto_bad;
+
+               if (space && !dm_pool_grow_object(cmd->mem, "'", 1))
+                       goto_bad;
+
+               if (i < (argc - 1))
+                       if (!dm_pool_grow_object(cmd->mem, " ", 1))
+                               goto_bad;
+       }
+
+       /*
+        * Terminate.
+        */
+       if (!dm_pool_grow_object(cmd->mem, "\0", 1))
+               goto_bad;
+
+       return dm_pool_end_object(cmd->mem);
+
+      bad:
+       log_error("Couldn't copy command line.");
+       dm_pool_abandon_object(cmd->mem);
+       return NULL;
+}
+
+int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
+{
+       int ret = 0;
+       int locking_type;
+
+       init_error_message_produced(0);
+
+       /* each command should start out with sigint flag cleared */
+       sigint_clear();
+
+       if (!(cmd->cmd_line = _copy_command_line(cmd, argc, argv))) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       log_debug("Parsing: %s", cmd->cmd_line);
+
+       if (!(cmd->command = _find_command(argv[0])))
+               return ENO_SUCH_CMD;
+
+       if (!_process_command_line(cmd, &argc, &argv)) {
+               log_error("Error during parsing of command line.");
+               return EINVALID_CMD_LINE;
+       }
+
+       set_cmd_name(cmd->command->name);
+
+       if (arg_count(cmd, config_ARG))
+               if (override_config_tree_from_string(cmd,
+                   arg_str_value(cmd, config_ARG, ""))) {
+                       ret = EINVALID_CMD_LINE;
+                       goto_out;
+               }
+
+       if (arg_count(cmd, config_ARG) || !cmd->config_valid || config_files_changed(cmd)) {
+               /* Reinitialise various settings inc. logging, filters */
+               if (!refresh_toolcontext(cmd)) {
+                       if (cmd->cft_override) {
+                               destroy_config_tree(cmd->cft_override);
+                               cmd->cft_override = NULL;
+                       }
+                       log_error("Updated config file invalid. Aborting.");
+                       return ECMD_FAILED;
+               }
+       }
+
+       if ((ret = _get_settings(cmd)))
+               goto_out;
+       _apply_settings(cmd);
+
+       log_debug("Processing: %s", cmd->cmd_line);
+
+#ifdef O_DIRECT_SUPPORT
+       log_debug("O_DIRECT will be used");
+#endif
+
+       if (!_set_udev_checking(cmd))
+               goto_out;
+
+       if ((ret = _process_common_commands(cmd)))
+               goto_out;
+
+       if (cmd->metadata_read_only &&
+           !(cmd->command->flags & PERMITTED_READ_ONLY)) {
+               log_error("%s: Command not permitted while global/metadata_read_only "
+                         "is set.", cmd->cmd_line);
+               goto out;
+       }
+
+       if (arg_count(cmd, nolocking_ARG))
+               locking_type = 0;
+       else
+               locking_type = -1;
+
+       if (!init_locking(locking_type, cmd, arg_count(cmd, sysinit_ARG))) {
+               ret = ECMD_FAILED;
+               goto out;
+       }
+
+       ret = cmd->command->fn(cmd, argc, argv);
+
+       fin_locking();
+
+      out:
+       if (test_mode()) {
+               log_verbose("Test mode: Wiping internal cache");
+               lvmcache_destroy(cmd, 1);
+       }
+
+       if (cmd->cft_override) {
+               destroy_config_tree(cmd->cft_override);
+               cmd->cft_override = NULL;
+               /* Move this? */
+               if (!refresh_toolcontext(cmd))
+                       stack;
+       }
+
+       /* FIXME Move this? */
+       cmd->current_settings = cmd->default_settings;
+       _apply_settings(cmd);
+
+       if (ret == EINVALID_CMD_LINE && !_cmdline.interactive)
+               _short_usage(cmd->command->name);
+
+       log_debug("Completed: %s", cmd->cmd_line);
+
+       /*
+        * free off any memory the command used.
+        */
+       dm_list_init(&cmd->arg_value_groups);
+       dm_pool_empty(cmd->mem);
+
+       reset_lvm_errno(1);
+       reset_log_duplicated();
+
+       return ret;
+}
+
+int lvm_return_code(int ret)
+{
+       return (ret == ECMD_PROCESSED ? 0 : ret);
+}
+
+int lvm_split(char *str, int *argc, char **argv, int max)
+{
+       char *b = str, *e;
+       *argc = 0;
+
+       while (*b) {
+               while (*b && isspace(*b))
+                       b++;
+
+               if ((!*b) || (*b == '#'))
+                       break;
+
+               e = b;
+               while (*e && !isspace(*e))
+                       e++;
+
+               argv[(*argc)++] = b;
+               if (!*e)
+                       break;
+               *e++ = '\0';
+               b = e;
+               if (*argc == max)
+                       break;
+       }
+
+       return *argc;
+}
+
+static const char *_get_cmdline(pid_t pid)
+{
+       static char _proc_cmdline[32];
+       char buf[256];
+       int fd, n = 0;
+
+       snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/%u/cmdline", pid);
+       /* FIXME Use generic read code. */
+       if ((fd = open(buf, O_RDONLY)) > 0) {
+               if ((n = read(fd, _proc_cmdline, sizeof(_proc_cmdline) - 1)) < 0) {
+                       log_sys_error("read", buf);
+                       n = 0;
+               }
+               if (close(fd))
+                       log_sys_error("close", buf);
+       }
+       _proc_cmdline[n] = '\0';
+
+       return _proc_cmdline;
+}
+
+static const char *_get_filename(int fd)
+{
+       static char filename[PATH_MAX];
+       char buf[32];   /* Assumes short DEFAULT_PROC_DIR */
+       int size;
+
+       snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/self/fd/%u", fd);
+
+       if ((size = readlink(buf, filename, sizeof(filename) - 1)) == -1)
+               filename[0] = '\0';
+       else
+               filename[size] = '\0';
+
+       return filename;
+}
+
+static void _close_descriptor(int fd, unsigned suppress_warnings,
+                             const char *command, pid_t ppid,
+                             const char *parent_cmdline)
+{
+       int r;
+       const char *filename;
+
+       /* Ignore bad file descriptors */
+       if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
+               return;
+
+       if (!suppress_warnings)
+               filename = _get_filename(fd);
+
+       r = close(fd);
+       if (suppress_warnings)
+               return;
+
+       if (!r)
+               fprintf(stderr, "File descriptor %d (%s) leaked on "
+                       "%s invocation.", fd, filename, command);
+       else if (errno == EBADF)
+               return;
+       else
+               fprintf(stderr, "Close failed on stray file descriptor "
+                       "%d (%s): %s", fd, filename, strerror(errno));
+
+       fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline);
+}
+
+static void _close_stray_fds(const char *command)
+{
+       struct rlimit rlim;
+       int fd;
+       unsigned suppress_warnings = 0;
+       pid_t ppid = getppid();
+       const char *parent_cmdline = _get_cmdline(ppid);
+
+       if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+               fprintf(stderr, "getrlimit(RLIMIT_NOFILE) failed: %s\n",
+                       strerror(errno));
+               return;
+       }
+
+       if (getenv("LVM_SUPPRESS_FD_WARNINGS"))
+               suppress_warnings = 1;
+
+       for (fd = 3; fd < rlim.rlim_cur; fd++)
+               _close_descriptor(fd, suppress_warnings, command, ppid,
+                                 parent_cmdline);
+}
+
+struct cmd_context *init_lvm(void)
+{
+       struct cmd_context *cmd;
+
+       if (!(cmd = create_toolcontext(0, NULL)))
+               return_NULL;
+
+       _cmdline.arg_props = &_arg_props[0];
+
+       if (stored_errno()) {
+               destroy_toolcontext(cmd);
+               return_NULL;
+       }
+
+       return cmd;
+}
+
+static void _fin_commands(void)
+{
+       int i;
+
+       for (i = 0; i < _cmdline.num_commands; i++)
+               dm_free(_cmdline.commands[i].valid_args);
+
+       dm_free(_cmdline.commands);
+
+       _cmdline.commands = NULL;
+       _cmdline.num_commands = 0;
+       _cmdline.commands_size = 0;
+}
+
+void lvm_fin(struct cmd_context *cmd)
+{
+       _fin_commands();
+       destroy_toolcontext(cmd);
+}
+
+static int _run_script(struct cmd_context *cmd, int argc, char **argv)
+{
+       FILE *script;
+
+       char buffer[CMD_LEN];
+       int ret = 0;
+       int magic_number = 0;
+       char *script_file = argv[0];
+
+       if ((script = fopen(script_file, "r")) == NULL)
+               return ENO_SUCH_CMD;
+
+       while (fgets(buffer, sizeof(buffer), script) != NULL) {
+               if (!magic_number) {
+                       if (buffer[0] == '#' && buffer[1] == '!')
+                               magic_number = 1;
+                       else {
+                               ret = ENO_SUCH_CMD;
+                               break;
+                       }
+               }
+               if ((strlen(buffer) == sizeof(buffer) - 1)
+                   && (buffer[sizeof(buffer) - 1] - 2 != '\n')) {
+                       buffer[50] = '\0';
+                       log_error("Line too long (max 255) beginning: %s",
+                                 buffer);
+                       ret = EINVALID_CMD_LINE;
+                       break;
+               }
+               if (lvm_split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+                       buffer[50] = '\0';
+                       log_error("Too many arguments: %s", buffer);
+                       ret = EINVALID_CMD_LINE;
+                       break;
+               }
+               if (!argc)
+                       continue;
+               if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit"))
+                       break;
+               ret = lvm_run_command(cmd, argc, argv);
+               if (ret != ECMD_PROCESSED) {
+                       if (!error_message_produced()) {
+                               log_debug(INTERNAL_ERROR "Failed command did not use log_error");
+                               log_error("Command failed with status code %d.", ret);
+                       }
+                       break;
+               }
+       }
+
+       if (fclose(script))
+               log_sys_error("fclose", script_file);
+
+       return ret;
+}
+
+/*
+ * Determine whether we should fall back and exec the equivalent LVM1 tool
+ */
+static int _lvm1_fallback(struct cmd_context *cmd)
+{
+       char vsn[80];
+       int dm_present;
+
+       if (!find_config_tree_int(cmd, "global/fallback_to_lvm1",
+                            DEFAULT_FALLBACK_TO_LVM1) ||
+           strncmp(cmd->kernel_vsn, "2.4.", 4))
+               return 0;
+
+       log_suppress(1);
+       dm_present = driver_version(vsn, sizeof(vsn));
+       log_suppress(0);
+
+       if (dm_present || !lvm1_present(cmd))
+               return 0;
+
+       return 1;
+}
+
+static void _exec_lvm1_command(char **argv)
+{
+       char path[PATH_MAX];
+
+       if (dm_snprintf(path, sizeof(path), "%s.lvm1", argv[0]) < 0) {
+               log_error("Failed to create LVM1 tool pathname");
+               return;
+       }
+
+       execvp(path, argv);
+       log_sys_error("execvp", path);
+}
+
+static void _nonroot_warning(void)
+{
+       if (getuid() || geteuid())
+               log_warn("WARNING: Running as a non-root user. Functionality may be unavailable.");
+}
+
+int lvm2_main(int argc, char **argv)
+{
+       const char *base;
+       int ret, alias = 0;
+       struct cmd_context *cmd;
+
+       base = last_path_component(argv[0]);
+       if (strcmp(base, "lvm") && strcmp(base, "lvm.static") &&
+           strcmp(base, "initrd-lvm"))
+               alias = 1;
+
+       _close_stray_fds(base);
+
+       if (is_static() && strcmp(base, "lvm.static") &&
+           path_exists(LVM_SHARED_PATH) &&
+           !getenv("LVM_DID_EXEC")) {
+               setenv("LVM_DID_EXEC", base, 1);
+               execvp(LVM_SHARED_PATH, argv);
+               unsetenv("LVM_DID_EXEC");
+       }
+
+       /* "version" command is simple enough so it doesn't need any complex init */
+       if (!alias && argc > 1 && !strcmp(argv[1], "version"))
+               return lvm_return_code(version(NULL, argc, argv));
+
+       if (!(cmd = init_lvm()))
+               return -1;
+
+       cmd->argv = argv;
+       lvm_register_commands();
+
+       if (_lvm1_fallback(cmd)) {
+               /* Attempt to run equivalent LVM1 tool instead */
+               if (!alias) {
+                       argv++;
+                       argc--;
+               }
+               if (!argc) {
+                       log_error("Falling back to LVM1 tools, but no "
+                                 "command specified.");
+                       ret = ECMD_FAILED;
+                       goto out;
+               }
+               _exec_lvm1_command(argv);
+               ret = ECMD_FAILED;
+               goto out;
+       }
+#ifdef READLINE_SUPPORT
+       if (!alias && argc == 1) {
+               _nonroot_warning();
+               ret = lvm_shell(cmd, &_cmdline);
+               goto out;
+       }
+#endif
+
+       if (!alias) {
+               if (argc < 2) {
+                       log_fatal("Please supply an LVM command.");
+                       _display_help();
+                       ret = EINVALID_CMD_LINE;
+                       goto out;
+               }
+
+               argc--;
+               argv++;
+       }
+
+       _nonroot_warning();
+       ret = lvm_run_command(cmd, argc, argv);
+       if ((ret == ENO_SUCH_CMD) && (!alias))
+               ret = _run_script(cmd, argc, argv);
+       if (ret == ENO_SUCH_CMD)
+               log_error("No such command.  Try 'help'.");
+
+       if ((ret != ECMD_PROCESSED) && !error_message_produced()) {
+               log_debug(INTERNAL_ERROR "Failed command did not use log_error");
+               log_error("Command failed with status code %d.", ret);
+       }
+
+      out:
+       lvm_fin(cmd);
+       return lvm_return_code(ret);
+}
diff --git a/tools/lvmdiskscan.c b/tools/lvmdiskscan.c
new file mode 100644 (file)
index 0000000..9127c15
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+/*
+ * Changelog
+ *
+ *   05/02/2002 - First drop [HM]
+ */
+
+#include "tools.h"
+
+int disks_found;
+int parts_found;
+int pv_disks_found;
+int pv_parts_found;
+int max_len;
+
+static int _get_max_dev_name_len(struct dev_filter *filter)
+{
+       int len = 0;
+       int maxlen = 0;
+       struct dev_iter *iter;
+       struct device *dev;
+
+       if (!(iter = dev_iter_create(filter, 1))) {
+               log_error("dev_iter_create failed");
+               return 0;
+       }
+
+       /* Do scan */
+       for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
+               len = strlen(dev_name(dev));
+               if (len > maxlen)
+                       maxlen = len;
+       }
+       dev_iter_destroy(iter);
+
+       return maxlen;
+}
+
+static void _count(struct device *dev, int *disks, int *parts)
+{
+       int c = dev_name(dev)[strlen(dev_name(dev)) - 1];
+
+       if (!isdigit(c))
+               (*disks)++;
+       else
+               (*parts)++;
+}
+
+static void _print(struct cmd_context *cmd, const struct device *dev,
+                  uint64_t size, const char *what)
+{
+       log_print("%-*s [%15s] %s", max_len, dev_name(dev),
+                 display_size(cmd, size), what ? : "");
+}
+
+static int _check_device(struct cmd_context *cmd, struct device *dev)
+{
+       char buffer;
+       uint64_t size;
+
+       if (!dev_open(dev)) {
+               return 0;
+       }
+       if (!dev_read(dev, UINT64_C(0), (size_t) 1, &buffer)) {
+               dev_close(dev);
+               return 0;
+       }
+       if (!dev_get_size(dev, &size)) {
+               log_error("Couldn't get size of \"%s\"", dev_name(dev));
+       }
+       _print(cmd, dev, size, NULL);
+       _count(dev, &disks_found, &parts_found);
+       if (!dev_close(dev)) {
+               log_error("dev_close on \"%s\" failed", dev_name(dev));
+               return 0;
+       }
+       return 1;
+}
+
+int lvmdiskscan(struct cmd_context *cmd, int argc __attribute__((unused)),
+               char **argv __attribute__((unused)))
+{
+       uint64_t size;
+       struct dev_iter *iter;
+       struct device *dev;
+       struct label *label;
+
+       /* initialise these here to avoid problems with the lvm shell */
+       disks_found = 0;
+       parts_found = 0;
+       pv_disks_found = 0;
+       pv_parts_found = 0;
+
+       if (arg_count(cmd, lvmpartition_ARG))
+               log_warn("WARNING: only considering LVM devices");
+
+       max_len = _get_max_dev_name_len(cmd->filter);
+
+       if (!(iter = dev_iter_create(cmd->filter, 0))) {
+               log_error("dev_iter_create failed");
+               return ECMD_FAILED;
+       }
+
+       /* Do scan */
+       for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
+               /* Try if it is a PV first */
+               if ((label_read(dev, &label, UINT64_C(0)))) {
+                       if (!dev_get_size(dev, &size)) {
+                               log_error("Couldn't get size of \"%s\"",
+                                         dev_name(dev));
+                               continue;
+                       }
+                       _print(cmd, dev, size, "LVM physical volume");
+                       _count(dev, &pv_disks_found, &pv_parts_found);
+                       continue;
+               }
+               /* If user just wants PVs we are done */
+               if (arg_count(cmd, lvmpartition_ARG))
+                       continue;
+
+               /* What other device is it? */
+               if (!_check_device(cmd, dev))
+                       continue;
+       }
+       dev_iter_destroy(iter);
+
+       /* Display totals */
+       if (!arg_count(cmd, lvmpartition_ARG)) {
+               log_print("%d disk%s",
+                         disks_found, disks_found == 1 ? "" : "s");
+               log_print("%d partition%s",
+                         parts_found, parts_found == 1 ? "" : "s");
+       }
+       log_print("%d LVM physical volume whole disk%s",
+                 pv_disks_found, pv_disks_found == 1 ? "" : "s");
+       log_print("%d LVM physical volume%s",
+                 pv_parts_found, pv_parts_found == 1 ? "" : "s");
+
+       return ECMD_PROCESSED;
+}
diff --git a/tools/lvreduce.c b/tools/lvreduce.c
new file mode 100644 (file)
index 0000000..92857f7
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int lvreduce(struct cmd_context *cmd, int argc, char **argv)
+{
+       return lvresize(cmd, argc, argv);
+}
diff --git a/tools/lvremove.c b/tools/lvremove.c
new file mode 100644 (file)
index 0000000..0252149
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
+                          void *handle __attribute__((unused)))
+{
+       struct logical_volume *origin;
+
+       /*
+        * If this is a sparse device, remove its origin too.
+        */
+        if (lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv)))
+                lv = origin;
+
+       if (!lv_remove_with_dependencies(cmd, lv, arg_count(cmd, force_ARG), 0)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int lvremove(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (!argc) {
+               log_error("Please enter one or more logical volume paths");
+               return EINVALID_CMD_LINE;
+       }
+
+       cmd->handles_missing_pvs = 1;
+
+       return process_each_lv(cmd, argc, argv, READ_FOR_UPDATE, NULL,
+                              &lvremove_single);
+}
diff --git a/tools/lvrename.c b/tools/lvrename.c
new file mode 100644 (file)
index 0000000..db47a8b
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "lvm-types.h"
+
+
+/*
+ * lvrename command implementation.
+ * Check arguments and call lv_rename() to execute the request.
+ */
+int lvrename(struct cmd_context *cmd, int argc, char **argv)
+{
+       size_t maxlen;
+       char *lv_name_old, *lv_name_new;
+       const char *vg_name, *vg_name_new, *vg_name_old;
+       char *st;
+       int r = ECMD_FAILED;
+
+       struct volume_group *vg = NULL;
+       struct lv_list *lvl;
+
+       if (argc == 3) {
+               vg_name = skip_dev_dir(cmd, argv[0], NULL);
+               lv_name_old = argv[1];
+               lv_name_new = argv[2];
+               if (strchr(lv_name_old, '/') &&
+                   (vg_name_old = extract_vgname(cmd, lv_name_old)) &&
+                   strcmp(vg_name_old, vg_name)) {
+                       log_error("Please use a single volume group name "
+                                 "(\"%s\" or \"%s\")", vg_name, vg_name_old);
+                       return EINVALID_CMD_LINE;
+               }
+       } else if (argc == 2) {
+               lv_name_old = argv[0];
+               lv_name_new = argv[1];
+               vg_name = extract_vgname(cmd, lv_name_old);
+       } else {
+               log_error("Old and new logical volume names required");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (!validate_name(vg_name)) {
+               log_error("Please provide a valid volume group name");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (strchr(lv_name_new, '/') &&
+           (vg_name_new = extract_vgname(cmd, lv_name_new)) &&
+           strcmp(vg_name, vg_name_new)) {
+               log_error("Logical volume names must "
+                         "have the same volume group (\"%s\" or \"%s\")",
+                         vg_name, vg_name_new);
+               return EINVALID_CMD_LINE;
+       }
+
+       if ((st = strrchr(lv_name_old, '/')))
+               lv_name_old = st + 1;
+
+       if ((st = strrchr(lv_name_new, '/')))
+               lv_name_new = st + 1;
+
+       /* Check sanity of new name */
+       maxlen = NAME_LEN - strlen(vg_name) - strlen(cmd->dev_dir) - 3;
+       if (strlen(lv_name_new) > maxlen) {
+               log_error("New logical volume path exceeds maximum length "
+                         "of %" PRIsize_t "!", maxlen);
+               return ECMD_FAILED;
+       }
+
+       if (!*lv_name_new) {
+               log_error("New logical volume name may not be blank");
+               return ECMD_FAILED;
+       }
+
+       if (!apply_lvname_restrictions(lv_name_new)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!validate_name(lv_name_new)) {
+               log_error("New logical volume name \"%s\" is invalid",
+                    lv_name_new);
+               return EINVALID_CMD_LINE;
+       }
+
+       if (!strcmp(lv_name_old, lv_name_new)) {
+               log_error("Old and new logical volume names must differ");
+               return EINVALID_CMD_LINE;
+       }
+
+       log_verbose("Checking for existing volume group \"%s\"", vg_name);
+       vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!(lvl = find_lv_in_vg(vg, lv_name_old))) {
+               log_error("Existing logical volume \"%s\" not found in "
+                         "volume group \"%s\"", lv_name_old, vg_name);
+               goto error;
+       }
+
+       if (!lv_rename(cmd, lvl->lv, lv_name_new))
+               goto error;
+
+       log_print("Renamed \"%s\" to \"%s\" in volume group \"%s\"",
+                 lv_name_old, lv_name_new, vg_name);
+
+       r = ECMD_PROCESSED;
+error:
+       unlock_and_free_vg(cmd, vg, vg_name);
+       return r;
+}
diff --git a/tools/lvresize.c b/tools/lvresize.c
new file mode 100644 (file)
index 0000000..c970f3f
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+#define SIZE_BUF 128
+
+struct lvresize_params {
+       const char *vg_name;
+       const char *lv_name;
+
+       uint32_t stripes;
+       uint32_t stripe_size;
+       uint32_t mirrors;
+
+       const struct segment_type *segtype;
+
+       /* size */
+       uint32_t extents;
+       uint64_t size;
+       sign_t sign;
+       percent_type_t percent;
+
+       enum {
+               LV_ANY = 0,
+               LV_REDUCE = 1,
+               LV_EXTEND = 2
+       } resize;
+
+       int resizefs;
+       int nofsck;
+
+       int argc;
+       char **argv;
+};
+
+static int _validate_stripesize(struct cmd_context *cmd,
+                               const struct volume_group *vg,
+                               struct lvresize_params *lp)
+{
+       if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
+               log_error("Stripesize may not be negative.");
+               return 0;
+       }
+
+       if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
+               log_error("Stripe size cannot be larger than %s",
+                         display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
+               return 0;
+       }
+
+       if (!(vg->fid->fmt->features & FMT_SEGMENTS))
+               log_warn("Varied stripesize not supported. Ignoring.");
+       else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) {
+               log_error("Reducing stripe size %s to maximum, "
+                         "physical extent size %s",
+                         display_size(cmd,
+                                      (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)),
+                         display_size(cmd, (uint64_t) vg->extent_size));
+               lp->stripe_size = vg->extent_size;
+       } else
+               lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
+
+       if (lp->stripe_size & (lp->stripe_size - 1)) {
+               log_error("Stripe size must be power of 2");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _request_confirmation(struct cmd_context *cmd,
+                                const struct volume_group *vg,
+                                const struct logical_volume *lv,
+                                const struct lvresize_params *lp)
+{
+       struct lvinfo info;
+
+       memset(&info, 0, sizeof(info));
+
+       if (!lv_info(cmd, lv, 0, &info, 1, 0) && driver_version(NULL, 0)) {
+               log_error("lv_info failed: aborting");
+               return 0;
+       }
+
+       if (lp->resizefs) {
+               if (!info.exists) {
+                       log_error("Logical volume %s must be activated "
+                                 "before resizing filesystem", lp->lv_name);
+                       return 0;
+               }
+               return 1;
+       }
+
+       if (!info.exists)
+               return 1;
+
+       log_warn("WARNING: Reducing active%s logical volume to %s",
+                info.open_count ? " and open" : "",
+                display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
+
+       log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
+
+       if (!arg_count(cmd, force_ARG)) {
+               if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ",
+                                 lp->lv_name) == 'n') {
+                       log_error("Logical volume %s NOT reduced", lp->lv_name);
+                       return 0;
+               }
+               if (sigint_caught())
+                       return 0;
+       }
+
+       return 1;
+}
+
+enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
+#define FSADM_CMD "fsadm"
+#define FSADM_CMD_MAX_ARGS 6
+#define FSADM_CHECK_FAILS_FOR_MOUNTED 3 /* shell exist status code */
+
+/*
+ * FSADM_CMD --dry-run --verbose --force check lv_path
+ * FSADM_CMD --dry-run --verbose --force resize lv_path size
+ */
+static int _fsadm_cmd(struct cmd_context *cmd,
+                     const struct volume_group *vg,
+                     const struct lvresize_params *lp,
+                     enum fsadm_cmd_e fcmd,
+                     int *status)
+{
+       char lv_path[PATH_MAX];
+       char size_buf[SIZE_BUF];
+       const char *argv[FSADM_CMD_MAX_ARGS + 2];
+       unsigned i = 0;
+
+       argv[i++] = FSADM_CMD;
+
+       if (test_mode())
+               argv[i++] = "--dry-run";
+
+       if (verbose_level() >= _LOG_NOTICE)
+               argv[i++] = "--verbose";
+
+       if (arg_count(cmd, force_ARG))
+               argv[i++] = "--force";
+
+       argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
+
+       if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
+                       lp->lv_name) < 0) {
+               log_error("Couldn't create LV path for %s", lp->lv_name);
+               return 0;
+       }
+
+       argv[i++] = lv_path;
+
+       if (fcmd == FSADM_CMD_RESIZE) {
+               if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K",
+                               (uint64_t) lp->extents * vg->extent_size / 2) < 0) {
+                       log_error("Couldn't generate new LV size string");
+                       return 0;
+               }
+
+               argv[i++] = size_buf;
+       }
+
+       argv[i] = NULL;
+
+       return exec_cmd(cmd, argv, status);
+}
+
+static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
+                           struct lvresize_params *lp)
+{
+       const char *cmd_name;
+       char *st;
+       unsigned dev_dir_found = 0;
+       int use_policy = arg_count(cmd, use_policies_ARG);
+
+       lp->sign = SIGN_NONE;
+       lp->resize = LV_ANY;
+
+       cmd_name = command_name(cmd);
+       if (!strcmp(cmd_name, "lvreduce"))
+               lp->resize = LV_REDUCE;
+       if (!strcmp(cmd_name, "lvextend"))
+               lp->resize = LV_EXTEND;
+
+       if (use_policy) {
+               /* do nothing; _lvresize will handle --use-policies itself */
+               lp->extents = 0;
+               lp->sign = SIGN_PLUS;
+               lp->percent = PERCENT_LV;
+       } else {
+               /*
+                * Allow omission of extents and size if the user has given us
+                * one or more PVs.  Most likely, the intent was "resize this
+                * LV the best you can with these PVs"
+                */
+               if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
+                   (argc >= 2)) {
+                       lp->extents = 100;
+                       lp->percent = PERCENT_PVS;
+                       lp->sign = SIGN_PLUS;
+               } else if ((arg_count(cmd, extents_ARG) +
+                           arg_count(cmd, size_ARG) != 1)) {
+                       log_error("Please specify either size or extents but not "
+                                 "both.");
+                       return 0;
+               }
+
+               if (arg_count(cmd, extents_ARG)) {
+                       lp->extents = arg_uint_value(cmd, extents_ARG, 0);
+                       lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
+                       lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
+               }
+
+               /* Size returned in kilobyte units; held in sectors */
+               if (arg_count(cmd, size_ARG)) {
+                       lp->size = arg_uint64_value(cmd, size_ARG, 0);
+                       lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
+                       lp->percent = PERCENT_NONE;
+               }
+       }
+
+       if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
+               log_error("Negative argument not permitted - use lvreduce");
+               return 0;
+       }
+
+       if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
+               log_error("Positive sign not permitted - use lvextend");
+               return 0;
+       }
+
+       lp->resizefs = arg_is_set(cmd, resizefs_ARG);
+       lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+
+       if (!argc) {
+               log_error("Please provide the logical volume name");
+               return 0;
+       }
+
+       lp->lv_name = argv[0];
+       argv++;
+       argc--;
+
+       if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) ||
+           !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
+               log_error("Please provide a volume group name");
+               return 0;
+       }
+
+       if (!validate_name(lp->vg_name)) {
+               log_error("Volume group name %s has invalid characters",
+                         lp->vg_name);
+               return 0;
+       }
+
+       if ((st = strrchr(lp->lv_name, '/')))
+               lp->lv_name = st + 1;
+
+       lp->argc = argc;
+       lp->argv = argv;
+
+       return 1;
+}
+
+static int _adjust_policy_params(struct cmd_context *cmd,
+                                struct logical_volume *lv, struct lvresize_params *lp)
+{
+       percent_t percent;
+       int policy_threshold, policy_amount;
+
+       policy_threshold =
+               find_config_tree_int(cmd, "activation/snapshot_autoextend_threshold",
+                                    DEFAULT_SNAPSHOT_AUTOEXTEND_THRESHOLD) * PERCENT_1;
+       policy_amount =
+               find_config_tree_int(cmd, "activation/snapshot_autoextend_percent",
+                                    DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT);
+
+       if (policy_threshold >= PERCENT_100)
+               return 1; /* nothing to do */
+
+       if (!lv_snapshot_percent(lv, &percent))
+               return_0;
+
+       if (!(PERCENT_0 < percent && percent < PERCENT_100) || percent <= policy_threshold)
+               return 1; /* nothing to do */
+
+       lp->extents = policy_amount;
+       return 1;
+}
+
+static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
+                    struct lvresize_params *lp)
+{
+       struct logical_volume *lv;
+       struct lvinfo info;
+       uint32_t stripesize_extents = 0;
+       uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
+       uint32_t seg_mirrors = 0;
+       uint32_t extents_used = 0;
+       uint32_t size_rest;
+       uint32_t pv_extent_count = 0;
+       alloc_policy_t alloc;
+       struct logical_volume *lock_lv;
+       struct lv_list *lvl;
+       struct lv_segment *seg, *uninitialized_var(mirr_seg);
+       uint32_t seg_extents;
+       uint32_t sz, str;
+       int status;
+       struct dm_list *pvh = NULL;
+       int use_policy = arg_count(cmd, use_policies_ARG);
+
+       /* does LV exist? */
+       if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
+               log_error("Logical volume %s not found in volume group %s",
+                         lp->lv_name, lp->vg_name);
+               return ECMD_FAILED;
+       }
+
+       if (arg_count(cmd, stripes_ARG)) {
+               if (vg->fid->fmt->features & FMT_SEGMENTS)
+                       lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
+               else
+                       log_warn("Varied striping not supported. Ignoring.");
+       }
+
+       if (arg_count(cmd, mirrors_ARG)) {
+               if (vg->fid->fmt->features & FMT_SEGMENTS)
+                       lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
+               else
+                       log_warn("Mirrors not supported. Ignoring.");
+               if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+                       log_error("Mirrors argument may not be negative");
+                       return EINVALID_CMD_LINE;
+               }
+       }
+
+       if (arg_count(cmd, stripesize_ARG) &&
+           !_validate_stripesize(cmd, vg, lp))
+               return EINVALID_CMD_LINE;
+
+       lv = lvl->lv;
+
+       if (use_policy) {
+               if (!lv_is_cow(lv)) {
+                       log_error("Can't use policy-based resize for non-snapshot volumes.");
+                       return ECMD_FAILED;
+               }
+               _adjust_policy_params(cmd, lv, lp);
+       }
+
+       if (!lv_is_visible(lv)) {
+               log_error("Can't resize internal logical volume %s", lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv->status & LOCKED) {
+               log_error("Can't resize locked LV %s", lv->name);
+               return ECMD_FAILED;
+       }
+
+       if (lv->status & CONVERTING) {
+               log_error("Can't resize %s while lvconvert in progress", lv->name);
+               return ECMD_FAILED;
+       }
+
+       alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
+
+       if (lp->size) {
+               if (lp->size % vg->extent_size) {
+                       if (lp->sign == SIGN_MINUS)
+                               lp->size -= lp->size % vg->extent_size;
+                       else
+                               lp->size += vg->extent_size -
+                                   (lp->size % vg->extent_size);
+
+                       log_print("Rounding up size to full physical extent %s",
+                                 display_size(cmd, (uint64_t) lp->size));
+               }
+
+               lp->extents = lp->size / vg->extent_size;
+       }
+
+       if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
+                                                    lp->argv, 1) : &vg->pvs)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       switch(lp->percent) {
+               case PERCENT_VG:
+                       lp->extents = lp->extents * vg->extent_count / 100;
+                       break;
+               case PERCENT_FREE:
+                       lp->extents = lp->extents * vg->free_count / 100;
+                       break;
+               case PERCENT_LV:
+                       lp->extents = lp->extents * lv->le_count / 100;
+                       break;
+               case PERCENT_PVS:
+                       if (lp->argc) {
+                               pv_extent_count = pv_list_extents_free(pvh);
+                               lp->extents = lp->extents * pv_extent_count / 100;
+                       } else
+                               lp->extents = lp->extents * vg->extent_count / 100;
+                       break;
+               case PERCENT_ORIGIN:
+                       if (!lv_is_cow(lv)) {
+                               log_error("Specified LV does not have an origin LV.");
+                               return EINVALID_CMD_LINE;
+                       }
+                       lp->extents = lp->extents * origin_from_cow(lv)->le_count / 100;
+                       break;
+               case PERCENT_NONE:
+                       break;
+       }
+
+       if (lp->sign == SIGN_PLUS)
+               lp->extents += lv->le_count;
+
+       if (lp->sign == SIGN_MINUS) {
+               if (lp->extents >= lv->le_count) {
+                       log_error("Unable to reduce %s below 1 extent",
+                                 lp->lv_name);
+                       return EINVALID_CMD_LINE;
+               }
+
+               lp->extents = lv->le_count - lp->extents;
+       }
+
+       if (!lp->extents) {
+               log_error("New size of 0 not permitted");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (lp->extents == lv->le_count) {
+               if (use_policy)
+                       return ECMD_PROCESSED; /* Nothing to do. */
+               if (!lp->resizefs) {
+                       log_error("New size (%d extents) matches existing size "
+                                 "(%d extents)", lp->extents, lv->le_count);
+                       return EINVALID_CMD_LINE;
+               }
+               lp->resize = LV_EXTEND; /* lets pretend zero size extension */
+       }
+
+       seg_size = lp->extents - lv->le_count;
+
+       /* Use segment type of last segment */
+       dm_list_iterate_items(seg, &lv->segments) {
+               lp->segtype = seg->segtype;
+       }
+
+       /* FIXME Support LVs with mixed segment types */
+       if (lp->segtype != get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG,
+                                                                     lp->segtype->name))) {
+               log_error("VolumeType does not match (%s)", lp->segtype->name);
+               return EINVALID_CMD_LINE;
+       }
+
+       /* If extending, find mirrors of last segment */
+       if ((lp->extents > lv->le_count)) {
+               dm_list_iterate_back_items(mirr_seg, &lv->segments) {
+                       if (seg_is_mirrored(mirr_seg))
+                               seg_mirrors = lv_mirror_count(mirr_seg->lv);
+                       else
+                               seg_mirrors = 0;
+                       break;
+               }
+               if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
+                       log_print("Extending %" PRIu32 " mirror images.",
+                                 seg_mirrors);
+                       lp->mirrors = seg_mirrors;
+               }
+               if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
+                   (lp->mirrors != seg_mirrors)) {
+                       log_error("Cannot vary number of mirrors in LV yet.");
+                       return EINVALID_CMD_LINE;
+               }
+       }
+
+       /* If extending, find stripes, stripesize & size of last segment */
+       if ((lp->extents > lv->le_count) &&
+           !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
+               /* FIXME Don't assume mirror seg will always be AREA_LV */
+               dm_list_iterate_items(seg, seg_mirrors ? &seg_lv(mirr_seg, 0)->segments : &lv->segments) {
+                       if (!seg_is_striped(seg))
+                               continue;
+
+                       sz = seg->stripe_size;
+                       str = seg->area_count;
+
+                       if ((seg_stripesize && seg_stripesize != sz &&
+                            sz && !lp->stripe_size) ||
+                           (seg_stripes && seg_stripes != str && !lp->stripes)) {
+                               log_error("Please specify number of "
+                                         "stripes (-i) and stripesize (-I)");
+                               return EINVALID_CMD_LINE;
+                       }
+
+                       seg_stripesize = sz;
+                       seg_stripes = str;
+               }
+
+               if (!lp->stripes)
+                       lp->stripes = seg_stripes;
+
+               if (!lp->stripe_size && lp->stripes > 1) {
+                       if (seg_stripesize) {
+                               log_print("Using stripesize of last segment %s",
+                                         display_size(cmd, (uint64_t) seg_stripesize));
+                               lp->stripe_size = seg_stripesize;
+                       } else {
+                               lp->stripe_size =
+                                       find_config_tree_int(cmd,
+                                                       "metadata/stripesize",
+                                                       DEFAULT_STRIPESIZE) * 2;
+                               log_print("Using default stripesize %s",
+                                         display_size(cmd, (uint64_t) lp->stripe_size));
+                       }
+               }
+       }
+
+       /* If reducing, find stripes, stripesize & size of last segment */
+       if (lp->extents < lv->le_count) {
+               extents_used = 0;
+
+               if (lp->stripes || lp->stripe_size || lp->mirrors)
+                       log_error("Ignoring stripes, stripesize and mirrors "
+                                 "arguments when reducing");
+
+               dm_list_iterate_items(seg, &lv->segments) {
+                       seg_extents = seg->len;
+
+                       if (seg_is_striped(seg)) {
+                               seg_stripesize = seg->stripe_size;
+                               seg_stripes = seg->area_count;
+                       }
+
+                       if (seg_is_mirrored(seg))
+                               seg_mirrors = lv_mirror_count(seg->lv);
+                       else
+                               seg_mirrors = 0;
+
+                       if (lp->extents <= extents_used + seg_extents)
+                               break;
+
+                       extents_used += seg_extents;
+               }
+
+               seg_size = lp->extents - extents_used;
+               lp->stripe_size = seg_stripesize;
+               lp->stripes = seg_stripes;
+               lp->mirrors = seg_mirrors;
+       }
+
+       if (lp->stripes > 1 && !lp->stripe_size) {
+               log_error("Stripesize for striped segment should not be 0!");
+               return EINVALID_CMD_LINE;
+       }
+
+       if ((lp->stripes > 1)) {
+               if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
+                       stripesize_extents = 1;
+
+               if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
+                       log_print("Rounding size (%d extents) down to stripe "
+                                 "boundary size for segment (%d extents)",
+                                 lp->extents, lp->extents - size_rest);
+                       lp->extents = lp->extents - size_rest;
+               }
+
+               if (lp->stripe_size < STRIPE_SIZE_MIN) {
+                       log_error("Invalid stripe size %s",
+                                 display_size(cmd, (uint64_t) lp->stripe_size));
+                       return EINVALID_CMD_LINE;
+               }
+       }
+
+       if (lp->extents < lv->le_count) {
+               if (lp->resize == LV_EXTEND) {
+                       log_error("New size given (%d extents) not larger "
+                                 "than existing size (%d extents)",
+                                 lp->extents, lv->le_count);
+                       return EINVALID_CMD_LINE;
+               }
+               lp->resize = LV_REDUCE;
+       } else if (lp->extents > lv->le_count) {
+               if (lp->resize == LV_REDUCE) {
+                       log_error("New size given (%d extents) not less than "
+                                 "existing size (%d extents)", lp->extents,
+                                 lv->le_count);
+                       return EINVALID_CMD_LINE;
+               }
+               lp->resize = LV_EXTEND;
+       }
+
+       if (lv_is_origin(lv)) {
+               if (lp->resize == LV_REDUCE) {
+                       log_error("Snapshot origin volumes cannot be reduced "
+                                 "in size yet.");
+                       return ECMD_FAILED;
+               }
+
+               memset(&info, 0, sizeof(info));
+
+               if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+                       log_error("Snapshot origin volumes can be resized "
+                                 "only while inactive: try lvchange -an");
+                       return ECMD_FAILED;
+               }
+       }
+
+       if ((lp->resize == LV_REDUCE) && lp->argc)
+               log_warn("Ignoring PVs on command line when reducing");
+
+       /* Request confirmation before operations that are often mistakes. */
+       if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
+           !_request_confirmation(cmd, vg, lv, lp)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (lp->resizefs) {
+               if (!lp->nofsck &&
+                   !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK, &status)) {
+                       if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) {
+                               stack;
+                               return ECMD_FAILED;
+                       }
+                        /* some filesystems supports online resize */
+               }
+
+               if ((lp->resize == LV_REDUCE) &&
+                   !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE, NULL)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       if (!archive(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       log_print("%sing logical volume %s to %s",
+                 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
+                 lp->lv_name,
+                 display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
+
+       if (lp->resize == LV_REDUCE) {
+               if (!lv_reduce(lv, lv->le_count - lp->extents)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       } else if ((lp->extents > lv->le_count) && /* Ensure we extend */
+                  !lv_extend(lv, lp->segtype, lp->stripes,
+                             lp->stripe_size, lp->mirrors,
+                             lp->extents - lv->le_count,
+                             NULL, 0u, 0u, pvh, alloc)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       /* store vg on disk(s) */
+       if (!vg_write(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       /* If snapshot, must suspend all associated devices */
+       if (lv_is_cow(lv))
+               lock_lv = origin_from_cow(lv);
+       else
+               lock_lv = lv;
+
+       if (!suspend_lv(cmd, lock_lv)) {
+               log_error("Failed to suspend %s", lp->lv_name);
+               vg_revert(vg);
+               backup(vg);
+               return ECMD_FAILED;
+       }
+
+       if (!vg_commit(vg)) {
+               stack;
+               if (!resume_lv(cmd, lock_lv))
+                       stack;
+               backup(vg);
+               return ECMD_FAILED;
+       }
+
+       if (!resume_lv(cmd, lock_lv)) {
+               log_error("Problem reactivating %s", lp->lv_name);
+               backup(vg);
+               return ECMD_FAILED;
+       }
+
+       backup(vg);
+
+       log_print("Logical volume %s successfully resized", lp->lv_name);
+
+       if (lp->resizefs && (lp->resize == LV_EXTEND) &&
+           !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE, NULL)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int lvresize(struct cmd_context *cmd, int argc, char **argv)
+{
+       struct lvresize_params lp;
+       struct volume_group *vg;
+       int r;
+
+       memset(&lp, 0, sizeof(lp));
+
+       if (!_lvresize_params(cmd, argc, argv, &lp))
+               return EINVALID_CMD_LINE;
+
+       log_verbose("Finding volume group %s", lp.vg_name);
+       vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!(r = _lvresize(cmd, vg, &lp)))
+               stack;
+
+       unlock_and_free_vg(cmd, vg, lp.vg_name);
+
+       return r;
+}
diff --git a/tools/lvscan.c b/tools/lvscan.c
new file mode 100644 (file)
index 0000000..638d2f5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv,
+                        void *handle __attribute__((unused)))
+{
+       struct lvinfo info;
+       int lv_total = 0;
+       uint64_t lv_capacity_total = 0;
+       int inkernel, snap_active = 1;
+       struct lv_segment *snap_seg = NULL;
+       percent_t snap_percent;     /* fused, fsize; */
+
+       const char *active_str, *snapshot_str;
+
+       if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
+               return ECMD_PROCESSED;
+
+       inkernel = lv_info(cmd, lv, 0, &info, 1, 0) && info.exists;
+       if (lv_is_origin(lv)) {
+               dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs,
+                                      origin_list) {
+                       if (inkernel &&
+                           (snap_active = lv_snapshot_percent(snap_seg->cow,
+                                                              &snap_percent)))
+                               if (snap_percent == PERCENT_INVALID)
+                                       snap_active = 0;
+               }
+               snap_seg = NULL;
+       } else if (lv_is_cow(lv)) {
+               if (inkernel &&
+                   (snap_active = lv_snapshot_percent(lv, &snap_percent)))
+                       if (snap_percent == PERCENT_INVALID)
+                               snap_active = 0;
+       }
+
+/* FIXME Add -D arg to skip this! */
+       if (inkernel && snap_active)
+               active_str = "ACTIVE   ";
+       else
+               active_str = "inactive ";
+
+       if (lv_is_origin(lv))
+               snapshot_str = "Original";
+       else if (lv_is_cow(lv))
+               snapshot_str = "Snapshot";
+       else
+               snapshot_str = "        ";
+
+       log_print("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str,
+                 cmd->dev_dir, lv->vg->name, lv->name,
+                 display_size(cmd, lv->size),
+                 get_alloc_string(lv->alloc));
+
+       lv_total++;
+
+       lv_capacity_total += lv->size;
+
+       return ECMD_PROCESSED;
+}
+
+int lvscan(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (argc) {
+               log_error("No additional command line arguments allowed");
+               return EINVALID_CMD_LINE;
+       }
+
+       return process_each_lv(cmd, argc, argv, 0, NULL,
+                              &lvscan_single);
+}
diff --git a/tools/polldaemon.c b/tools/polldaemon.c
new file mode 100644 (file)
index 0000000..50579ba
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "polldaemon.h"
+#include "lvm2cmdline.h"
+#include <signal.h>
+#include <sys/wait.h>
+
+static void _sigchld_handler(int sig __attribute__((unused)))
+{
+       while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
+}
+
+/*
+ * returns:
+ * -1 if the fork failed
+ *  0 if the parent
+ *  1 if the child
+ */
+static int _become_daemon(struct cmd_context *cmd)
+{
+       pid_t pid;
+       struct sigaction act = {
+               {_sigchld_handler},
+               .sa_flags = SA_NOCLDSTOP,
+       };
+
+       log_verbose("Forking background process");
+
+       sigaction(SIGCHLD, &act, NULL);
+
+       if ((pid = fork()) == -1) {
+               log_error("fork failed: %s", strerror(errno));
+               return -1;
+       }
+
+       /* Parent */
+       if (pid > 0)
+               return 0;
+
+       /* Child */
+       if (setsid() == -1)
+               log_error("Background process failed to setsid: %s",
+                         strerror(errno));
+       init_verbose(VERBOSE_BASE_LEVEL);
+
+       close(STDIN_FILENO);
+       close(STDOUT_FILENO);
+       close(STDERR_FILENO);
+
+       strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv));
+
+       reset_locking();
+       lvmcache_init();
+       dev_close_all();
+
+       return 1;
+}
+
+progress_t poll_mirror_progress(struct cmd_context *cmd,
+                               struct logical_volume *lv, const char *name,
+                               struct daemon_parms *parms)
+{
+       percent_t segment_percent = PERCENT_0, overall_percent = PERCENT_0;
+       uint32_t event_nr = 0;
+
+       if (!lv_is_mirrored(lv) ||
+           !lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent,
+                              &event_nr) ||
+           (segment_percent == PERCENT_INVALID)) {
+               log_error("ABORTING: Mirror percentage check failed.");
+               return PROGRESS_CHECK_FAILED;
+       }
+
+       overall_percent = copy_percent(lv);
+       if (parms->progress_display)
+               log_print("%s: %s: %.1f%%", name, parms->progress_title,
+                         percent_to_float(overall_percent));
+       else
+               log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
+                           percent_to_float(overall_percent));
+
+       if (segment_percent != PERCENT_100)
+               return PROGRESS_UNFINISHED;
+
+       if (overall_percent == PERCENT_100)
+               return PROGRESS_FINISHED_ALL;
+
+       return PROGRESS_FINISHED_SEGMENT;
+}
+
+static int _check_lv_status(struct cmd_context *cmd,
+                           struct volume_group *vg,
+                           struct logical_volume *lv,
+                           const char *name, struct daemon_parms *parms,
+                           int *finished)
+{
+       struct dm_list *lvs_changed;
+       progress_t progress;
+
+       /* By default, caller should not retry */
+       *finished = 1;
+
+       if (parms->aborting) {
+               if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
+                       log_error("Failed to generate list of copied LVs: "
+                                 "can't abort.");
+                       return 0;
+               }
+               if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
+                       return_0;
+
+               return 1;
+       }
+
+       progress = parms->poll_fns->poll_progress(cmd, lv, name, parms);
+       if (progress == PROGRESS_CHECK_FAILED)
+               return_0;
+
+       if (progress == PROGRESS_UNFINISHED) {
+               /* The only case the caller *should* try again later */
+               *finished = 0;
+               return 1;
+       }
+
+       if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
+               log_error("ABORTING: Failed to generate list of copied LVs");
+               return 0;
+       }
+
+       /* Finished? Or progress to next segment? */
+       if (progress == PROGRESS_FINISHED_ALL) {
+               if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
+                       return 0;
+       } else {
+               if (parms->poll_fns->update_metadata &&
+                   !parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) {
+                       log_error("ABORTING: Segment progression failed.");
+                       parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
+                       return 0;
+               }
+               *finished = 0;  /* Another segment */
+       }
+
+       return 1;
+}
+
+static void _sleep_and_rescan_devices(struct daemon_parms *parms)
+{
+       /* FIXME Use alarm for regular intervals instead */
+       if (parms->interval && !parms->aborting) {
+               sleep(parms->interval);
+               /* Devices might have changed while we slept */
+               init_full_scan_done(0);
+       }
+}
+
+static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid,
+                              struct daemon_parms *parms)
+{
+       struct volume_group *vg;
+       struct logical_volume *lv;
+       int finished = 0;
+
+       /* Poll for completion */
+       while (!finished) {
+               if (parms->wait_before_testing)
+                       _sleep_and_rescan_devices(parms);
+
+               /* Locks the (possibly renamed) VG again */
+               vg = parms->poll_fns->get_copy_vg(cmd, name, uuid);
+               if (vg_read_error(vg)) {
+                       free_vg(vg);
+                       log_error("ABORTING: Can't reread VG for %s", name);
+                       /* What more could we do here? */
+                       return 0;
+               }
+
+               if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid,
+                                                       parms->lv_type))) {
+                       log_error("ABORTING: Can't find LV in %s for %s",
+                                 vg->name, name);
+                       unlock_and_free_vg(cmd, vg, vg->name);
+                       return 0;
+               }
+
+               if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) {
+                       unlock_and_free_vg(cmd, vg, vg->name);
+                       return 0;
+               }
+
+               unlock_and_free_vg(cmd, vg, vg->name);
+
+               /*
+                * FIXME Sleeping after testing, while preferred, also works around
+                * unreliable "finished" state checking in _percent_run.  If the
+                * above _check_lv_status is deferred until after the first sleep it
+                * may be that a polldaemon will run without ever completing.
+                *
+                * This happens when one snapshot-merge polldaemon is racing with
+                * another (polling the same LV).  The first to see the LV status
+                * reach the "finished" state will alter the LV that the other
+                * polldaemon(s) are polling.  These other polldaemon(s) can then
+                * continue polling an LV that doesn't have a "status".
+                */
+               if (!parms->wait_before_testing)
+                       _sleep_and_rescan_devices(parms);
+       }
+
+       return 1;
+}
+
+static int _poll_vg(struct cmd_context *cmd, const char *vgname,
+                   struct volume_group *vg, void *handle)
+{
+       struct daemon_parms *parms = (struct daemon_parms *) handle;
+       struct lv_list *lvl;
+       struct logical_volume *lv;
+       const char *name;
+       int finished;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv = lvl->lv;
+               if (!(lv->status & parms->lv_type))
+                       continue;
+               name = parms->poll_fns->get_copy_name_from_lv(lv);
+               if (!name && !parms->aborting)
+                       continue;
+
+               /* FIXME Need to do the activation from _set_up_pvmove here
+                *       if it's not running and we're not aborting */
+               if (_check_lv_status(cmd, vg, lv, name, parms, &finished) &&
+                   !finished)
+                       parms->outstanding_count++;
+       }
+
+       return ECMD_PROCESSED;
+
+}
+
+static void _poll_for_all_vgs(struct cmd_context *cmd,
+                             struct daemon_parms *parms)
+{
+       while (1) {
+               parms->outstanding_count = 0;
+               process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg);
+               if (!parms->outstanding_count)
+                       break;
+               sleep(parms->interval);
+       }
+}
+
+/*
+ * Only allow *one* return from poll_daemon() (the parent).
+ * If there is a child it must exit (ignoring the memory leak messages).
+ * - 'background' is advisory so a child polldaemon may not be used even
+ *   if it was requested.
+ */
+int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
+               unsigned background,
+               uint32_t lv_type, struct poll_functions *poll_fns,
+               const char *progress_title)
+{
+       struct daemon_parms parms;
+       int daemon_mode = 0;
+       int ret = ECMD_PROCESSED;
+       sign_t interval_sign;
+
+       parms.aborting = arg_is_set(cmd, abort_ARG);
+       parms.background = background;
+       interval_sign = arg_sign_value(cmd, interval_ARG, 0);
+       if (interval_sign == SIGN_MINUS)
+               log_error("Argument to --interval cannot be negative");
+       parms.interval = arg_uint_value(cmd, interval_ARG,
+                                       find_config_tree_int(cmd, "activation/polling_interval",
+                                                            DEFAULT_INTERVAL));
+       parms.wait_before_testing = (interval_sign == SIGN_PLUS);
+       parms.progress_display = 1;
+       parms.progress_title = progress_title;
+       parms.lv_type = lv_type;
+       parms.poll_fns = poll_fns;
+
+       if (parms.interval && !parms.aborting)
+               log_verbose("Checking progress %s waiting every %u seconds",
+                           (parms.wait_before_testing ? "after" : "before"),
+                           parms.interval);
+
+       if (!parms.interval) {
+               parms.progress_display = 0;
+
+               /* FIXME Disabled multiple-copy wait_event */
+               if (!name)
+                       parms.interval = find_config_tree_int(cmd, "activation/polling_interval",
+                                                             DEFAULT_INTERVAL);
+       }
+
+       if (parms.background) {
+               daemon_mode = _become_daemon(cmd);
+               if (daemon_mode == 0)
+                       return ECMD_PROCESSED;      /* Parent */
+               else if (daemon_mode == 1)
+                       parms.progress_display = 0; /* Child */
+               /* FIXME Use wait_event (i.e. interval = 0) and */
+               /*       fork one daemon per copy? */
+       }
+
+       /*
+        * Process one specific task or all incomplete tasks?
+        */
+       if (name) {
+               if (!_wait_for_single_lv(cmd, name, uuid, &parms)) {
+                       stack;
+                       ret = ECMD_FAILED;
+               }
+       } else
+               _poll_for_all_vgs(cmd, &parms);
+
+       if (parms.background && daemon_mode == 1) {
+               /*
+                * child was successfully forked:
+                * background polldaemon must not return to the caller
+                * because it will redundantly continue performing the
+                * caller's task (that the parent already performed)
+                */
+               /* FIXME Attempt proper cleanup */
+               _exit(lvm_return_code(ret));
+       }
+
+       return ret;
+}
diff --git a/tools/polldaemon.h b/tools/polldaemon.h
new file mode 100644 (file)
index 0000000..8ebbb25
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TOOL_POLLDAEMON_H
+#define _LVM_TOOL_POLLDAEMON_H
+
+#include "metadata-exported.h"
+
+typedef enum {
+       PROGRESS_CHECK_FAILED = 0,
+       PROGRESS_UNFINISHED = 1,
+       PROGRESS_FINISHED_SEGMENT = 2,
+       PROGRESS_FINISHED_ALL = 3
+} progress_t;
+
+struct daemon_parms;
+
+struct poll_functions {
+       const char *(*get_copy_name_from_lv) (struct logical_volume *lv);
+       struct volume_group *(*get_copy_vg) (struct cmd_context *cmd,
+                                            const char *name,
+                                            const char *uuid);
+       struct logical_volume *(*get_copy_lv) (struct cmd_context *cmd,
+                                              struct volume_group *vg,
+                                              const char *name,
+                                              const char *uuid,
+                                              uint32_t lv_type);
+       progress_t (*poll_progress)(struct cmd_context *cmd,
+                                   struct logical_volume *lv,
+                                   const char *name,
+                                   struct daemon_parms *parms);
+       int (*update_metadata) (struct cmd_context *cmd,
+                               struct volume_group *vg,
+                               struct logical_volume *lv,
+                               struct dm_list *lvs_changed, unsigned flags);
+       int (*finish_copy) (struct cmd_context *cmd,
+                           struct volume_group *vg,
+                           struct logical_volume *lv,
+                           struct dm_list *lvs_changed);
+};
+
+struct daemon_parms {
+       unsigned interval;
+       unsigned wait_before_testing;
+       unsigned aborting;
+       unsigned background;
+       unsigned outstanding_count;
+       unsigned progress_display;
+       const char *progress_title;
+       uint32_t lv_type;
+       struct poll_functions *poll_fns;
+};
+
+int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
+               unsigned background,
+               uint32_t lv_type, struct poll_functions *poll_fns,
+               const char *progress_title);
+
+progress_t poll_mirror_progress(struct cmd_context *cmd,
+                               struct logical_volume *lv, const char *name,
+                               struct daemon_parms *parms);
+
+#endif
diff --git a/tools/pvchange.c b/tools/pvchange.c
new file mode 100644 (file)
index 0000000..28e71b8
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+/* FIXME Locking.  PVs in VG. */
+
+static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg,
+                           struct physical_volume *pv,
+                           void *handle __attribute__((unused)))
+{
+       uint32_t orig_pe_alloc_count;
+       /* FIXME Next three only required for format1. */
+       uint32_t orig_pe_count, orig_pe_size;
+       uint64_t orig_pe_start;
+
+       const char *pv_name = pv_dev_name(pv);
+       const char *tag = NULL;
+       const char *orig_vg_name;
+       char uuid[64] __attribute__((aligned(8)));
+
+       int allocatable = 0;
+       int tagarg = 0;
+       int r = 0;
+       int mda_ignore = 0;
+
+       struct arg_value_group_list *current_group;
+
+       if (arg_count(cmd, addtag_ARG))
+               tagarg = addtag_ARG;
+       else if (arg_count(cmd, deltag_ARG))
+               tagarg = deltag_ARG;
+
+       if (arg_count(cmd, allocatable_ARG))
+               allocatable = !strcmp(arg_str_value(cmd, allocatable_ARG, "n"),
+                                     "y");
+       if (arg_count(cmd, metadataignore_ARG))
+               mda_ignore = !strcmp(arg_str_value(cmd, metadataignore_ARG, "n"),
+                                     "y");
+
+       /* If in a VG, must change using volume group. */
+       if (!is_orphan(pv)) {
+               if (tagarg && !(vg->fid->fmt->features & FMT_TAGS)) {
+                       log_error("Volume group containing %s does not "
+                                 "support tags", pv_name);
+                       goto out;
+               }
+               if (arg_count(cmd, uuid_ARG) && lvs_in_vg_activated(vg)) {
+                       log_error("Volume group containing %s has active "
+                                 "logical volumes", pv_name);
+                       goto out;
+               }
+               if (!archive(vg))
+                       goto out;
+       } else {
+               if (tagarg) {
+                       log_error("Can't change tag on Physical Volume %s not "
+                                 "in volume group", pv_name);
+                       return 0;
+               }
+       }
+
+       if (arg_count(cmd, allocatable_ARG)) {
+               if (is_orphan(pv) &&
+                   !(pv->fmt->features & FMT_ORPHAN_ALLOCATABLE)) {
+                       log_error("Allocatability not supported by orphan "
+                                 "%s format PV %s", pv->fmt->name, pv_name);
+                       goto out;
+               }
+
+               /* change allocatability for a PV */
+               if (allocatable && (pv_status(pv) & ALLOCATABLE_PV)) {
+                       log_error("Physical volume \"%s\" is already "
+                                 "allocatable", pv_name);
+                       r = 1;
+                       goto out;
+               }
+
+               if (!allocatable && !(pv_status(pv) & ALLOCATABLE_PV)) {
+                       log_error("Physical volume \"%s\" is already "
+                                 "unallocatable", pv_name);
+                       r = 1;
+                       goto out;
+               }
+
+               if (allocatable) {
+                       log_verbose("Setting physical volume \"%s\" "
+                                   "allocatable", pv_name);
+                       pv->status |= ALLOCATABLE_PV;
+               } else {
+                       log_verbose("Setting physical volume \"%s\" NOT "
+                                   "allocatable", pv_name);
+                       pv->status &= ~ALLOCATABLE_PV;
+               }
+       } else if (tagarg) {
+               /* tag or deltag */
+
+               dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+                       if (!grouped_arg_is_set(current_group->arg_values, tagarg))
+                               continue;
+
+                       if (!(tag = grouped_arg_str_value(current_group->arg_values, tagarg, NULL))) {
+                               log_error("Failed to get tag");
+                               goto out;
+                       }
+
+                       if ((tagarg == addtag_ARG)) {
+                               if (!str_list_add(cmd->mem, &pv->tags, tag)) {
+                                       log_error("Failed to add tag %s to physical "
+                                                 "volume %s", tag, pv_name);
+                                       goto out;
+                               }
+                       } else if (!str_list_del(&pv->tags, tag)) {
+                               log_error("Failed to remove tag %s from "
+                                       "physical volume" "%s", tag, pv_name);
+                               goto out;
+                       }
+               }
+       } else if (arg_count(cmd, metadataignore_ARG)) {
+               if ((vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) &&
+                   (arg_count(cmd, force_ARG) == PROMPT) &&
+                   yes_no_prompt("Override preferred number of copies "
+                                 "of VG %s metadata? [y/n]: ",
+                                 pv_vg_name(pv)) == 'n') {
+                       log_error("Physical volume %s not changed", pv_name);
+                       goto out;
+               }
+               if (!pv_change_metadataignore(pv, mda_ignore))
+                       goto out;
+       } else {
+               /* --uuid: Change PV ID randomly */
+               if (!id_create(&pv->id)) {
+                       log_error("Failed to generate new random UUID for %s.",
+                                 pv_name);
+                       goto out;
+               }
+               if (!id_write_format(&pv->id, uuid, sizeof(uuid)))
+                       goto_out;
+               log_verbose("Changing uuid of %s to %s.", pv_name, uuid);
+               if (!is_orphan(pv)) {
+                       orig_vg_name = pv_vg_name(pv);
+                       orig_pe_alloc_count = pv_pe_alloc_count(pv);
+
+                       /* FIXME format1 pv_write doesn't preserve these. */
+                       orig_pe_size = pv_pe_size(pv);
+                       orig_pe_start = pv_pe_start(pv);
+                       orig_pe_count = pv_pe_count(pv);
+
+                       pv->vg_name = pv->fmt->orphan_vg_name;
+                       pv->pe_alloc_count = 0;
+                       if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
+                               log_error("pv_write with new uuid failed "
+                                         "for %s.", pv_name);
+                               goto out;
+                       }
+                       pv->vg_name = orig_vg_name;
+                       pv->pe_alloc_count = orig_pe_alloc_count;
+
+                       pv->pe_size = orig_pe_size;
+                       pv->pe_start = orig_pe_start;
+                       pv->pe_count = orig_pe_count;
+               }
+       }
+
+       log_verbose("Updating physical volume \"%s\"", pv_name);
+       if (!is_orphan(pv)) {
+               if (!vg_write(vg) || !vg_commit(vg)) {
+                       log_error("Failed to store physical volume \"%s\" in "
+                                 "volume group \"%s\"", pv_name, vg->name);
+                       goto out;
+               }
+               backup(vg);
+       } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
+               log_error("Failed to store physical volume \"%s\"",
+                         pv_name);
+               goto out;
+       }
+
+       log_print("Physical volume \"%s\" changed", pv_name);
+       r = 1;
+out:
+       return r;
+
+}
+
+int pvchange(struct cmd_context *cmd, int argc, char **argv)
+{
+       int opt = 0;
+       int done = 0;
+       int total = 0;
+
+       struct volume_group *vg;
+       const char *vg_name;
+       char *pv_name;
+
+       struct pv_list *pvl;
+       struct dm_list *vgnames;
+       struct str_list *sll;
+
+       if (arg_count(cmd, allocatable_ARG) + arg_is_set(cmd, addtag_ARG) +
+           arg_is_set(cmd, deltag_ARG) + arg_count(cmd, uuid_ARG) +
+           arg_count(cmd, metadataignore_ARG) != 1) {
+               log_error("Please give exactly one option of -x, -uuid, "
+                         "--addtag or --deltag");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (!(arg_count(cmd, all_ARG)) && !argc) {
+               log_error("Please give a physical volume path");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, all_ARG) && argc) {
+               log_error("Option a and PhysicalVolumePath are exclusive");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (argc) {
+               log_verbose("Using physical volume(s) on command line");
+               for (; opt < argc; opt++) {
+                       pv_name = argv[opt];
+                       unescape_colons_and_at_signs(pv_name, NULL, NULL);
+                       vg_name = find_vgname_from_pvname(cmd, pv_name);
+                       if (!vg_name) {
+                               log_error("Failed to read physical volume %s",
+                                         pv_name);
+                               continue;
+                       }
+                       vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+                       if (vg_read_error(vg)) {
+                               free_vg(vg);
+                               stack;
+                               continue;
+                       }
+                       pvl = find_pv_in_vg(vg, pv_name);
+                       if (!pvl || !pvl->pv) {
+                               log_error("Unable to find %s in %s",
+                                         pv_name, vg_name);
+                               continue;
+                       }
+
+                       total++;
+                       done += _pvchange_single(cmd, vg,
+                                                pvl->pv, NULL);
+                       unlock_and_free_vg(cmd, vg, vg_name);
+               }
+       } else {
+               log_verbose("Scanning for physical volume names");
+               /* FIXME: share code with toollib */
+               /*
+                * Take the global lock here so the lvmcache remains
+                * consistent across orphan/non-orphan vg locks.  If we don't
+                * take the lock here, pvs with 0 mdas in a non-orphan VG will
+                * be processed twice.
+                */
+               if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE)) {
+                       log_error("Unable to obtain global lock.");
+                       return ECMD_FAILED;
+               }
+
+               if ((vgnames = get_vgnames(cmd, 1)) &&
+                   !dm_list_empty(vgnames)) {
+                       dm_list_iterate_items(sll, vgnames) {
+                               vg = vg_read_for_update(cmd, sll->str, NULL, 0);
+                               if (vg_read_error(vg)) {
+                                       free_vg(vg);
+                                       stack;
+                                       continue;
+                               }
+                               dm_list_iterate_items(pvl, &vg->pvs) {
+                                       total++;
+                                       done += _pvchange_single(cmd, vg,
+                                                                pvl->pv,
+                                                                NULL);
+                               }
+                               unlock_and_free_vg(cmd, vg, sll->str);
+                       }
+               }
+       }
+
+       unlock_vg(cmd, VG_GLOBAL);
+       log_print("%d physical volume%s changed / %d physical volume%s "
+                 "not changed",
+                 done, done == 1 ? "" : "s",
+                 total - done, (total - done) == 1 ? "" : "s");
+
+       return (total == done) ? ECMD_PROCESSED : ECMD_FAILED;
+}
diff --git a/tools/pvck.c b/tools/pvck.c
new file mode 100644 (file)
index 0000000..2ec7597
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int pvck(struct cmd_context *cmd, int argc, char **argv)
+{
+       int i;
+
+       /* FIXME: validate cmdline options */
+       /* FIXME: what does the cmdline look like? */
+       /*
+        * Use what's on the cmdline directly, and avoid calling into
+        * some of the other infrastructure functions, so as to avoid
+        * hitting some of the lvmcache behavior, scanning other devices,
+        * etc.
+        */
+       for (i = 0; i < argc; i++) {
+               /* FIXME: warning and/or check if in use? */
+               log_verbose("Scanning %s", argv[i]);
+
+               unescape_colons_and_at_signs(argv[i], NULL, NULL);
+               pv_analyze(cmd, argv[i],
+                          arg_uint64_value(cmd, labelsector_ARG,
+                                          UINT64_C(0)));
+       }
+
+       return ECMD_PROCESSED;
+}
diff --git a/tools/pvcreate.c b/tools/pvcreate.c
new file mode 100644 (file)
index 0000000..a955d37
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "metadata-exported.h"
+
+/*
+ * Intial sanity checking of recovery-related command-line arguments.
+ * These args are: --restorefile, --uuid, and --physicalvolumesize
+ *
+ * Output arguments:
+ * pp: structure allocated by caller, fields written / validated here
+ */
+static int pvcreate_restore_params_validate(struct cmd_context *cmd,
+                                           int argc, char **argv,
+                                           struct pvcreate_params *pp)
+{
+       const char *uuid = NULL;
+       struct volume_group *vg;
+       struct pv_list *existing_pvl;
+
+       if (arg_count(cmd, restorefile_ARG) && !arg_count(cmd, uuidstr_ARG)) {
+               log_error("--uuid is required with --restorefile");
+               return 0;
+       }
+
+       if (!arg_count(cmd, restorefile_ARG) && arg_count(cmd, uuidstr_ARG)) {
+               if (!arg_count(cmd, norestorefile_ARG) &&
+                   find_config_tree_bool(cmd,
+                                         "devices/require_restorefile_with_uuid",
+                                         DEFAULT_REQUIRE_RESTOREFILE_WITH_UUID)) {
+                       log_error("--restorefile is required with --uuid");
+                       return 0;
+               }
+       }
+
+       if (arg_count(cmd, uuidstr_ARG) && argc != 1) {
+               log_error("Can only set uuid on one volume at once");
+               return 0;
+       }
+
+       if (arg_count(cmd, uuidstr_ARG)) {
+               uuid = arg_str_value(cmd, uuidstr_ARG, "");
+               if (!id_read_format(&pp->id, uuid))
+                       return 0;
+               pp->idp = &pp->id;
+       }
+
+       if (arg_count(cmd, restorefile_ARG)) {
+               pp->restorefile = arg_str_value(cmd, restorefile_ARG, "");
+               /* The uuid won't already exist */
+               if (!(vg = backup_read_vg(cmd, NULL, pp->restorefile))) {
+                       log_error("Unable to read volume group from %s",
+                                 pp->restorefile);
+                       return 0;
+               }
+               if (!(existing_pvl = find_pv_in_vg_by_uuid(vg, pp->idp))) {
+                       log_error("Can't find uuid %s in backup file %s",
+                                 uuid, pp->restorefile);
+                       return 0;
+               }
+               pp->pe_start = pv_pe_start(existing_pvl->pv);
+               pp->extent_size = pv_pe_size(existing_pvl->pv);
+               pp->extent_count = pv_pe_count(existing_pvl->pv);
+               free_vg(vg);
+       }
+
+       if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) {
+               log_error("Physical volume size may not be negative");
+               return 0;
+       }
+       pp->size = arg_uint64_value(cmd, physicalvolumesize_ARG, UINT64_C(0));
+
+       if (arg_count(cmd, restorefile_ARG) || arg_count(cmd, uuidstr_ARG))
+               pp->zero = 0;
+       return 1;
+}
+
+int pvcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+       int i;
+       int ret = ECMD_PROCESSED;
+       struct pvcreate_params pp;
+
+       pvcreate_params_set_defaults(&pp);
+
+       if (!pvcreate_restore_params_validate(cmd, argc, argv, &pp)) {
+               return EINVALID_CMD_LINE;
+       }
+       if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
+               return EINVALID_CMD_LINE;
+       }
+
+       for (i = 0; i < argc; i++) {
+               if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+                       log_error("Can't get lock for orphan PVs");
+                       return ECMD_FAILED;
+               }
+
+               unescape_colons_and_at_signs(argv[i], NULL, NULL);
+
+               if (!pvcreate_single(cmd, argv[i], &pp)) {
+                       stack;
+                       ret = ECMD_FAILED;
+               }
+
+               unlock_vg(cmd, VG_ORPHANS);
+               if (sigint_caught())
+                       return ret;
+       }
+
+       return ret;
+}
diff --git a/tools/pvdisplay.c b/tools/pvdisplay.c
new file mode 100644 (file)
index 0000000..1ae0339
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int _pvdisplay_single(struct cmd_context *cmd,
+                            struct volume_group *vg,
+                            struct physical_volume *pv, void *handle)
+{
+       struct pv_list *pvl;
+       int ret = ECMD_PROCESSED;
+       uint64_t size;
+       struct volume_group *old_vg = vg;
+
+       const char *pv_name = pv_dev_name(pv);
+       const char *vg_name = NULL;
+
+       if (!is_orphan(pv) && !vg) {
+               vg_name = pv_vg_name(pv);
+               vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0);
+               if (vg_read_error(vg)) {
+                       log_error("Skipping volume group %s", vg_name);
+                       free_vg(vg);
+                       /* FIXME If CLUSTERED should return ECMD_PROCESSED here */
+                       return ECMD_FAILED;
+               }
+
+               /*
+                * Replace possibly incomplete PV structure with new one
+                * allocated in vg_read_internal() path.
+                */
+                if (!(pvl = find_pv_in_vg(vg, pv_name))) {
+                        log_error("Unable to find \"%s\" in volume group \"%s\"",
+                                  pv_name, vg->name);
+                        ret = ECMD_FAILED;
+                        goto out;
+                }
+
+                pv = pvl->pv;
+       }
+
+       if (is_orphan(pv))
+               size = pv_size(pv);
+       else
+               size = (pv_pe_count(pv) - pv_pe_alloc_count(pv)) *
+                       pv_pe_size(pv);
+
+       if (arg_count(cmd, short_ARG)) {
+               log_print("Device \"%s\" has a capacity of %s", pv_name,
+                         display_size(cmd, size));
+               goto out;
+       }
+
+       if (pv_status(pv) & EXPORTED_VG)
+               log_print("Physical volume \"%s\" of volume group \"%s\" "
+                         "is exported", pv_name, pv_vg_name(pv));
+
+       if (is_orphan(pv))
+               log_print("\"%s\" is a new physical volume of \"%s\"",
+                         pv_name, display_size(cmd, size));
+
+       if (arg_count(cmd, colon_ARG)) {
+               pvdisplay_colons(pv);
+               goto out;
+       }
+
+       pvdisplay_full(cmd, pv, handle);
+
+       if (arg_count(cmd, maps_ARG))
+               pvdisplay_segments(pv);
+
+out:
+       if (vg_name)
+               unlock_vg(cmd, vg_name);
+       if (!old_vg)
+               free_vg(vg);
+
+       return ret;
+}
+
+int pvdisplay(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (arg_count(cmd, columns_ARG)) {
+               if (arg_count(cmd, colon_ARG) || arg_count(cmd, maps_ARG) ||
+                   arg_count(cmd, short_ARG)) {
+                       log_error("Incompatible options selected");
+                       return EINVALID_CMD_LINE;
+               }
+               return pvs(cmd, argc, argv);
+       } else if (arg_count(cmd, aligned_ARG) ||
+                  arg_count(cmd, all_ARG) ||
+                  arg_count(cmd, noheadings_ARG) ||
+                  arg_count(cmd, options_ARG) ||
+                  arg_count(cmd, separator_ARG) ||
+                  arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) {
+               log_error("Incompatible options selected");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, colon_ARG) && arg_count(cmd, maps_ARG)) {
+               log_error("Option -v not allowed with option -c");
+               return EINVALID_CMD_LINE;
+       }
+
+       return process_each_pv(cmd, argc, argv, NULL, 0, 0, NULL,
+                              _pvdisplay_single);
+}
diff --git a/tools/pvmove.c b/tools/pvmove.c
new file mode 100644 (file)
index 0000000..51f442f
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "polldaemon.h"
+#include "display.h"
+
+#define PVMOVE_FIRST_TIME   0x00000001      /* Called for first time */
+
+static int _pvmove_target_present(struct cmd_context *cmd, int clustered)
+{
+       const struct segment_type *segtype;
+       unsigned attr = 0;
+       int found = 1;
+       static int _clustered_found = -1;
+
+       if (clustered && _clustered_found >= 0)
+               return _clustered_found;
+
+       if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+               return_0;
+
+       if (activation() && segtype->ops->target_present &&
+           !segtype->ops->target_present(cmd, NULL, clustered ? &attr : NULL))
+               found = 0;
+
+       if (activation() && clustered) {
+               if (found && (attr & MIRROR_LOG_CLUSTERED))
+                       _clustered_found = found = 1;
+               else
+                       _clustered_found = found = 0;
+       }
+
+       return found;
+}
+
+static unsigned _pvmove_is_exclusive(struct cmd_context *cmd,
+                                    struct volume_group *vg)
+{
+       if (vg_is_clustered(vg))
+               if (!_pvmove_target_present(cmd, 1))
+                       return 1;
+
+       return 0;
+}
+
+/* Allow /dev/vgname/lvname, vgname/lvname or lvname */
+static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
+                                  const char *arg)
+{
+       const char *lvname;
+
+       /* Is an lvname supplied directly? */
+       if (!strchr(arg, '/'))
+               return arg;
+
+       lvname = skip_dev_dir(cmd, arg, NULL);
+       while (*lvname == '/')
+               lvname++;
+       if (!strchr(lvname, '/')) {
+               log_error("--name takes a logical volume name");
+               return NULL;
+       }
+       if (strncmp(vgname, lvname, strlen(vgname)) ||
+           (lvname += strlen(vgname), *lvname != '/')) {
+               log_error("Named LV and old PV must be in the same VG");
+               return NULL;
+       }
+       while (*lvname == '/')
+               lvname++;
+       if (!*lvname) {
+               log_error("Incomplete LV name supplied with --name");
+               return NULL;
+       }
+       return lvname;
+}
+
+static struct volume_group *_get_vg(struct cmd_context *cmd, const char *vgname)
+{
+       dev_close_all();
+
+       return vg_read_for_update(cmd, vgname, NULL, 0);
+}
+
+/* Create list of PVs for allocation of replacement extents */
+static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
+                                        char **argv, struct volume_group *vg,
+                                        struct physical_volume *pv,
+                                        alloc_policy_t alloc)
+{
+       struct dm_list *allocatable_pvs, *pvht, *pvh;
+       struct pv_list *pvl;
+
+       if (argc)
+               allocatable_pvs = create_pv_list(cmd->mem, vg, argc, argv, 1);
+       else
+               allocatable_pvs = clone_pv_list(cmd->mem, &vg->pvs);
+
+       if (!allocatable_pvs)
+               return_NULL;
+
+       dm_list_iterate_safe(pvh, pvht, allocatable_pvs) {
+               pvl = dm_list_item(pvh, struct pv_list);
+
+               /* Don't allocate onto the PV we're clearing! */
+               if ((alloc != ALLOC_ANYWHERE) && (pvl->pv->dev == pv_dev(pv))) {
+                       dm_list_del(&pvl->list);
+                       continue;
+               }
+
+               /* Remove PV if full */
+               if ((pvl->pv->pe_count == pvl->pv->pe_alloc_count))
+                       dm_list_del(&pvl->list);
+       }
+
+       if (dm_list_empty(allocatable_pvs)) {
+               log_error("No extents available for allocation");
+               return NULL;
+       }
+
+       return allocatable_pvs;
+}
+
+/*
+ * Replace any LV segments on given PV with temporary mirror.
+ * Returns list of LVs changed.
+ */
+static int _insert_pvmove_mirrors(struct cmd_context *cmd,
+                                 struct logical_volume *lv_mirr,
+                                 struct dm_list *source_pvl,
+                                 struct logical_volume *lv,
+                                 struct dm_list *lvs_changed)
+
+{
+       struct pv_list *pvl;
+       uint32_t prev_le_count;
+
+       /* Only 1 PV may feature in source_pvl */
+       pvl = dm_list_item(source_pvl->n, struct pv_list);
+
+       prev_le_count = lv_mirr->le_count;
+       if (!insert_layer_for_segments_on_pv(cmd, lv, lv_mirr, PVMOVE,
+                                            pvl, lvs_changed))
+               return_0;
+
+       /* check if layer was inserted */
+       if (lv_mirr->le_count - prev_le_count) {
+               lv->status |= LOCKED;
+
+               log_verbose("Moving %u extents of logical volume %s/%s",
+                           lv_mirr->le_count - prev_le_count,
+                           lv->vg->name, lv->name);
+       }
+
+       return 1;
+}
+
+/* Create new LV with mirror segments for the required copies */
+static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
+                                               struct volume_group *vg,
+                                               struct dm_list *source_pvl,
+                                               const char *lv_name,
+                                               struct dm_list *allocatable_pvs,
+                                               alloc_policy_t alloc,
+                                               struct dm_list **lvs_changed)
+{
+       struct logical_volume *lv_mirr, *lv;
+       struct lv_list *lvl;
+       uint32_t log_count = 0;
+       int lv_found = 0;
+       int lv_skipped = 0;
+
+       /* FIXME Cope with non-contiguous => splitting existing segments */
+       if (!(lv_mirr = lv_create_empty("pvmove%d", NULL,
+                                       LVM_READ | LVM_WRITE,
+                                       ALLOC_CONTIGUOUS, vg))) {
+               log_error("Creation of temporary pvmove LV failed");
+               return NULL;
+       }
+
+       lv_mirr->status |= (PVMOVE | LOCKED);
+
+       if (!(*lvs_changed = dm_pool_alloc(cmd->mem, sizeof(**lvs_changed)))) {
+               log_error("lvs_changed list struct allocation failed");
+               return NULL;
+       }
+
+       dm_list_init(*lvs_changed);
+
+       /* Find segments to be moved and set up mirrors */
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv = lvl->lv;
+               if ((lv == lv_mirr))
+                       continue;
+               if (lv_name) {
+                       if (strcmp(lv->name, lv_name))
+                               continue;
+                       lv_found = 1;
+               }
+               if (lv_is_origin(lv) || lv_is_cow(lv)) {
+                       lv_skipped = 1;
+                       log_print("Skipping snapshot-related LV %s", lv->name);
+                       continue;
+               }
+               if (lv->status & MIRRORED) {
+                       lv_skipped = 1;
+                       log_print("Skipping mirror LV %s", lv->name);
+                       continue;
+               }
+               if (lv->status & MIRROR_LOG) {
+                       lv_skipped = 1;
+                       log_print("Skipping mirror log LV %s", lv->name);
+                       continue;
+               }
+               if (lv->status & MIRROR_IMAGE) {
+                       lv_skipped = 1;
+                       log_print("Skipping mirror image LV %s", lv->name);
+                       continue;
+               }
+               if (lv->status & LOCKED) {
+                       lv_skipped = 1;
+                       log_print("Skipping locked LV %s", lv->name);
+                       continue;
+               }
+               if (!_insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv,
+                                           *lvs_changed))
+                       return_NULL;
+       }
+
+       if (lv_name && !lv_found) {
+               log_error("Logical volume %s not found.", lv_name);
+               return NULL;
+       }
+
+       /* Is temporary mirror empty? */
+       if (!lv_mirr->le_count) {
+               if (lv_skipped)
+                       log_error("All data on source PV skipped. "
+                                 "It contains locked, hidden or "
+                                 "non-top level LVs only.");
+               log_error("No data to move for %s", vg->name);
+               return NULL;
+       }
+
+       if (!lv_add_mirrors(cmd, lv_mirr, 1, 1, 0, 0, log_count,
+                           allocatable_pvs, alloc, MIRROR_BY_SEG)) {
+               log_error("Failed to convert pvmove LV to mirrored");
+               return_NULL;
+       }
+
+       if (!split_parent_segments_for_layer(cmd, lv_mirr)) {
+               log_error("Failed to split segments being moved");
+               return_NULL;
+       }
+
+       return lv_mirr;
+}
+
+static int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr,
+                       unsigned exclusive)
+{
+       int r = 0;
+
+       if (exclusive)
+               r = activate_lv_excl(cmd, lv_mirr);
+       else
+               r = activate_lv(cmd, lv_mirr);
+
+       if (!r)
+               stack;
+
+       return r;
+}
+
+static int _detach_pvmove_mirror(struct cmd_context *cmd,
+                                struct logical_volume *lv_mirr)
+{
+       struct dm_list lvs_completed;
+       struct lv_list *lvl;
+
+       /* Update metadata to remove mirror segments and break dependencies */
+       dm_list_init(&lvs_completed);
+       if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, NULL, NULL, PVMOVE) ||
+           !remove_layers_for_segments_all(cmd, lv_mirr, PVMOVE,
+                                           &lvs_completed)) {
+               return 0;
+       }
+
+       dm_list_iterate_items(lvl, &lvs_completed)
+               /* FIXME Assumes only one pvmove at a time! */
+               lvl->lv->status &= ~LOCKED;
+
+       return 1;
+}
+
+static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg,
+                           struct logical_volume *lv_mirr,
+                           struct dm_list *lvs_changed, unsigned flags)
+{
+       unsigned exclusive = _pvmove_is_exclusive(cmd, vg);
+       unsigned first_time = (flags & PVMOVE_FIRST_TIME) ? 1 : 0;
+       int r = 0;
+
+       log_verbose("Updating volume group metadata");
+       if (!vg_write(vg)) {
+               log_error("ABORTING: Volume group metadata update failed.");
+               return 0;
+       }
+
+       /* Suspend lvs_changed */
+       if (!suspend_lvs(cmd, lvs_changed)) {
+               vg_revert(vg);
+               goto_out;
+       }
+
+       /* Suspend mirrors on subsequent calls */
+       if (!first_time) {
+               if (!suspend_lv(cmd, lv_mirr)) {
+                       if (!resume_lvs(cmd, lvs_changed))
+                               stack;
+                       vg_revert(vg);
+                       goto_out;
+               }
+       }
+
+       /* Commit on-disk metadata */
+       if (!vg_commit(vg)) {
+               log_error("ABORTING: Volume group metadata update failed.");
+               if (!first_time)
+                       if (!resume_lv(cmd, lv_mirr))
+                               stack;
+               if (!resume_lvs(cmd, lvs_changed))
+                       stack;
+               vg_revert(vg);
+               goto out;
+       }
+
+       /* Activate the temporary mirror LV */
+       /* Only the first mirror segment gets activated as a mirror */
+       /* FIXME: Add option to use a log */
+       if (first_time) {
+               if (!_activate_lv(cmd, lv_mirr, exclusive)) {
+                       if (test_mode()) {
+                               r = 1;
+                               goto out;
+                       }
+
+                       /*
+                        * FIXME: review ordering of operations above,
+                        * temporary mirror should be preloaded in suspend.
+                        * Also banned operation here when suspended.
+                        * Nothing changed yet, try to revert pvmove.
+                        */
+                       log_error("Temporary pvmove mirror activation failed.");
+
+                       /* Ensure that temporary mrror is deactivate even on other nodes. */
+                       (void)deactivate_lv(cmd, lv_mirr);
+
+                       /* Revert metadata */
+                       if (!_detach_pvmove_mirror(cmd, lv_mirr) ||
+                           !lv_remove(lv_mirr) ||
+                           !vg_write(vg) || !vg_commit(vg))
+                               log_error("ABORTING: Restoring original configuration "
+                                         "before pvmove failed. Run pvmove --abort.");
+
+                       /* Unsuspend LVs */
+                       if(!resume_lvs(cmd, lvs_changed))
+                               stack;
+
+                       goto out;
+               }
+       } else if (!resume_lv(cmd, lv_mirr)) {
+               log_error("Unable to reactivate logical volume \"%s\"",
+                         lv_mirr->name);
+               if (!resume_lvs(cmd, lvs_changed))
+                       stack;
+               goto out;
+       }
+
+       /* Unsuspend LVs */
+       if (!resume_lvs(cmd, lvs_changed)) {
+               log_error("Unable to resume logical volumes");
+               goto out;
+       }
+
+       r = 1;
+out:
+       backup(vg);
+       return r;
+}
+
+static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
+                         int argc, char **argv)
+{
+       const char *lv_name = NULL;
+       char *pv_name_arg;
+       struct volume_group *vg;
+       struct dm_list *source_pvl;
+       struct dm_list *allocatable_pvs;
+       alloc_policy_t alloc;
+       struct dm_list *lvs_changed;
+       struct physical_volume *pv;
+       struct logical_volume *lv_mirr;
+       unsigned first_time = 1;
+       unsigned exclusive;
+       int r = ECMD_FAILED;
+
+       pv_name_arg = argv[0];
+       argc--;
+       argv++;
+
+       /* Find PV (in VG) */
+       if (!(pv = find_pv_by_name(cmd, pv_name))) {
+               stack;
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, name_ARG)) {
+               if (!(lv_name = _extract_lvname(cmd, pv_vg_name(pv),
+                                               arg_value(cmd, name_ARG)))) {
+                       stack;
+                       return EINVALID_CMD_LINE;
+               }
+
+               if (!validate_name(lv_name)) {
+                       log_error("Logical volume name %s is invalid", lv_name);
+                       return EINVALID_CMD_LINE;
+               }
+       }
+
+       /* Read VG */
+       log_verbose("Finding volume group \"%s\"", pv_vg_name(pv));
+
+       vg = _get_vg(cmd, pv_vg_name(pv));
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               stack;
+               return ECMD_FAILED;
+       }
+
+       exclusive = _pvmove_is_exclusive(cmd, vg);
+
+       if ((lv_mirr = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) {
+               log_print("Detected pvmove in progress for %s", pv_name);
+               if (argc || lv_name)
+                       log_error("Ignoring remaining command line arguments");
+
+               if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) {
+                       log_error("ABORTING: Failed to generate list of moving LVs");
+                       goto out;
+               }
+
+               /* Ensure mirror LV is active */
+               if (!_activate_lv(cmd, lv_mirr, exclusive)) {
+                       log_error("ABORTING: Temporary mirror activation failed.");
+                       goto out;
+               }
+
+               first_time = 0;
+       } else {
+               /* Determine PE ranges to be moved */
+               if (!(source_pvl = create_pv_list(cmd->mem, vg, 1,
+                                                 &pv_name_arg, 0)))
+                       goto_out;
+
+               alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
+               if (alloc == ALLOC_INHERIT)
+                       alloc = vg->alloc;
+
+               /* Get PVs we can use for allocation */
+               if (!(allocatable_pvs = _get_allocatable_pvs(cmd, argc, argv,
+                                                            vg, pv, alloc)))
+                       goto_out;
+
+               if (!archive(vg))
+                       goto_out;
+
+               if (!(lv_mirr = _set_up_pvmove_lv(cmd, vg, source_pvl, lv_name,
+                                                 allocatable_pvs, alloc,
+                                                 &lvs_changed)))
+                       goto_out;
+       }
+
+       /* Lock lvs_changed and activate (with old metadata) */
+       if (!activate_lvs(cmd, lvs_changed, exclusive))
+               goto_out;
+
+       /* FIXME Presence of a mirror once set PVMOVE - now remove associated logic */
+       /* init_pvmove(1); */
+       /* vg->status |= PVMOVE; */
+
+       if (first_time) {
+               if (!_update_metadata
+                   (cmd, vg, lv_mirr, lvs_changed, PVMOVE_FIRST_TIME))
+                       goto_out;
+       }
+
+       /* LVs are all in status LOCKED */
+       r = ECMD_PROCESSED;
+out:
+       unlock_and_free_vg(cmd, vg, pv_vg_name(pv));
+       return r;
+}
+
+static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg,
+                         struct logical_volume *lv_mirr,
+                         struct dm_list *lvs_changed)
+{
+       int r = 1;
+
+       if (!dm_list_empty(lvs_changed) &&
+           !_detach_pvmove_mirror(cmd, lv_mirr)) {
+               log_error("ABORTING: Removal of temporary mirror failed");
+               return 0;
+       }
+
+       /* Store metadata without dependencies on mirror segments */
+       if (!vg_write(vg)) {
+               log_error("ABORTING: Failed to write new data locations "
+                         "to disk.");
+               return 0;
+       }
+
+       /* Suspend LVs changed */
+       if (!suspend_lvs(cmd, lvs_changed)) {
+               log_error("Locking LVs to remove temporary mirror failed");
+               r = 0;
+       }
+
+       /* Suspend mirror LV to flush pending I/O */
+       if (!suspend_lv(cmd, lv_mirr)) {
+               log_error("Suspension of temporary mirror LV failed");
+               r = 0;
+       }
+
+       /* Store metadata without dependencies on mirror segments */
+       if (!vg_commit(vg)) {
+               log_error("ABORTING: Failed to write new data locations "
+                         "to disk.");
+               vg_revert(vg);
+               if (!resume_lv(cmd, lv_mirr))
+                       stack;
+               if (!resume_lvs(cmd, lvs_changed))
+                       stack;
+               return 0;
+       }
+
+       /* Release mirror LV.  (No pending I/O because it's been suspended.) */
+       if (!resume_lv(cmd, lv_mirr)) {
+               log_error("Unable to reactivate logical volume \"%s\"",
+                         lv_mirr->name);
+               r = 0;
+       }
+
+       /* Unsuspend LVs */
+       if (!resume_lvs(cmd, lvs_changed))
+               stack;
+
+       /* Deactivate mirror LV */
+       if (!deactivate_lv(cmd, lv_mirr)) {
+               log_error("ABORTING: Unable to deactivate temporary logical "
+                         "volume \"%s\"", lv_mirr->name);
+               r = 0;
+       }
+
+       log_verbose("Removing temporary pvmove LV");
+       if (!lv_remove(lv_mirr)) {
+               log_error("ABORTING: Removal of temporary pvmove LV failed");
+               return 0;
+       }
+
+       /* Store it on disks */
+       log_verbose("Writing out final volume group after pvmove");
+       if (!vg_write(vg) || !vg_commit(vg)) {
+               log_error("ABORTING: Failed to write new data locations "
+                         "to disk.");
+               return 0;
+       }
+
+       /* FIXME backup positioning */
+       backup(vg);
+
+       return r;
+}
+
+static struct volume_group *_get_move_vg(struct cmd_context *cmd,
+                                        const char *name,
+                                        const char *uuid __attribute__((unused)))
+{
+       struct physical_volume *pv;
+
+       /* Reread all metadata in case it got changed */
+       if (!(pv = find_pv_by_name(cmd, name))) {
+               log_error("ABORTING: Can't reread PV %s", name);
+               /* What more could we do here? */
+               return NULL;
+       }
+
+       return _get_vg(cmd, pv_vg_name(pv));
+}
+
+static struct poll_functions _pvmove_fns = {
+       .get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr,
+       .get_copy_vg = _get_move_vg,
+       .get_copy_lv = find_pvmove_lv_from_pvname,
+       .poll_progress = poll_mirror_progress,
+       .update_metadata = _update_metadata,
+       .finish_copy = _finish_pvmove,
+};
+
+int pvmove_poll(struct cmd_context *cmd, const char *pv_name,
+               unsigned background)
+{
+       if (test_mode())
+               return ECMD_PROCESSED;
+
+       return poll_daemon(cmd, pv_name, NULL, background, PVMOVE, &_pvmove_fns,
+                          "Moved");
+}
+
+int pvmove(struct cmd_context *cmd, int argc, char **argv)
+{
+       char *pv_name = NULL;
+       char *colon;
+       int ret;
+
+       /* dm raid1 target must be present in every case */
+       if (!_pvmove_target_present(cmd, 0)) {
+               log_error("Required device-mapper target(s) not "
+                         "detected in your kernel");
+               return ECMD_FAILED;
+       }
+
+       if (argc) {
+               if (!(pv_name = dm_pool_strdup(cmd->mem, argv[0]))) {
+                       log_error("Failed to clone PV name");
+                       return ECMD_FAILED;
+               }
+
+               unescape_colons_and_at_signs(pv_name, &colon, NULL);
+
+               /* Drop any PE lists from PV name */
+               if (colon)
+                       *colon = '\0';
+
+               if (!arg_count(cmd, abort_ARG) &&
+                   (ret = _set_up_pvmove(cmd, pv_name, argc, argv)) !=
+                   ECMD_PROCESSED) {
+                       stack;
+                       return ret;
+               }
+       }
+
+       return pvmove_poll(cmd, pv_name, arg_is_set(cmd, background_ARG));
+}
diff --git a/tools/pvremove.c b/tools/pvremove.c
new file mode 100644 (file)
index 0000000..a8717e0
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+const char _really_wipe[] =
+    "Really WIPE LABELS from physical volume \"%s\" of volume group \"%s\" [y/n]? ";
+
+/*
+ * Decide whether it is "safe" to wipe the labels on this device.
+ * 0 indicates we may not.
+ */
+static int pvremove_check(struct cmd_context *cmd, const char *name)
+{
+       struct physical_volume *pv;
+       struct dm_list mdas;
+
+       dm_list_init(&mdas);
+
+       /* FIXME Check partition type is LVM unless --force is given */
+
+       /* Is there a pv here already? */
+       /* If not, this is an error unless you used -f. */
+       if (!(pv = pv_read(cmd, name, &mdas, NULL, 1, 0))) {
+               if (arg_count(cmd, force_ARG))
+                       return 1;
+               log_error("Physical Volume %s not found", name);
+               return 0;
+       }
+
+       /*
+        * If a PV has no MDAs it may appear to be an
+        * orphan until the metadata is read off
+        * another PV in the same VG.  Detecting this
+        * means checking every VG by scanning every
+        * PV on the system.
+        */
+       if (is_orphan(pv) && !dm_list_size(&mdas)) {
+               if (!scan_vgs_for_pvs(cmd, 0)) {
+                       log_error("Rescan for PVs without metadata areas "
+                                 "failed.");
+                       return 0;
+               }
+               if (!(pv = pv_read(cmd, name, NULL, NULL, 1, 0))) {
+                       log_error("Failed to read physical volume %s", name);
+                       return 0;
+               }
+       }
+
+       /* orphan ? */
+       if (is_orphan(pv))
+               return 1;
+
+       /* Allow partial & exported VGs to be destroyed. */
+       /* we must have -ff to overwrite a non orphan */
+       if (arg_count(cmd, force_ARG) < 2) {
+               log_error("Can't pvremove physical volume \"%s\" of "
+                         "volume group \"%s\" without -ff", name, pv_vg_name(pv));
+               return 0;
+       }
+
+       /* prompt */
+       if (!arg_count(cmd, yes_ARG) &&
+           yes_no_prompt(_really_wipe, name, pv_vg_name(pv)) == 'n') {
+               log_error("%s: physical volume label not removed", name);
+               return 0;
+       }
+
+       if (arg_count(cmd, force_ARG)) {
+               log_warn("WARNING: Wiping physical volume label from "
+                         "%s%s%s%s", name,
+                         !is_orphan(pv) ? " of volume group \"" : "",
+                         !is_orphan(pv) ? pv_vg_name(pv) : "",
+                         !is_orphan(pv) ? "\"" : "");
+       }
+
+       return 1;
+}
+
+static int pvremove_single(struct cmd_context *cmd, const char *pv_name,
+                          void *handle __attribute__((unused)))
+{
+       struct device *dev;
+       int ret = ECMD_FAILED;
+
+       if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+               log_error("Can't get lock for orphan PVs");
+               return ECMD_FAILED;
+       }
+
+       if (!pvremove_check(cmd, pv_name))
+               goto error;
+
+       if (!(dev = dev_cache_get(pv_name, cmd->filter))) {
+               log_error("%s: Couldn't find device.  Check your filters?",
+                         pv_name);
+               goto error;
+       }
+
+       if (!dev_test_excl(dev)) {
+               /* FIXME Detect whether device-mapper is still using the device */
+               log_error("Can't open %s exclusively - not removing. "
+                         "Mounted filesystem?", dev_name(dev));
+               goto error;
+       }
+
+       /* Wipe existing label(s) */
+       if (!label_remove(dev)) {
+               log_error("Failed to wipe existing label(s) on %s", pv_name);
+               goto error;
+       }
+
+       log_print("Labels on physical volume \"%s\" successfully wiped",
+                 pv_name);
+
+       ret = ECMD_PROCESSED;
+
+      error:
+       unlock_vg(cmd, VG_ORPHANS);
+
+       return ret;
+}
+
+int pvremove(struct cmd_context *cmd, int argc, char **argv)
+{
+       int i, r;
+       int ret = ECMD_PROCESSED;
+
+       if (!argc) {
+               log_error("Please enter a physical volume path");
+               return EINVALID_CMD_LINE;
+       }
+
+       for (i = 0; i < argc; i++) {
+               unescape_colons_and_at_signs(argv[i], NULL, NULL);
+               r = pvremove_single(cmd, argv[i], NULL);
+               if (r > ret)
+                       ret = r;
+       }
+
+       return ret;
+}
diff --git a/tools/pvresize.c b/tools/pvresize.c
new file mode 100644 (file)
index 0000000..8582ef4
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005 Zak Kipling. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+struct pvresize_params {
+       uint64_t new_size;
+
+       unsigned done;
+       unsigned total;
+};
+
+static int _pv_resize_single(struct cmd_context *cmd,
+                            struct volume_group *vg,
+                            struct physical_volume *pv,
+                            const uint64_t new_size)
+{
+       struct pv_list *pvl;
+       uint64_t size = 0;
+       uint32_t new_pe_count = 0;
+       int r = 0;
+       struct dm_list mdas;
+       const char *pv_name = pv_dev_name(pv);
+       const char *vg_name = pv_vg_name(pv);
+       struct lvmcache_info *info;
+       int mda_count = 0;
+       struct volume_group *old_vg = vg;
+
+       dm_list_init(&mdas);
+
+       if (is_orphan_vg(vg_name)) {
+               if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
+                       log_error("Can't get lock for orphans");
+                       return 0;
+               }
+
+               if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1, 0))) {
+                       unlock_vg(cmd, vg_name);
+                       log_error("Unable to read PV \"%s\"", pv_name);
+                       return 0;
+               }
+
+               mda_count = dm_list_size(&mdas);
+       } else {
+               vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+
+               if (vg_read_error(vg)) {
+                       free_vg(vg);
+                       log_error("Unable to read volume group \"%s\".",
+                                 vg_name);
+                       return 0;
+               }
+
+               if (!(pvl = find_pv_in_vg(vg, pv_name))) {
+                       log_error("Unable to find \"%s\" in volume group \"%s\"",
+                                 pv_name, vg->name);
+                       goto out;
+               }
+
+               pv = pvl->pv;
+
+               if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
+                       log_error("Can't get info for PV %s in volume group %s",
+                                 pv_name, vg->name);
+                       goto out;
+               }
+
+               mda_count = dm_list_size(&info->mdas);
+
+               if (!archive(vg))
+                       goto out;
+       }
+
+       /* FIXME Create function to test compatibility properly */
+       if (mda_count > 1) {
+               log_error("%s: too many metadata areas for pvresize", pv_name);
+               goto out;
+       }
+
+       if (!(pv->fmt->features & FMT_RESIZE_PV)) {
+               log_error("Physical volume %s format does not support resizing.",
+                         pv_name);
+               goto out;
+       }
+
+       /* Get new size */
+       if (!dev_get_size(pv_dev(pv), &size)) {
+               log_error("%s: Couldn't get size.", pv_name);
+               goto out;
+       }
+
+       if (new_size) {
+               if (new_size > size)
+                       log_warn("WARNING: %s: Overriding real size. "
+                                 "You could lose data.", pv_name);
+               log_verbose("%s: Pretending size is %" PRIu64 " not %" PRIu64
+                           " sectors.", pv_name, new_size, pv_size(pv));
+               size = new_size;
+       }
+
+       if (size < PV_MIN_SIZE) {
+               log_error("%s: Size must exceed minimum of %ld sectors.",
+                         pv_name, PV_MIN_SIZE);
+               goto out;
+       }
+
+       if (size < pv_pe_start(pv)) {
+               log_error("%s: Size must exceed physical extent start of "
+                         "%" PRIu64 " sectors.", pv_name, pv_pe_start(pv));
+               goto out;
+       }
+
+       pv->size = size;
+
+       if (vg) {
+               pv->size -= pv_pe_start(pv);
+               new_pe_count = pv_size(pv) / vg->extent_size;
+
+               if (!new_pe_count) {
+                       log_error("%s: Size must leave space for at "
+                                 "least one physical extent of "
+                                 "%" PRIu32 " sectors.", pv_name,
+                                 pv_pe_size(pv));
+                       goto out;
+               }
+
+               if (!pv_resize(pv, vg, new_pe_count))
+                       goto_out;
+       }
+
+       log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.",
+                   pv_name, pv_size(pv));
+
+       log_verbose("Updating physical volume \"%s\"", pv_name);
+       if (!is_orphan_vg(vg_name)) {
+               if (!vg_write(vg) || !vg_commit(vg)) {
+                       log_error("Failed to store physical volume \"%s\" in "
+                                 "volume group \"%s\"", pv_name, vg_name);
+                       goto out;
+               }
+               backup(vg);
+       } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
+               log_error("Failed to store physical volume \"%s\"",
+                         pv_name);
+               goto out;
+       }
+
+       log_print("Physical volume \"%s\" changed", pv_name);
+       r = 1;
+
+out:
+       unlock_vg(cmd, vg_name);
+       if (!old_vg)
+               free_vg(vg);
+       return r;
+}
+
+static int _pvresize_single(struct cmd_context *cmd,
+                           struct volume_group *vg,
+                           struct physical_volume *pv,
+                           void *handle)
+{
+       struct pvresize_params *params = (struct pvresize_params *) handle;
+
+       params->total++;
+
+       if (!_pv_resize_single(cmd, vg, pv, params->new_size)) {
+               stack;
+               return ECMD_FAILED;
+       }
+       
+       params->done++;
+
+       return ECMD_PROCESSED;
+}
+
+int pvresize(struct cmd_context *cmd, int argc, char **argv)
+{
+       struct pvresize_params params;
+       int ret;
+
+       if (!argc) {
+               log_error("Please supply physical volume(s)");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) {
+               log_error("Physical volume size may not be negative");
+               return 0;
+       }
+
+       params.new_size = arg_uint64_value(cmd, physicalvolumesize_ARG,
+                                          UINT64_C(0));
+
+       params.done = 0;
+       params.total = 0;
+
+       ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, 0, &params,
+                             _pvresize_single);
+
+       log_print("%d physical volume(s) resized / %d physical volume(s) "
+                 "not resized", params.done, params.total - params.done);
+
+       return ret;
+}
diff --git a/tools/pvscan.c b/tools/pvscan.c
new file mode 100644 (file)
index 0000000..b24b7ab
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int pv_max_name_len = 0;
+int vg_max_name_len = 0;
+
+static void _pvscan_display_single(struct cmd_context *cmd,
+                                  struct physical_volume *pv,
+                                  void *handle __attribute__((unused)))
+{
+       char uuid[64] __attribute__((aligned(8)));
+       unsigned vg_name_len = 0;
+
+       char pv_tmp_name[NAME_LEN] = { 0, };
+       char vg_tmp_name[NAME_LEN] = { 0, };
+       char vg_name_this[NAME_LEN] = { 0, };
+
+       /* short listing? */
+       if (arg_count(cmd, short_ARG) > 0) {
+               log_print("%s", pv_dev_name(pv));
+               return;
+       }
+
+       if (arg_count(cmd, verbose_ARG) > 1) {
+               /* FIXME As per pv_display! Drop through for now. */
+               /* pv_show(pv); */
+
+               /* FIXME - Moved to Volume Group structure */
+               /* log_print("System Id             %s", pv->vg->system_id); */
+
+               /* log_print(" "); */
+               /* return; */
+       }
+
+       memset(pv_tmp_name, 0, sizeof(pv_tmp_name));
+
+       vg_name_len = strlen(pv_vg_name(pv)) + 1;
+
+       if (arg_count(cmd, uuid_ARG)) {
+               if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+                       stack;
+                       return;
+               }
+
+               sprintf(pv_tmp_name, "%-*s with UUID %s",
+                       pv_max_name_len - 2, pv_dev_name(pv), uuid);
+       } else {
+               sprintf(pv_tmp_name, "%s", pv_dev_name(pv));
+       }
+
+       if (is_orphan(pv)) {
+               log_print("PV %-*s    %-*s %s [%s]",
+                         pv_max_name_len, pv_tmp_name,
+                         vg_max_name_len, " ",
+                         pv->fmt ? pv->fmt->name : "    ",
+                         display_size(cmd, pv_size(pv)));
+               return;
+       }
+
+       if (pv_status(pv) & EXPORTED_VG) {
+               strncpy(vg_name_this, pv_vg_name(pv), vg_name_len);
+               log_print("PV %-*s  is in exported VG %s "
+                         "[%s / %s free]",
+                         pv_max_name_len, pv_tmp_name,
+                         vg_name_this,
+                         display_size(cmd, (uint64_t) pv_pe_count(pv) *
+                                      pv_pe_size(pv)),
+                         display_size(cmd, (uint64_t) (pv_pe_count(pv) -
+                                               pv_pe_alloc_count(pv))
+                                      * pv_pe_size(pv)));
+               return;
+       }
+
+       sprintf(vg_tmp_name, "%s", pv_vg_name(pv));
+       log_print("PV %-*s VG %-*s %s [%s / %s free]", pv_max_name_len,
+                 pv_tmp_name, vg_max_name_len, vg_tmp_name,
+                 pv->fmt ? pv->fmt->name : "    ",
+                 display_size(cmd, (uint64_t) pv_pe_count(pv) *
+                                              pv_pe_size(pv)),
+                 display_size(cmd, (uint64_t) (pv_pe_count(pv) -
+                                               pv_pe_alloc_count(pv)) *
+                                          pv_pe_size(pv)));
+}
+
+int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)),
+          char **argv __attribute__((unused)))
+{
+       int new_pvs_found = 0;
+       int pvs_found = 0;
+
+       struct dm_list *pvslist;
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+
+       uint64_t size_total = 0;
+       uint64_t size_new = 0;
+
+       int len = 0;
+       pv_max_name_len = 0;
+       vg_max_name_len = 0;
+
+       if (arg_count(cmd, novolumegroup_ARG) && arg_count(cmd, exported_ARG)) {
+               log_error("Options -e and -n are incompatible");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, exported_ARG) || arg_count(cmd, novolumegroup_ARG))
+               log_warn("WARNING: only considering physical volumes %s",
+                         arg_count(cmd, exported_ARG) ?
+                         "of exported volume group(s)" : "in no volume group");
+
+       if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE)) {
+               log_error("Unable to obtain global lock.");
+               return ECMD_FAILED;
+       }
+
+       persistent_filter_wipe(cmd->filter);
+       lvmcache_destroy(cmd, 1);
+
+       log_verbose("Walking through all physical volumes");
+       if (!(pvslist = get_pvs(cmd))) {
+               unlock_vg(cmd, VG_GLOBAL);
+               stack;
+               return ECMD_FAILED;
+       }
+
+       /* eliminate exported/new if required */
+       dm_list_iterate_items(pvl, pvslist) {
+               pv = pvl->pv;
+
+               if ((arg_count(cmd, exported_ARG)
+                    && !(pv_status(pv) & EXPORTED_VG))
+                   || (arg_count(cmd, novolumegroup_ARG) && (!is_orphan(pv)))) {
+                       dm_list_del(&pvl->list);
+                       continue;
+               }
+
+               /* Also check for MD use? */
+/*******
+               if (MAJOR(pv_create_kdev_t(pv[p]->pv_name)) != MD_MAJOR) {
+                       log_warn
+                           ("WARNING: physical volume \"%s\" belongs to a meta device",
+                            pv[p]->pv_name);
+               }
+               if (MAJOR(pv[p]->pv_dev) != MD_MAJOR)
+                       continue;
+********/
+               pvs_found++;
+
+               if (is_orphan(pv)) {
+                       new_pvs_found++;
+                       size_new += pv_size(pv);
+                       size_total += pv_size(pv);
+               } else
+                       size_total += (uint64_t) pv_pe_count(pv) * pv_pe_size(pv);
+       }
+
+       /* find maximum pv name length */
+       pv_max_name_len = vg_max_name_len = 0;
+       dm_list_iterate_items(pvl, pvslist) {
+               pv = pvl->pv;
+               len = strlen(pv_dev_name(pv));
+               if (pv_max_name_len < len)
+                       pv_max_name_len = len;
+               len = strlen(pv_vg_name(pv));
+               if (vg_max_name_len < len)
+                       vg_max_name_len = len;
+       }
+       pv_max_name_len += 2;
+       vg_max_name_len += 2;
+
+       dm_list_iterate_items(pvl, pvslist)
+           _pvscan_display_single(cmd, pvl->pv, NULL);
+
+       if (!pvs_found) {
+               log_print("No matching physical volumes found");
+               unlock_vg(cmd, VG_GLOBAL);
+               return ECMD_PROCESSED;
+       }
+
+       log_print("Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]",
+                 pvs_found,
+                 display_size(cmd, size_total),
+                 pvs_found - new_pvs_found,
+                 display_size(cmd, (size_total - size_new)),
+                 new_pvs_found, display_size(cmd, size_new));
+
+       unlock_vg(cmd, VG_GLOBAL);
+
+       return ECMD_PROCESSED;
+}
diff --git a/tools/reporter.c b/tools/reporter.c
new file mode 100644 (file)
index 0000000..fbd0c27
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "report.h"
+
+static int _vgs_single(struct cmd_context *cmd __attribute__((unused)),
+                      const char *vg_name, struct volume_group *vg,
+                      void *handle)
+{
+       if (!report_object(handle, vg, NULL, NULL, NULL, NULL)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       check_current_backup(vg);
+
+       return ECMD_PROCESSED;
+}
+
+static int _lvs_single(struct cmd_context *cmd, struct logical_volume *lv,
+                      void *handle)
+{
+       if (!report_object(handle, lv->vg, lv, NULL, NULL, NULL)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+static int _segs_single(struct cmd_context *cmd __attribute__((unused)),
+                       struct lv_segment *seg, void *handle)
+{
+       if (!report_object(handle, seg->lv->vg, seg->lv, NULL, seg, NULL)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+static int _pvsegs_sub_single(struct cmd_context *cmd,
+                             struct volume_group *vg,
+                             struct pv_segment *pvseg, void *handle)
+{
+       int ret = ECMD_PROCESSED;
+       struct lv_segment *seg = pvseg->lvseg;
+
+       struct volume_group _free_vg = {
+               .cmd = cmd,
+               .name = (char *)"",
+       };
+
+        if (!(_free_vg.vgmem = dm_pool_create("_free_vg", 10240)))
+               return ECMD_FAILED;
+
+       struct logical_volume _free_logical_volume = {
+               .vg = vg ?: &_free_vg,
+               .name = (char *) "",
+               .snapshot = NULL,
+               .status = VISIBLE_LV,
+               .major = -1,
+               .minor = -1,
+       };
+
+       struct lv_segment _free_lv_segment = {
+               .lv = &_free_logical_volume,
+               .le = 0,
+               .status = 0,
+               .stripe_size = 0,
+               .area_count = 0,
+               .area_len = 0,
+               .origin = NULL,
+               .cow = NULL,
+               .chunk_size = 0,
+               .region_size = 0,
+               .extents_copied = 0,
+               .log_lv = NULL,
+               .areas = NULL,
+       };
+
+       _free_lv_segment.segtype = get_segtype_from_string(cmd, "free");
+       _free_lv_segment.len = pvseg->len;
+       dm_list_init(&_free_vg.pvs);
+       dm_list_init(&_free_vg.lvs);
+       dm_list_init(&_free_vg.tags);
+       dm_list_init(&_free_lv_segment.tags);
+       dm_list_init(&_free_lv_segment.origin_list);
+       dm_list_init(&_free_logical_volume.tags);
+       dm_list_init(&_free_logical_volume.segments);
+       dm_list_init(&_free_logical_volume.segs_using_this_lv);
+       dm_list_init(&_free_logical_volume.snapshot_segs);
+
+       if (!report_object(handle, vg, seg ? seg->lv : &_free_logical_volume, pvseg->pv,
+                          seg ? : &_free_lv_segment, pvseg)) {
+               ret = ECMD_FAILED;
+                goto_out;
+       }
+ out:
+       free_vg(&_free_vg);
+       return ret;
+}
+
+static int _lvsegs_single(struct cmd_context *cmd, struct logical_volume *lv,
+                         void *handle)
+{
+       if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
+               return ECMD_PROCESSED;
+
+       return process_each_segment_in_lv(cmd, lv, handle, _segs_single);
+}
+
+static int _pvsegs_single(struct cmd_context *cmd, struct volume_group *vg,
+                         struct physical_volume *pv, void *handle)
+{
+       return process_each_segment_in_pv(cmd, vg, pv, handle,
+                                         _pvsegs_sub_single);
+}
+
+static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg,
+                      struct physical_volume *pv, void *handle)
+{
+       struct pv_list *pvl;
+       int ret = ECMD_PROCESSED;
+       const char *vg_name = NULL;
+       struct volume_group *old_vg = vg;
+       char uuid[64] __attribute__((aligned(8)));
+
+       if (is_pv(pv) && !is_orphan(pv) && !vg) {
+               vg_name = pv_vg_name(pv);
+
+               vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0);
+               if (vg_read_error(vg)) {
+                       log_error("Skipping volume group %s", vg_name);
+                       free_vg(vg);
+                       return ECMD_FAILED;
+               }
+
+               /*
+                * Replace possibly incomplete PV structure with new one
+                * allocated in vg_read.
+               */
+               if (!is_missing_pv(pv)) {
+                       if (!(pvl = find_pv_in_vg(vg, pv_dev_name(pv)))) {
+                               log_error("Unable to find \"%s\" in volume group \"%s\"",
+                                         pv_dev_name(pv), vg->name);
+                               ret = ECMD_FAILED;
+                               goto out;
+                       }
+               } else if (!(pvl = find_pv_in_vg_by_uuid(vg, &pv->id))) {
+                       if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+                               stack;
+                               uuid[0] = '\0';
+                       }
+
+                       log_error("Unable to find missing PV %s in volume group %s",
+                                 uuid, vg->name);
+                       ret = ECMD_FAILED;
+                       goto out;
+               }
+
+               pv = pvl->pv;
+       }
+
+       if (!report_object(handle, vg, NULL, pv, NULL, NULL)) {
+               stack;
+               ret = ECMD_FAILED;
+       }
+
+out:
+       if (vg_name)
+               unlock_vg(cmd, vg_name);
+
+       if (!old_vg)
+               free_vg(vg);
+
+       return ret;
+}
+
+static int _label_single(struct cmd_context *cmd, struct volume_group *vg,
+                      struct physical_volume *pv, void *handle)
+{
+       if (!report_object(handle, vg, NULL, pv, NULL, NULL)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+static int _pvs_in_vg(struct cmd_context *cmd, const char *vg_name,
+                     struct volume_group *vg,
+                     void *handle)
+{
+       if (vg_read_error(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvs_single);
+}
+
+static int _pvsegs_in_vg(struct cmd_context *cmd, const char *vg_name,
+                        struct volume_group *vg,
+                        void *handle)
+{
+       if (vg_read_error(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvsegs_single);
+}
+
+static int _report(struct cmd_context *cmd, int argc, char **argv,
+                  report_type_t report_type)
+{
+       void *report_handle;
+       const char *opts;
+       char *str;
+       const char *keys = NULL, *options = NULL, *separator;
+       int r = ECMD_PROCESSED;
+       int aligned, buffered, headings, field_prefixes, quoted;
+       int columns_as_rows;
+       unsigned args_are_pvs;
+
+       aligned = find_config_tree_int(cmd, "report/aligned",
+                                 DEFAULT_REP_ALIGNED);
+       buffered = find_config_tree_int(cmd, "report/buffered",
+                                  DEFAULT_REP_BUFFERED);
+       headings = find_config_tree_int(cmd, "report/headings",
+                                  DEFAULT_REP_HEADINGS);
+       separator = find_config_tree_str(cmd, "report/separator",
+                                   DEFAULT_REP_SEPARATOR);
+       field_prefixes = find_config_tree_int(cmd, "report/prefixes",
+                                             DEFAULT_REP_PREFIXES);
+       quoted = find_config_tree_int(cmd, "report/quoted",
+                                    DEFAULT_REP_QUOTED);
+       columns_as_rows = find_config_tree_int(cmd, "report/columns_as_rows",
+                                              DEFAULT_REP_COLUMNS_AS_ROWS);
+
+       args_are_pvs = (report_type == PVS ||
+                       report_type == LABEL ||
+                       report_type == PVSEGS) ? 1 : 0;
+
+       switch (report_type) {
+       case LVS:
+               keys = find_config_tree_str(cmd, "report/lvs_sort",
+                                      DEFAULT_LVS_SORT);
+               if (!arg_count(cmd, verbose_ARG))
+                       options = find_config_tree_str(cmd,
+                                                 "report/lvs_cols",
+                                                 DEFAULT_LVS_COLS);
+               else
+                       options = find_config_tree_str(cmd,
+                                                 "report/lvs_cols_verbose",
+                                                 DEFAULT_LVS_COLS_VERB);
+               break;
+       case VGS:
+               keys = find_config_tree_str(cmd, "report/vgs_sort",
+                                      DEFAULT_VGS_SORT);
+               if (!arg_count(cmd, verbose_ARG))
+                       options = find_config_tree_str(cmd,
+                                                 "report/vgs_cols",
+                                                 DEFAULT_VGS_COLS);
+               else
+                       options = find_config_tree_str(cmd,
+                                                 "report/vgs_cols_verbose",
+                                                 DEFAULT_VGS_COLS_VERB);
+               break;
+       case LABEL:
+       case PVS:
+               keys = find_config_tree_str(cmd, "report/pvs_sort",
+                                      DEFAULT_PVS_SORT);
+               if (!arg_count(cmd, verbose_ARG))
+                       options = find_config_tree_str(cmd,
+                                                 "report/pvs_cols",
+                                                 DEFAULT_PVS_COLS);
+               else
+                       options = find_config_tree_str(cmd,
+                                                 "report/pvs_cols_verbose",
+                                                 DEFAULT_PVS_COLS_VERB);
+               break;
+       case SEGS:
+               keys = find_config_tree_str(cmd, "report/segs_sort",
+                                      DEFAULT_SEGS_SORT);
+               if (!arg_count(cmd, verbose_ARG))
+                       options = find_config_tree_str(cmd,
+                                                 "report/segs_cols",
+                                                 DEFAULT_SEGS_COLS);
+               else
+                       options = find_config_tree_str(cmd,
+                                                 "report/segs_cols_verbose",
+                                                 DEFAULT_SEGS_COLS_VERB);
+               break;
+       case PVSEGS:
+               keys = find_config_tree_str(cmd, "report/pvsegs_sort",
+                                      DEFAULT_PVSEGS_SORT);
+               if (!arg_count(cmd, verbose_ARG))
+                       options = find_config_tree_str(cmd,
+                                                 "report/pvsegs_cols",
+                                                 DEFAULT_PVSEGS_COLS);
+               else
+                       options = find_config_tree_str(cmd,
+                                                 "report/pvsegs_cols_verbose",
+                                                 DEFAULT_PVSEGS_COLS_VERB);
+               break;
+       }
+
+       /* If -o supplied use it, else use default for report_type */
+       if (arg_count(cmd, options_ARG)) {
+               opts = arg_str_value(cmd, options_ARG, "");
+               if (!opts || !*opts) {
+                       log_error("Invalid options string: %s", opts);
+                       return EINVALID_CMD_LINE;
+               }
+               if (*opts == '+') {
+                       if (!(str = dm_pool_alloc(cmd->mem,
+                                        strlen(options) + strlen(opts) + 1))) {
+                               log_error("options string allocation failed");
+                               return ECMD_FAILED;
+                       }
+                       strcpy(str, options);
+                       strcat(str, ",");
+                       strcat(str, opts + 1);
+                       options = str;
+               } else
+                       options = opts;
+       }
+
+       /* -O overrides default sort settings */
+       keys = arg_str_value(cmd, sort_ARG, keys);
+
+       separator = arg_str_value(cmd, separator_ARG, separator);
+       if (arg_count(cmd, separator_ARG))
+               aligned = 0;
+       if (arg_count(cmd, aligned_ARG))
+               aligned = 1;
+       if (arg_count(cmd, unbuffered_ARG) && !arg_count(cmd, sort_ARG))
+               buffered = 0;
+       if (arg_count(cmd, noheadings_ARG))
+               headings = 0;
+       if (arg_count(cmd, nameprefixes_ARG)) {
+               aligned = 0;
+               field_prefixes = 1;
+       }
+       if (arg_count(cmd, unquoted_ARG))
+               quoted = 0;
+       if (arg_count(cmd, rows_ARG))
+               columns_as_rows = 1;
+
+       if (!(report_handle = report_init(cmd, options, keys, &report_type,
+                                         separator, aligned, buffered,
+                                         headings, field_prefixes, quoted,
+                                         columns_as_rows))) {
+               if (!strcasecmp(options, "help") || !strcmp(options, "?"))
+                       return r;
+               stack;
+               return ECMD_FAILED;
+       }
+
+       /* Ensure options selected are compatible */
+       if (report_type & SEGS)
+               report_type |= LVS;
+       if (report_type & PVSEGS)
+               report_type |= PVS;
+       if ((report_type & LVS) && (report_type & (PVS | LABEL)) && !args_are_pvs) {
+               log_error("Can't report LV and PV fields at the same time");
+               dm_report_free(report_handle);
+               return ECMD_FAILED;
+       }
+
+       /* Change report type if fields specified makes this necessary */
+       if ((report_type & PVSEGS) ||
+           ((report_type & (PVS | LABEL)) && (report_type & LVS)))
+               report_type = PVSEGS;
+       else if ((report_type & LABEL) && (report_type & VGS))
+               report_type = PVS;
+       else if (report_type & PVS)
+               report_type = PVS;
+       else if (report_type & SEGS)
+               report_type = SEGS;
+       else if (report_type & LVS)
+               report_type = LVS;
+
+       switch (report_type) {
+       case LVS:
+               r = process_each_lv(cmd, argc, argv, 0, report_handle,
+                                   &_lvs_single);
+               break;
+       case VGS:
+               r = process_each_vg(cmd, argc, argv, 0,
+                                   report_handle, &_vgs_single);
+               break;
+       case LABEL:
+               r = process_each_pv(cmd, argc, argv, NULL, READ_WITHOUT_LOCK,
+                                   1, report_handle, &_label_single);
+               break;
+       case PVS:
+               if (args_are_pvs)
+                       r = process_each_pv(cmd, argc, argv, NULL, 0,
+                                           0, report_handle, &_pvs_single);
+               else
+                       r = process_each_vg(cmd, argc, argv, 0,
+                                           report_handle, &_pvs_in_vg);
+               break;
+       case SEGS:
+               r = process_each_lv(cmd, argc, argv, 0, report_handle,
+                                   &_lvsegs_single);
+               break;
+       case PVSEGS:
+               if (args_are_pvs)
+                       r = process_each_pv(cmd, argc, argv, NULL, 0,
+                                           0, report_handle, &_pvsegs_single);
+               else
+                       r = process_each_vg(cmd, argc, argv, 0,
+                                           report_handle, &_pvsegs_in_vg);
+               break;
+       }
+
+       dm_report_output(report_handle);
+
+       dm_report_free(report_handle);
+       return r;
+}
+
+int lvs(struct cmd_context *cmd, int argc, char **argv)
+{
+       report_type_t type;
+
+       if (arg_count(cmd, segments_ARG))
+               type = SEGS;
+       else
+               type = LVS;
+
+       return _report(cmd, argc, argv, type);
+}
+
+int vgs(struct cmd_context *cmd, int argc, char **argv)
+{
+       return _report(cmd, argc, argv, VGS);
+}
+
+int pvs(struct cmd_context *cmd, int argc, char **argv)
+{
+       report_type_t type;
+
+       if (arg_count(cmd, segments_ARG))
+               type = PVSEGS;
+       else
+               type = LABEL;
+
+       return _report(cmd, argc, argv, type);
+}
diff --git a/tools/segtypes.c b/tools/segtypes.c
new file mode 100644 (file)
index 0000000..a5f55a8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int segtypes(struct cmd_context *cmd, int argc __attribute__((unused)),
+            char **argv __attribute__((unused)))
+{
+       display_segtypes(cmd);
+
+       return ECMD_PROCESSED;
+}
diff --git a/tools/stub.h b/tools/stub.h
new file mode 100644 (file)
index 0000000..478de5d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#define unimplemented \
+       log_error("Command not implemented yet."); return ECMD_FAILED
+
+/*int e2fsadm(struct cmd_context *cmd, int argc, char **argv) unimplemented*/
+int lvmsadc(struct cmd_context *cmd __attribute__((unused)),
+           int argc __attribute__((unused)),
+           char **argv __attribute__((unused)))
+{
+       unimplemented;
+}
+
+int lvmsar(struct cmd_context *cmd __attribute__((unused)),
+          int argc __attribute__((unused)),
+          char **argv __attribute__((unused)))
+{
+       unimplemented;
+}
+
+int pvdata(struct cmd_context *cmd __attribute__((unused)),
+          int argc __attribute__((unused)),
+          char **argv __attribute__((unused)))
+{
+       log_error("There's no 'pvdata' command in LVM2.");
+       log_error("Use lvs, pvs, vgs instead; or use vgcfgbackup and read the text file backup.");
+       log_error("Metadata in LVM1 format can still be displayed using LVM1's pvdata command.");
+       return ECMD_FAILED;
+}
+
diff --git a/tools/toollib.c b/tools/toollib.c
new file mode 100644 (file)
index 0000000..f76aacd
--- /dev/null
@@ -0,0 +1,1546 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "lv_alloc.h"
+#include "xlate.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+const char *command_name(struct cmd_context *cmd)
+{
+       return cmd->command->name;
+}
+
+/*
+ * Strip dev_dir if present
+ */
+char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
+                  unsigned *dev_dir_found)
+{
+       const char *dmdir = dm_dir();
+       size_t dmdir_len = strlen(dmdir), vglv_sz;
+       char *vgname, *lvname, *layer, *vglv;
+
+       /* FIXME Do this properly */
+       if (*vg_name == '/') {
+               while (*vg_name == '/')
+                       vg_name++;
+               vg_name--;
+       }
+
+       /* Reformat string if /dev/mapper found */
+       if (!strncmp(vg_name, dmdir, dmdir_len) && vg_name[dmdir_len] == '/') {
+               if (dev_dir_found)
+                       *dev_dir_found = 1;
+               vg_name += dmdir_len;
+               while (*vg_name == '/')
+                       vg_name++;
+
+               if (!dm_split_lvm_name(cmd->mem, vg_name, &vgname, &lvname, &layer) ||
+                   *layer) {
+                       log_error("skip_dev_dir: Couldn't split up device name %s",
+                                 vg_name);
+                       return (char *) vg_name;
+               }
+               vglv_sz = strlen(vgname) + strlen(lvname) + 2;
+               if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
+                   dm_snprintf(vglv, vglv_sz, "%s%s%s", vgname,
+                                *lvname ? "/" : "",
+                                lvname) < 0) {
+                       log_error("vg/lv string alloc failed");
+                       return (char *) vg_name;
+               }
+               return vglv;
+       }
+
+       if (!strncmp(vg_name, cmd->dev_dir, strlen(cmd->dev_dir))) {
+               if (dev_dir_found)
+                       *dev_dir_found = 1;
+               vg_name += strlen(cmd->dev_dir);
+               while (*vg_name == '/')
+                       vg_name++;
+       } else if (dev_dir_found)
+               *dev_dir_found = 0;
+
+       return (char *) vg_name;
+}
+
+/*
+ * Metadata iteration functions
+ */
+int process_each_lv_in_vg(struct cmd_context *cmd,
+                         struct volume_group *vg,
+                         const struct dm_list *arg_lvnames,
+                         const struct dm_list *tags,
+                         struct dm_list *failed_lvnames,
+                         void *handle,
+                         process_single_lv_fn_t process_single_lv)
+{
+       int ret_max = ECMD_PROCESSED;
+       int ret = 0;
+       unsigned process_all = 0;
+       unsigned process_lv = 0;
+       unsigned tags_supplied = 0;
+       unsigned lvargs_supplied = 0;
+       unsigned lvargs_matched = 0;
+       char *lv_name;
+       struct lv_list *lvl;
+
+       if (!vg_check_status(vg, EXPORTED_VG))
+               return ECMD_FAILED;
+
+       if (tags && !dm_list_empty(tags))
+               tags_supplied = 1;
+
+       if (arg_lvnames && !dm_list_empty(arg_lvnames))
+               lvargs_supplied = 1;
+
+       /* Process all LVs in this VG if no restrictions given */
+       if (!tags_supplied && !lvargs_supplied)
+               process_all = 1;
+
+       /* Or if VG tags match */
+       if (!process_lv && tags_supplied &&
+           str_list_match_list(tags, &vg->tags, NULL)) {
+               process_all = 1;
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               if (lvl->lv->status & SNAPSHOT)
+                       continue;
+
+               if (lv_is_virtual_origin(lvl->lv) && !arg_count(cmd, all_ARG))
+                       continue;
+
+               /*
+                * Only let hidden LVs through it --all was used or the LVs 
+                * were specifically named on the command line.
+                */
+               if (!lvargs_supplied && !lv_is_visible(lvl->lv) && !arg_count(cmd, all_ARG))
+                       continue;
+
+               /* Should we process this LV? */
+               if (process_all)
+                       process_lv = 1;
+               else
+                       process_lv = 0;
+
+               /* LV tag match? */
+               if (!process_lv && tags_supplied &&
+                   str_list_match_list(tags, &lvl->lv->tags, NULL)) {
+                       process_lv = 1;
+               }
+
+               /* LV name match? */
+               if (lvargs_supplied &&
+                   str_list_match_item(arg_lvnames, lvl->lv->name)) {
+                       process_lv = 1;
+                       lvargs_matched++;
+               }
+
+               if (!process_lv)
+                       continue;
+
+               lvl->lv->vg->cmd_missing_vgs = 0;
+               ret = process_single_lv(cmd, lvl->lv, handle);
+               if (ret != ECMD_PROCESSED && failed_lvnames) {
+                       lv_name = dm_pool_strdup(cmd->mem, lvl->lv->name);
+                       if (!lv_name ||
+                           !str_list_add(cmd->mem, failed_lvnames, lv_name)) {
+                               log_error("Allocation failed for str_list.");
+                               return ECMD_FAILED;
+                       }
+                       if (lvl->lv->vg->cmd_missing_vgs)
+                               ret = ECMD_PROCESSED;
+               }
+               if (ret > ret_max)
+                       ret_max = ret;
+               if (sigint_caught())
+                       return ret_max;
+       }
+
+       if (lvargs_supplied && lvargs_matched != dm_list_size(arg_lvnames)) {
+               log_error("One or more specified logical volume(s) not found.");
+               if (ret_max < ECMD_FAILED)
+                       ret_max = ECMD_FAILED;
+       }
+
+       return ret_max;
+}
+
+int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
+                   uint32_t flags, void *handle,
+                   process_single_lv_fn_t process_single_lv)
+{
+       int opt = 0;
+       int ret_max = ECMD_PROCESSED;
+       int ret = 0;
+
+       struct dm_list *tags_arg;
+       struct dm_list *vgnames;        /* VGs to process */
+       struct str_list *sll, *strl;
+       struct cmd_vg *cvl_vg;
+       struct dm_list cmd_vgs;
+       struct dm_list failed_lvnames;
+       struct dm_list tags, lvnames;
+       struct dm_list arg_lvnames;     /* Cmdline vgname or vgname/lvname */
+       struct dm_list arg_vgnames;
+       char *vglv;
+       size_t vglv_sz;
+
+       const char *vgname;
+
+       dm_list_init(&tags);
+       dm_list_init(&arg_lvnames);
+       dm_list_init(&failed_lvnames);
+
+       if (argc) {
+               log_verbose("Using logical volume(s) on command line");
+               dm_list_init(&arg_vgnames);
+
+               for (; opt < argc; opt++) {
+                       const char *lv_name = argv[opt];
+                       const char *tmp_lv_name;
+                       char *vgname_def;
+                       unsigned dev_dir_found = 0;
+
+                       /* Do we have a tag or vgname or lvname? */
+                       vgname = lv_name;
+
+                       if (*vgname == '@') {
+                               if (!validate_tag(vgname + 1)) {
+                                       log_error("Skipping invalid tag %s",
+                                                 vgname);
+                                       continue;
+                               }
+                               if (!str_list_add(cmd->mem, &tags,
+                                                 dm_pool_strdup(cmd->mem,
+                                                             vgname + 1))) {
+                                       log_error("strlist allocation failed");
+                                       return ECMD_FAILED;
+                               }
+                               continue;
+                       }
+
+                       /* FIXME Jumbled parsing */
+                       vgname = skip_dev_dir(cmd, vgname, &dev_dir_found);
+
+                       if (*vgname == '/') {
+                               log_error("\"%s\": Invalid path for Logical "
+                                         "Volume", argv[opt]);
+                               if (ret_max < ECMD_FAILED)
+                                       ret_max = ECMD_FAILED;
+                               continue;
+                       }
+                       lv_name = vgname;
+                       if ((tmp_lv_name = strchr(vgname, '/'))) {
+                               /* Must be an LV */
+                               lv_name = tmp_lv_name;
+                               while (*lv_name == '/')
+                                       lv_name++;
+                               if (!(vgname = extract_vgname(cmd, vgname))) {
+                                       if (ret_max < ECMD_FAILED)
+                                               ret_max = ECMD_FAILED;
+                                       continue;
+                               }
+                       } else if (!dev_dir_found &&
+                                  (vgname_def = default_vgname(cmd))) {
+                               vgname = vgname_def;
+                       } else
+                               lv_name = NULL;
+
+                       if (!str_list_add(cmd->mem, &arg_vgnames,
+                                         dm_pool_strdup(cmd->mem, vgname))) {
+                               log_error("strlist allocation failed");
+                               return ECMD_FAILED;
+                       }
+
+                       if (!lv_name) {
+                               if (!str_list_add(cmd->mem, &arg_lvnames,
+                                                 dm_pool_strdup(cmd->mem,
+                                                             vgname))) {
+                                       log_error("strlist allocation failed");
+                                       return ECMD_FAILED;
+                               }
+                       } else {
+                               vglv_sz = strlen(vgname) + strlen(lv_name) + 2;
+                               if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
+                                   dm_snprintf(vglv, vglv_sz, "%s/%s", vgname,
+                                                lv_name) < 0) {
+                                       log_error("vg/lv string alloc failed");
+                                       return ECMD_FAILED;
+                               }
+                               if (!str_list_add(cmd->mem, &arg_lvnames, vglv)) {
+                                       log_error("strlist allocation failed");
+                                       return ECMD_FAILED;
+                               }
+                       }
+               }
+               vgnames = &arg_vgnames;
+       }
+
+       if (!argc || !dm_list_empty(&tags)) {
+               log_verbose("Finding all logical volumes");
+               if (!(vgnames = get_vgnames(cmd, 0)) || dm_list_empty(vgnames)) {
+                       log_error("No volume groups found");
+                       return ret_max;
+               }
+       }
+
+       dm_list_iterate_items(strl, vgnames) {
+               vgname = strl->str;
+               dm_list_init(&cmd_vgs);
+               if (!(cvl_vg = cmd_vg_add(cmd->mem, &cmd_vgs,
+                                         vgname, NULL, flags))) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               if (!cmd_vg_read(cmd, &cmd_vgs)) {
+                       free_cmd_vgs(&cmd_vgs);
+                       if (ret_max < ECMD_FAILED) {
+                               log_error("Skipping volume group %s", vgname);
+                               ret_max = ECMD_FAILED;
+                       } else
+                               stack;
+                       continue;
+               }
+
+               tags_arg = &tags;
+               dm_list_init(&lvnames); /* LVs to be processed in this VG */
+               dm_list_iterate_items(sll, &arg_lvnames) {
+                       const char *vg_name = sll->str;
+                       const char *lv_name = strchr(vg_name, '/');
+
+                       if ((!lv_name && !strcmp(vg_name, vgname))) {
+                               /* Process all LVs in this VG */
+                               tags_arg = NULL;
+                               dm_list_init(&lvnames);
+                               break;
+                       } else if (!strncmp(vg_name, vgname, strlen(vgname)) && lv_name &&
+                                  strlen(vgname) == (size_t) (lv_name - vg_name)) {
+                               if (!str_list_add(cmd->mem, &lvnames,
+                                                 dm_pool_strdup(cmd->mem,
+                                                                lv_name + 1))) {
+                                       log_error("strlist allocation failed");
+                                       free_cmd_vgs(&cmd_vgs);
+                                       return ECMD_FAILED;
+                               }
+                       }
+               }
+
+               while (!sigint_caught()) {
+                       ret = process_each_lv_in_vg(cmd, cvl_vg->vg, &lvnames,
+                                                   tags_arg, &failed_lvnames,
+                                                   handle, process_single_lv);
+                       if (ret != ECMD_PROCESSED ||
+                           dm_list_empty(&failed_lvnames))
+                               break;
+
+                       /* Try again with failed LVs in this VG */
+                       dm_list_init(&lvnames);
+                       dm_list_splice(&lvnames, &failed_lvnames);
+
+                       free_cmd_vgs(&cmd_vgs);
+                       if (!cmd_vg_read(cmd, &cmd_vgs)) {
+                               ret = ECMD_FAILED; /* break */
+                               break;
+                       }
+               }
+               if (ret > ret_max)
+                       ret_max = ret;
+
+               free_cmd_vgs(&cmd_vgs);
+               /* FIXME: logic for breaking command is not consistent */
+               if (sigint_caught())
+                       return ECMD_FAILED;
+       }
+
+       return ret_max;
+}
+
+int process_each_segment_in_pv(struct cmd_context *cmd,
+                              struct volume_group *vg,
+                              struct physical_volume *pv,
+                              void *handle,
+                              process_single_pvseg_fn_t process_single_pvseg)
+{
+       struct pv_segment *pvseg;
+       struct pv_list *pvl;
+       const char *vg_name = NULL;
+       int ret_max = ECMD_PROCESSED;
+       int ret;
+       struct volume_group *old_vg = vg;
+       struct pv_segment _free_pv_segment = { .pv = pv };
+
+       if (is_pv(pv) && !vg && !is_orphan(pv)) {
+               vg_name = pv_vg_name(pv);
+
+               vg = vg_read(cmd, vg_name, NULL, 0);
+               if (vg_read_error(vg)) {
+                       free_vg(vg);
+                       log_error("Skipping volume group %s", vg_name);
+                       return ECMD_FAILED;
+               }
+
+               /*
+                * Replace possibly incomplete PV structure with new one
+                * allocated in vg_read_internal() path.
+                */
+               if (!(pvl = find_pv_in_vg(vg, pv_dev_name(pv)))) {
+                        log_error("Unable to find %s in volume group %s",
+                                  pv_dev_name(pv), vg_name);
+                        unlock_and_free_vg(cmd, vg, vg_name);
+                        return ECMD_FAILED;
+               }
+
+               pv = pvl->pv;
+       }
+
+       if (dm_list_empty(&pv->segments)) {
+               ret = process_single_pvseg(cmd, NULL, &_free_pv_segment, handle);
+               if (ret > ret_max)
+                       ret_max = ret;
+       } else
+               dm_list_iterate_items(pvseg, &pv->segments) {
+                       ret = process_single_pvseg(cmd, vg, pvseg, handle);
+                       if (ret > ret_max)
+                               ret_max = ret;
+                       if (sigint_caught())
+                               break;
+               }
+
+       if (vg_name)
+               unlock_vg(cmd, vg_name);
+       if (!old_vg)
+               free_vg(vg);
+
+       return ret_max;
+}
+
+int process_each_segment_in_lv(struct cmd_context *cmd,
+                              struct logical_volume *lv,
+                              void *handle,
+                              process_single_seg_fn_t process_single_seg)
+{
+       struct lv_segment *seg;
+       int ret_max = ECMD_PROCESSED;
+       int ret;
+
+       dm_list_iterate_items(seg, &lv->segments) {
+               ret = process_single_seg(cmd, seg, handle);
+               if (ret > ret_max)
+                       ret_max = ret;
+               /* FIXME: logic for breaking command is not consistent */
+               if (sigint_caught())
+                       return ECMD_FAILED;
+       }
+
+       return ret_max;
+}
+
+static int _process_one_vg(struct cmd_context *cmd, const char *vg_name,
+                          const char *vgid,
+                          struct dm_list *tags, struct dm_list *arg_vgnames,
+                          uint32_t flags, void *handle, int ret_max,
+                          process_single_vg_fn_t process_single_vg)
+{
+       struct dm_list cmd_vgs;
+       struct cmd_vg *cvl_vg;
+       int ret = 0;
+
+       log_verbose("Finding volume group \"%s\"", vg_name);
+
+       dm_list_init(&cmd_vgs);
+       if (!(cvl_vg = cmd_vg_add(cmd->mem, &cmd_vgs, vg_name, vgid, flags)))
+               return_0;
+
+       for (;;) {
+               /* FIXME: consistent handling of command break */
+               if (sigint_caught()) {
+                        ret = ECMD_FAILED;
+                       break;
+               }
+               if (!cmd_vg_read(cmd, &cmd_vgs))
+                       /* Allow FAILED_INCONSISTENT through only for vgcfgrestore */
+                       if (vg_read_error(cvl_vg->vg) &&
+                           (!((flags & READ_ALLOW_INCONSISTENT) &&
+                              (vg_read_error(cvl_vg->vg) == FAILED_INCONSISTENT)))) {
+                               ret = ECMD_FAILED;
+                               break;
+                       }
+
+               if (!dm_list_empty(tags) &&
+                   /* Only process if a tag matches or it's on arg_vgnames */
+                   !str_list_match_item(arg_vgnames, vg_name) &&
+                   !str_list_match_list(tags, &cvl_vg->vg->tags, NULL))
+                       break;
+
+               ret = process_single_vg(cmd, vg_name, cvl_vg->vg, handle);
+
+               if (vg_read_error(cvl_vg->vg)) /* FAILED_INCONSISTENT */
+                       break;
+
+               if (!cvl_vg->vg->cmd_missing_vgs)
+                       break;
+
+               free_cmd_vgs(&cmd_vgs);
+       }
+
+       free_cmd_vgs(&cmd_vgs);
+
+       return (ret > ret_max) ? ret : ret_max;
+}
+
+int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
+                   uint32_t flags, void *handle,
+                   process_single_vg_fn_t process_single_vg)
+{
+       int opt = 0;
+       int ret_max = ECMD_PROCESSED;
+
+       struct str_list *sl;
+       struct dm_list *vgnames, *vgids;
+       struct dm_list arg_vgnames, tags;
+
+       const char *vg_name, *vgid;
+
+       dm_list_init(&tags);
+       dm_list_init(&arg_vgnames);
+
+       if (argc) {
+               log_verbose("Using volume group(s) on command line");
+
+               for (; opt < argc; opt++) {
+                       vg_name = argv[opt];
+                       if (*vg_name == '@') {
+                               if (!validate_tag(vg_name + 1)) {
+                                       log_error("Skipping invalid tag %s",
+                                                 vg_name);
+                                       if (ret_max < EINVALID_CMD_LINE)
+                                               ret_max = EINVALID_CMD_LINE;
+                                       continue;
+                               }
+                               if (!str_list_add(cmd->mem, &tags,
+                                                 dm_pool_strdup(cmd->mem,
+                                                             vg_name + 1))) {
+                                       log_error("strlist allocation failed");
+                                       return ECMD_FAILED;
+                               }
+                               continue;
+                       }
+
+                       vg_name = skip_dev_dir(cmd, vg_name, NULL);
+                       if (strchr(vg_name, '/')) {
+                               log_error("Invalid volume group name: %s",
+                                         vg_name);
+                               if (ret_max < EINVALID_CMD_LINE)
+                                       ret_max = EINVALID_CMD_LINE;
+                               continue;
+                       }
+                       if (!str_list_add(cmd->mem, &arg_vgnames,
+                                         dm_pool_strdup(cmd->mem, vg_name))) {
+                               log_error("strlist allocation failed");
+                               return ECMD_FAILED;
+                       }
+               }
+
+               vgnames = &arg_vgnames;
+       }
+
+       if (!argc || !dm_list_empty(&tags)) {
+               log_verbose("Finding all volume groups");
+               if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) {
+                       log_error("No volume groups found");
+                       return ret_max;
+               }
+               dm_list_iterate_items(sl, vgids) {
+                       vgid = sl->str;
+                       if (!(vgid) || !(vg_name = vgname_from_vgid(cmd->mem, vgid)))
+                               continue;
+                       ret_max = _process_one_vg(cmd, vg_name, vgid, &tags,
+                                                 &arg_vgnames,
+                                                 flags, handle,
+                                                 ret_max, process_single_vg);
+                       if (sigint_caught())
+                               return ret_max;
+               }
+       } else {
+               dm_list_iterate_items(sl, vgnames) {
+                       vg_name = sl->str;
+                       if (is_orphan_vg(vg_name))
+                               continue;       /* FIXME Unnecessary? */
+                       ret_max = _process_one_vg(cmd, vg_name, NULL, &tags,
+                                                 &arg_vgnames,
+                                                 flags, handle,
+                                                 ret_max, process_single_vg);
+                       if (sigint_caught())
+                               return ret_max;
+               }
+       }
+
+       return ret_max;
+}
+
+int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
+                         const struct dm_list *tags, void *handle,
+                         process_single_pv_fn_t process_single_pv)
+{
+       int ret_max = ECMD_PROCESSED;
+       int ret = 0;
+       struct pv_list *pvl;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (tags && !dm_list_empty(tags) &&
+                   !str_list_match_list(tags, &pvl->pv->tags, NULL)) {
+                       continue;
+               }
+               if ((ret = process_single_pv(cmd, vg, pvl->pv, handle)) > ret_max)
+                       ret_max = ret;
+               if (sigint_caught())
+                       return ret_max;
+       }
+
+       return ret_max;
+}
+
+static int _process_all_devs(struct cmd_context *cmd, void *handle,
+                            process_single_pv_fn_t process_single_pv)
+{
+       struct physical_volume *pv;
+       struct physical_volume pv_dummy;
+       struct dev_iter *iter;
+       struct device *dev;
+
+       int ret_max = ECMD_PROCESSED;
+       int ret = 0;
+
+       if (!scan_vgs_for_pvs(cmd, 1)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!(iter = dev_iter_create(cmd->filter, 1))) {
+               log_error("dev_iter creation failed");
+               return ECMD_FAILED;
+       }
+
+       while ((dev = dev_iter_get(iter))) {
+               if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL, 0, 0))) {
+                       memset(&pv_dummy, 0, sizeof(pv_dummy));
+                       dm_list_init(&pv_dummy.tags);
+                       dm_list_init(&pv_dummy.segments);
+                       pv_dummy.dev = dev;
+                       pv_dummy.fmt = NULL;
+                       pv = &pv_dummy;
+               }
+               ret = process_single_pv(cmd, NULL, pv, handle);
+               if (ret > ret_max)
+                       ret_max = ret;
+               if (sigint_caught())
+                       break;
+       }
+
+       dev_iter_destroy(iter);
+
+       return ret_max;
+}
+
+/*
+ * If the lock_type is LCK_VG_READ (used only in reporting commands),
+ * we lock VG_GLOBAL to enable use of metadata cache.
+ * This can pause alongide pvscan or vgscan process for a while.
+ */
+int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
+                   struct volume_group *vg, uint32_t flags,
+                   int scan_label_only, void *handle,
+                   process_single_pv_fn_t process_single_pv)
+{
+       int opt = 0;
+       int ret_max = ECMD_PROCESSED;
+       int ret = 0;
+       int lock_global = !(flags & READ_WITHOUT_LOCK) && !(flags & READ_FOR_UPDATE);
+
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+       struct dm_list *pvslist, *vgnames;
+       struct dm_list tags;
+       struct str_list *sll;
+       char *at_sign, *tagname;
+       int scanned = 0;
+       struct dm_list mdas;
+
+       dm_list_init(&tags);
+
+       if (lock_global && !lock_vol(cmd, VG_GLOBAL, LCK_VG_READ)) {
+               log_error("Unable to obtain global lock.");
+               return ECMD_FAILED;
+       }
+
+       if (argc) {
+               log_verbose("Using physical volume(s) on command line");
+               for (; opt < argc; opt++) {
+                       unescape_colons_and_at_signs(argv[opt], NULL, &at_sign);
+                       if (at_sign && (at_sign == argv[opt])) {
+                               tagname = at_sign + 1;
+
+                               if (!validate_tag(tagname)) {
+                                       log_error("Skipping invalid tag %s",
+                                                 tagname);
+                                       if (ret_max < EINVALID_CMD_LINE)
+                                               ret_max = EINVALID_CMD_LINE;
+                                       continue;
+                               }
+                               if (!str_list_add(cmd->mem, &tags,
+                                                 dm_pool_strdup(cmd->mem,
+                                                             tagname))) {
+                                       log_error("strlist allocation failed");
+                                       goto bad;
+                               }
+                               continue;
+                       }
+                       if (vg) {
+                               if (!(pvl = find_pv_in_vg(vg, argv[opt]))) {
+                                       log_error("Physical Volume \"%s\" not "
+                                                 "found in Volume Group "
+                                                 "\"%s\"", argv[opt],
+                                                 vg->name);
+                                       ret_max = ECMD_FAILED;
+                                       continue;
+                               }
+                               pv = pvl->pv;
+                       } else {
+
+                               dm_list_init(&mdas);
+                               if (!(pv = pv_read(cmd, argv[opt], &mdas,
+                                                  NULL, 1, scan_label_only))) {
+                                       log_error("Failed to read physical "
+                                                 "volume \"%s\"", argv[opt]);
+                                       ret_max = ECMD_FAILED;
+                                       continue;
+                               }
+
+                               /*
+                                * If a PV has no MDAs it may appear to be an
+                                * orphan until the metadata is read off
+                                * another PV in the same VG.  Detecting this
+                                * means checking every VG by scanning every
+                                * PV on the system.
+                                */
+                               if (!scanned && is_orphan(pv) &&
+                                   !dm_list_size(&mdas)) {
+                                       if (!scan_label_only &&
+                                           !scan_vgs_for_pvs(cmd, 1)) {
+                                               stack;
+                                               ret_max = ECMD_FAILED;
+                                               continue;
+                                       }
+                                       scanned = 1;
+                                       if (!(pv = pv_read(cmd, argv[opt],
+                                                          NULL, NULL, 1,
+                                                          scan_label_only))) {
+                                               log_error("Failed to read "
+                                                         "physical volume "
+                                                         "\"%s\"", argv[opt]);
+                                               ret_max = ECMD_FAILED;
+                                               continue;
+                                       }
+                               }
+                       }
+
+                       ret = process_single_pv(cmd, vg, pv, handle);
+                       if (ret > ret_max)
+                               ret_max = ret;
+                       if (sigint_caught())
+                               goto out;
+               }
+               if (!dm_list_empty(&tags) && (vgnames = get_vgnames(cmd, 1)) &&
+                          !dm_list_empty(vgnames)) {
+                       dm_list_iterate_items(sll, vgnames) {
+                               vg = vg_read(cmd, sll->str, NULL, flags);
+                               if (vg_read_error(vg)) {
+                                       ret_max = ECMD_FAILED;
+                                       free_vg(vg);
+                                       stack;
+                                       continue;
+                               }
+
+                               ret = process_each_pv_in_vg(cmd, vg, &tags,
+                                                           handle,
+                                                           process_single_pv);
+
+                               unlock_and_free_vg(cmd, vg, sll->str);
+
+                               if (ret > ret_max)
+                                       ret_max = ret;
+                               if (sigint_caught())
+                                       goto out;
+                       }
+               }
+       } else {
+               if (vg) {
+                       log_verbose("Using all physical volume(s) in "
+                                   "volume group");
+                       ret = process_each_pv_in_vg(cmd, vg, NULL, handle,
+                                                   process_single_pv);
+                       if (ret > ret_max)
+                               ret_max = ret;
+                       if (sigint_caught())
+                               goto out;
+               } else if (arg_count(cmd, all_ARG)) {
+                       ret = _process_all_devs(cmd, handle, process_single_pv);
+                       if (ret > ret_max)
+                               ret_max = ret;
+                       if (sigint_caught())
+                               goto out;
+               } else {
+                       log_verbose("Scanning for physical volume names");
+
+                       if (!(pvslist = get_pvs(cmd)))
+                               goto bad;
+
+                       dm_list_iterate_items(pvl, pvslist) {
+                               ret = process_single_pv(cmd, NULL, pvl->pv,
+                                                    handle);
+                               if (ret > ret_max)
+                                       ret_max = ret;
+                               if (sigint_caught())
+                                       goto out;
+                       }
+               }
+       }
+out:
+       if (lock_global)
+               unlock_vg(cmd, VG_GLOBAL);
+       return ret_max;
+bad:
+       if (lock_global)
+               unlock_vg(cmd, VG_GLOBAL);
+
+       return ECMD_FAILED;
+}
+
+/*
+ * Determine volume group name from a logical volume name
+ */
+const char *extract_vgname(struct cmd_context *cmd, const char *lv_name)
+{
+       const char *vg_name = lv_name;
+       char *st;
+       char *dev_dir = cmd->dev_dir;
+
+       /* Path supplied? */
+       if (vg_name && strchr(vg_name, '/')) {
+               /* Strip dev_dir (optional) */
+               if (*vg_name == '/') {
+                       while (*vg_name == '/')
+                               vg_name++;
+                       vg_name--;
+               }
+               if (!strncmp(vg_name, dev_dir, strlen(dev_dir))) {
+                       vg_name += strlen(dev_dir);
+                       while (*vg_name == '/')
+                               vg_name++;
+               }
+               if (*vg_name == '/') {
+                       log_error("\"%s\": Invalid path for Logical "
+                                 "Volume", lv_name);
+                       return 0;
+               }
+
+               /* Require exactly one set of consecutive slashes */
+               if ((st = strchr(vg_name, '/')))
+                       while (*st == '/')
+                               st++;
+
+               if (!st || strchr(st, '/')) {
+                       log_error("\"%s\": Invalid path for Logical Volume",
+                                 lv_name);
+                       return 0;
+               }
+
+               vg_name = dm_pool_strdup(cmd->mem, vg_name);
+               if (!vg_name) {
+                       log_error("Allocation of vg_name failed");
+                       return 0;
+               }
+
+               *strchr(vg_name, '/') = '\0';
+               return vg_name;
+       }
+
+       if (!(vg_name = default_vgname(cmd))) {
+               if (lv_name)
+                       log_error("Path required for Logical Volume \"%s\"",
+                                 lv_name);
+               return 0;
+       }
+
+       return vg_name;
+}
+
+/*
+ * Extract default volume group name from environment
+ */
+char *default_vgname(struct cmd_context *cmd)
+{
+       char *vg_path;
+
+       /* Take default VG from environment? */
+       vg_path = getenv("LVM_VG_NAME");
+       if (!vg_path)
+               return 0;
+
+       vg_path = skip_dev_dir(cmd, vg_path, NULL);
+
+       if (strchr(vg_path, '/')) {
+               log_error("Environment Volume Group in LVM_VG_NAME invalid: "
+                         "\"%s\"", vg_path);
+               return 0;
+       }
+
+       return dm_pool_strdup(cmd->mem, vg_path);
+}
+
+/*
+ * Process physical extent range specifiers
+ */
+static int _add_pe_range(struct dm_pool *mem, const char *pvname,
+                        struct dm_list *pe_ranges, uint32_t start, uint32_t count)
+{
+       struct pe_range *per;
+
+       log_debug("Adding PE range: start PE %" PRIu32 " length %" PRIu32
+                 " on %s", start, count, pvname);
+
+       /* Ensure no overlap with existing areas */
+       dm_list_iterate_items(per, pe_ranges) {
+               if (((start < per->start) && (start + count - 1 >= per->start))
+                   || ((start >= per->start) &&
+                       (per->start + per->count - 1) >= start)) {
+                       log_error("Overlapping PE ranges specified (%" PRIu32
+                                 "-%" PRIu32 ", %" PRIu32 "-%" PRIu32 ")"
+                                 " on %s",
+                                 start, start + count - 1, per->start,
+                                 per->start + per->count - 1, pvname);
+                       return 0;
+               }
+       }
+
+       if (!(per = dm_pool_alloc(mem, sizeof(*per)))) {
+               log_error("Allocation of list failed");
+               return 0;
+       }
+
+       per->start = start;
+       per->count = count;
+       dm_list_add(pe_ranges, &per->list);
+
+       return 1;
+}
+
+static int xstrtouint32(const char *s, char **p, int base, uint32_t *result)
+{
+       unsigned long ul;
+
+       errno = 0;
+       ul = strtoul(s, p, base);
+       if (errno || *p == s || (uint32_t) ul != ul)
+               return -1;
+       *result = ul;
+       return 0;
+}
+
+static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges,
+                     const char *pvname, uint32_t size)
+{
+       char *endptr;
+       uint32_t start, end;
+
+       /* Default to whole PV */
+       if (!c) {
+               if (!_add_pe_range(mem, pvname, pe_ranges, UINT32_C(0), size))
+                       return_0;
+               return 1;
+       }
+
+       while (*c) {
+               if (*c != ':')
+                       goto error;
+
+               c++;
+
+               /* Disallow :: and :\0 */
+               if (*c == ':' || !*c)
+                       goto error;
+
+               /* Default to whole range */
+               start = UINT32_C(0);
+               end = size - 1;
+
+               /* Start extent given? */
+               if (isdigit(*c)) {
+                       if (xstrtouint32(c, &endptr, 10, &start))
+                               goto error;
+                       c = endptr;
+                       /* Just one number given? */
+                       if (!*c || *c == ':')
+                               end = start;
+               }
+               /* Range? */
+               if (*c == '-') {
+                       c++;
+                       if (isdigit(*c)) {
+                               if (xstrtouint32(c, &endptr, 10, &end))
+                                       goto error;
+                               c = endptr;
+                       }
+               }
+               if (*c && *c != ':')
+                       goto error;
+
+               if ((start > end) || (end > size - 1)) {
+                       log_error("PE range error: start extent %" PRIu32 " to "
+                                 "end extent %" PRIu32, start, end);
+                       return 0;
+               }
+
+               if (!_add_pe_range(mem, pvname, pe_ranges, start, end - start + 1))
+                       return_0;
+
+       }
+
+       return 1;
+
+      error:
+       log_error("Physical extent parsing error at %s", c);
+       return 0;
+}
+
+static int _create_pv_entry(struct dm_pool *mem, struct pv_list *pvl,
+                            char *colon, int allocatable_only, struct dm_list *r)
+{
+       const char *pvname;
+       struct pv_list *new_pvl = NULL, *pvl2;
+       struct dm_list *pe_ranges;
+
+       pvname = pv_dev_name(pvl->pv);
+       if (allocatable_only && !(pvl->pv->status & ALLOCATABLE_PV)) {
+               log_error("Physical volume %s not allocatable", pvname);
+               return 1;
+       }
+
+       if (allocatable_only && is_missing_pv(pvl->pv)) {
+               log_error("Physical volume %s is missing", pvname);
+               return 1;
+       }
+
+       if (allocatable_only &&
+           (pvl->pv->pe_count == pvl->pv->pe_alloc_count)) {
+               log_error("No free extents on physical volume \"%s\"", pvname);
+               return 1;
+       }
+
+       dm_list_iterate_items(pvl2, r)
+               if (pvl->pv->dev == pvl2->pv->dev) {
+                       new_pvl = pvl2;
+                       break;
+               }
+
+       if (!new_pvl) {
+               if (!(new_pvl = dm_pool_alloc(mem, sizeof(*new_pvl)))) {
+                       log_error("Unable to allocate physical volume list.");
+                       return 0;
+               }
+
+               memcpy(new_pvl, pvl, sizeof(*new_pvl));
+
+               if (!(pe_ranges = dm_pool_alloc(mem, sizeof(*pe_ranges)))) {
+                       log_error("Allocation of pe_ranges list failed");
+                       return 0;
+               }
+               dm_list_init(pe_ranges);
+               new_pvl->pe_ranges = pe_ranges;
+               dm_list_add(r, &new_pvl->list);
+       }
+
+       /* Determine selected physical extents */
+       if (!_parse_pes(mem, colon, new_pvl->pe_ranges, pv_dev_name(pvl->pv),
+                       pvl->pv->pe_count))
+               return_0;
+
+       return 1;
+}
+
+struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int argc,
+                           char **argv, int allocatable_only)
+{
+       struct dm_list *r;
+       struct pv_list *pvl;
+       struct dm_list tags, arg_pvnames;
+       char *pvname = NULL;
+       char *colon, *at_sign, *tagname;
+       int i;
+
+       /* Build up list of PVs */
+       if (!(r = dm_pool_alloc(mem, sizeof(*r)))) {
+               log_error("Allocation of list failed");
+               return NULL;
+       }
+       dm_list_init(r);
+
+       dm_list_init(&tags);
+       dm_list_init(&arg_pvnames);
+
+       for (i = 0; i < argc; i++) {
+               unescape_colons_and_at_signs(argv[i], &colon, &at_sign);
+
+               if (at_sign && (at_sign == argv[i])) {
+                       tagname = at_sign + 1;
+                       if (!validate_tag(tagname)) {
+                               log_error("Skipping invalid tag %s", tagname);
+                               continue;
+                       }
+                       dm_list_iterate_items(pvl, &vg->pvs) {
+                               if (str_list_match_item(&pvl->pv->tags,
+                                                       tagname)) {
+                                       if (!_create_pv_entry(mem, pvl, NULL,
+                                                             allocatable_only,
+                                                             r))
+                                               return_NULL;
+                               }
+                       }
+                       continue;
+               }
+
+               pvname = argv[i];
+
+               if (colon && !(pvname = dm_pool_strndup(mem, pvname,
+                                       (unsigned) (colon - pvname)))) {
+                       log_error("Failed to clone PV name");
+                       return NULL;
+               }
+
+               if (!(pvl = find_pv_in_vg(vg, pvname))) {
+                       log_error("Physical Volume \"%s\" not found in "
+                                 "Volume Group \"%s\"", pvname, vg->name);
+                       return NULL;
+               }
+               if (!_create_pv_entry(mem, pvl, colon, allocatable_only, r))
+                       return_NULL;
+       }
+
+       if (dm_list_empty(r))
+               log_error("No specified PVs have space available");
+
+       return dm_list_empty(r) ? NULL : r;
+}
+
+struct dm_list *clone_pv_list(struct dm_pool *mem, struct dm_list *pvsl)
+{
+       struct dm_list *r;
+       struct pv_list *pvl, *new_pvl;
+
+       /* Build up list of PVs */
+       if (!(r = dm_pool_alloc(mem, sizeof(*r)))) {
+               log_error("Allocation of list failed");
+               return NULL;
+       }
+       dm_list_init(r);
+
+       dm_list_iterate_items(pvl, pvsl) {
+               if (!(new_pvl = dm_pool_zalloc(mem, sizeof(*new_pvl)))) {
+                       log_error("Unable to allocate physical volume list.");
+                       return NULL;
+               }
+
+               memcpy(new_pvl, pvl, sizeof(*new_pvl));
+               dm_list_add(r, &new_pvl->list);
+       }
+
+       return r;
+}
+
+void vgcreate_params_set_defaults(struct vgcreate_params *vp_def,
+                                 struct volume_group *vg)
+{
+       if (vg) {
+               vp_def->vg_name = NULL;
+               vp_def->extent_size = vg->extent_size;
+               vp_def->max_pv = vg->max_pv;
+               vp_def->max_lv = vg->max_lv;
+               vp_def->alloc = vg->alloc;
+               vp_def->clustered = vg_is_clustered(vg);
+               vp_def->vgmetadatacopies = vg->mda_copies;
+       } else {
+               vp_def->vg_name = NULL;
+               vp_def->extent_size = DEFAULT_EXTENT_SIZE * 2;
+               vp_def->max_pv = DEFAULT_MAX_PV;
+               vp_def->max_lv = DEFAULT_MAX_LV;
+               vp_def->alloc = DEFAULT_ALLOC_POLICY;
+               vp_def->clustered = DEFAULT_CLUSTERED;
+               vp_def->vgmetadatacopies = DEFAULT_VGMETADATACOPIES;
+       }
+}
+
+/*
+ * Set members of struct vgcreate_params from cmdline arguments.
+ * Do preliminary validation with arg_*() interface.
+ * Further, more generic validation is done in validate_vgcreate_params().
+ * This function is to remain in tools directory.
+ */
+int vgcreate_params_set_from_args(struct cmd_context *cmd,
+                                 struct vgcreate_params *vp_new,
+                                 struct vgcreate_params *vp_def)
+{
+       vp_new->vg_name = skip_dev_dir(cmd, vp_def->vg_name, NULL);
+       vp_new->max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG,
+                                       vp_def->max_lv);
+       vp_new->max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG,
+                                       vp_def->max_pv);
+       vp_new->alloc = arg_uint_value(cmd, alloc_ARG, vp_def->alloc);
+
+       /* Units of 512-byte sectors */
+       vp_new->extent_size =
+           arg_uint_value(cmd, physicalextentsize_ARG, vp_def->extent_size);
+
+       if (arg_count(cmd, clustered_ARG))
+               vp_new->clustered =
+                       !strcmp(arg_str_value(cmd, clustered_ARG,
+                                             vp_def->clustered ? "y":"n"), "y");
+       else
+               /* Default depends on current locking type */
+               vp_new->clustered = locking_is_clustered();
+
+       if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) {
+               log_error("Physical extent size may not be negative");
+               return 1;
+       }
+
+       if (arg_sign_value(cmd, maxlogicalvolumes_ARG, 0) == SIGN_MINUS) {
+               log_error("Max Logical Volumes may not be negative");
+               return 1;
+       }
+
+       if (arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) {
+               log_error("Max Physical Volumes may not be negative");
+               return 1;
+       }
+
+       if (arg_count(cmd, metadatacopies_ARG)) {
+               vp_new->vgmetadatacopies = arg_int_value(cmd, metadatacopies_ARG,
+                                                       DEFAULT_VGMETADATACOPIES);
+       } else if (arg_count(cmd, vgmetadatacopies_ARG)) {
+               vp_new->vgmetadatacopies = arg_int_value(cmd, vgmetadatacopies_ARG,
+                                                       DEFAULT_VGMETADATACOPIES);
+       } else {
+               vp_new->vgmetadatacopies = find_config_tree_int(cmd,
+                                                  "metadata/vgmetadatacopies",
+                                                  DEFAULT_VGMETADATACOPIES);
+       }
+
+       return 0;
+}
+
+int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv)
+{
+       int r = 0;
+
+       if (!cmd->partial_activation && (lv->status & PARTIAL_LV)) {
+               log_error("Refusing refresh of partial LV %s. Use --partial to override.",
+                         lv->name);
+               goto out;
+       }
+
+       r = suspend_lv(cmd, lv);
+       if (!r)
+               goto_out;
+
+       r = resume_lv(cmd, lv);
+       if (!r)
+               goto_out;
+
+       /*
+        * check if snapshot merge should be polled
+        * - unfortunately: even though the dev_manager will clear
+        *   the lv's merge attributes if a merge is not possible;
+        *   it is clearing a different instance of the lv (as
+        *   retrieved with lv_from_lvid)
+        * - fortunately: polldaemon will immediately shutdown if the
+        *   origin doesn't have a status with a snapshot percentage
+        */
+       if (background_polling() && lv_is_origin(lv) && lv_is_merging_origin(lv))
+               lv_spawn_background_polling(cmd, lv);
+
+out:
+       return r;
+}
+
+int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg)
+{
+       struct lv_list *lvl;
+       int r = 1;
+
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (lv_is_visible(lvl->lv))
+                       if (!lv_refresh(cmd, lvl->lv))
+                               r = 0;
+
+       return r;
+}
+
+void lv_spawn_background_polling(struct cmd_context *cmd,
+                                struct logical_volume *lv)
+{
+       const char *pvname;
+
+       if ((lv->status & PVMOVE) &&
+           (pvname = get_pvmove_pvname_from_lv_mirr(lv))) {
+               log_verbose("Spawning background pvmove process for %s",
+                           pvname);
+               pvmove_poll(cmd, pvname, 1);
+       } else if ((lv->status & LOCKED) &&
+           (pvname = get_pvmove_pvname_from_lv(lv))) {
+               log_verbose("Spawning background pvmove process for %s",
+                           pvname);
+               pvmove_poll(cmd, pvname, 1);
+       }
+
+       if (lv->status & (CONVERTING|MERGING)) {
+               log_verbose("Spawning background lvconvert process for %s",
+                       lv->name);
+               lvconvert_poll(cmd, lv, 1);
+       }
+}
+
+/*
+ * Intial sanity checking of non-recovery related command-line arguments.
+ *
+ * Output arguments:
+ * pp: structure allocated by caller, fields written / validated here
+ */
+int pvcreate_params_validate(struct cmd_context *cmd,
+                            int argc, char **argv,
+                            struct pvcreate_params *pp)
+{
+       if (!argc) {
+               log_error("Please enter a physical volume path");
+               return 0;
+       }
+
+       pp->yes = arg_count(cmd, yes_ARG);
+       pp->force = arg_count(cmd, force_ARG);
+
+       if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) {
+               log_error("labelsector must be less than %lu",
+                         LABEL_SCAN_SECTORS);
+               return 0;
+       } else {
+               pp->labelsector = arg_int64_value(cmd, labelsector_ARG,
+                                                 DEFAULT_LABELSECTOR);
+       }
+
+       if (!(cmd->fmt->features & FMT_MDAS) &&
+           (arg_count(cmd, pvmetadatacopies_ARG) ||
+            arg_count(cmd, metadatasize_ARG)   ||
+            arg_count(cmd, dataalignment_ARG)  ||
+            arg_count(cmd, dataalignmentoffset_ARG))) {
+               log_error("Metadata and data alignment parameters only "
+                         "apply to text format.");
+               return 0;
+       }
+
+       if (arg_count(cmd, pvmetadatacopies_ARG) &&
+           arg_int_value(cmd, pvmetadatacopies_ARG, -1) > 2) {
+               log_error("Metadatacopies may only be 0, 1 or 2");
+               return 0;
+       }
+
+       if (arg_count(cmd, metadataignore_ARG)) {
+               pp->metadataignore = !strcmp(arg_str_value(cmd,
+                                               metadataignore_ARG,
+                                               DEFAULT_PVMETADATAIGNORE_STR),
+                                        "y");
+       } else {
+               pp->metadataignore = !strcmp(find_config_tree_str(cmd,
+                                       "metadata/pvmetadataignore",
+                                       DEFAULT_PVMETADATAIGNORE_STR),
+                                       "y");
+       }
+       if (arg_count(cmd, pvmetadatacopies_ARG) &&
+           !arg_int_value(cmd, pvmetadatacopies_ARG, -1) &&
+           pp->metadataignore) {
+               log_error("metadataignore only applies to metadatacopies > 0");
+               return 0;
+       }
+
+       if (arg_count(cmd, zero_ARG))
+               pp->zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n");
+
+       if (arg_sign_value(cmd, dataalignment_ARG, 0) == SIGN_MINUS) {
+               log_error("Physical volume data alignment may not be negative");
+               return 0;
+       }
+       pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0));
+
+       if (pp->data_alignment > ULONG_MAX) {
+               log_error("Physical volume data alignment is too big.");
+               return 0;
+       }
+
+       if (pp->data_alignment && pp->pe_start) {
+               if (pp->pe_start % pp->data_alignment)
+                       log_warn("WARNING: Ignoring data alignment %" PRIu64
+                                " incompatible with --restorefile value (%"
+                                PRIu64").", pp->data_alignment, pp->pe_start);
+               pp->data_alignment = 0;
+       }
+
+       if (arg_sign_value(cmd, dataalignmentoffset_ARG, 0) == SIGN_MINUS) {
+               log_error("Physical volume data alignment offset may not be negative");
+               return 0;
+       }
+       pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0));
+
+       if (pp->data_alignment_offset > ULONG_MAX) {
+               log_error("Physical volume data alignment offset is too big.");
+               return 0;
+       }
+
+       if (pp->data_alignment_offset && pp->pe_start) {
+               log_warn("WARNING: Ignoring data alignment offset %" PRIu64
+                        " incompatible with --restorefile value (%"
+                        PRIu64").", pp->data_alignment_offset, pp->pe_start);
+               pp->data_alignment_offset = 0;
+       }
+
+       if (arg_sign_value(cmd, metadatasize_ARG, 0) == SIGN_MINUS) {
+               log_error("Metadata size may not be negative");
+               return 0;
+       }
+
+       pp->pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0));
+       if (!pp->pvmetadatasize)
+               pp->pvmetadatasize = find_config_tree_int(cmd,
+                                                "metadata/pvmetadatasize",
+                                                DEFAULT_PVMETADATASIZE);
+
+       pp->pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1);
+       if (pp->pvmetadatacopies < 0)
+               pp->pvmetadatacopies = find_config_tree_int(cmd,
+                                                  "metadata/pvmetadatacopies",
+                                                  DEFAULT_PVMETADATACOPIES);
+
+       return 1;
+}
+
+int get_activation_monitoring_mode(struct cmd_context *cmd,
+                                  struct volume_group *vg,
+                                  int *monitoring_mode)
+{
+       *monitoring_mode = DEFAULT_DMEVENTD_MONITOR;
+
+       if (arg_count(cmd, monitor_ARG) &&
+           (arg_count(cmd, ignoremonitoring_ARG) ||
+            arg_count(cmd, sysinit_ARG))) {
+               log_error("--ignoremonitoring or --sysinit option not allowed with --monitor option");
+               return 0;
+       }
+
+       if (arg_count(cmd, monitor_ARG))
+               *monitoring_mode = arg_int_value(cmd, monitor_ARG,
+                                                DEFAULT_DMEVENTD_MONITOR);
+       else if (is_static() || arg_count(cmd, ignoremonitoring_ARG) ||
+                arg_count(cmd, sysinit_ARG) ||
+                !find_config_tree_bool(cmd, "activation/monitoring",
+                                       DEFAULT_DMEVENTD_MONITOR))
+               *monitoring_mode = DMEVENTD_MONITOR_IGNORE;
+
+       if (vg && vg_is_clustered(vg) &&
+           *monitoring_mode == DMEVENTD_MONITOR_IGNORE) {
+               log_error("%s is incompatible with clustered Volume Group "
+                         "\"%s\": Skipping.",
+                         (arg_count(cmd, ignoremonitoring_ARG) ?
+                          "--ignoremonitoring" : "activation/monitoring=0"),
+                         vg->name);
+               return 0;
+       }
+       
+       return 1;
+}
+
+/*
+ * Generic stripe parameter checks.
+ */
+static int _validate_stripe_params(struct cmd_context *cmd, uint32_t *stripes,
+                                  uint32_t *stripe_size)
+{
+       if (*stripes == 1 && *stripe_size) {
+               log_print("Ignoring stripesize argument with single stripe");
+               *stripe_size = 0;
+       }
+
+       if (*stripes > 1 && !*stripe_size) {
+               *stripe_size = find_config_tree_int(cmd, "metadata/stripesize", DEFAULT_STRIPESIZE) * 2;
+               log_print("Using default stripesize %s",
+                         display_size(cmd, (uint64_t) *stripe_size));
+       }
+
+       if (*stripes < 1 || *stripes > MAX_STRIPES) {
+               log_error("Number of stripes (%d) must be between %d and %d",
+                         *stripes, 1, MAX_STRIPES);
+               return 0;
+       }
+
+       if (*stripes > 1 && (*stripe_size < STRIPE_SIZE_MIN ||
+                            *stripe_size & (*stripe_size - 1))) {
+               log_error("Invalid stripe size %s",
+                         display_size(cmd, (uint64_t) *stripe_size));
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * The stripe size is limited by the size of a uint32_t, but since the
+ * value given by the user is doubled, and the final result must be a
+ * power of 2, we must divide UINT_MAX by four and add 1 (to round it
+ * up to the power of 2)
+ */
+int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stripe_size)
+{
+       /* stripes_long_ARG takes precedence (for lvconvert) */
+        *stripes = arg_uint_value(cmd, arg_count(cmd, stripes_long_ARG) ? stripes_long_ARG : stripes_ARG, 1);
+
+       *stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
+       if (*stripe_size) {
+               if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
+                       log_error("Negative stripesize is invalid");
+                       return 0;
+               }
+
+               if(*stripe_size > STRIPE_SIZE_LIMIT * 2) {
+                       log_error("Stripe size cannot be larger than %s",
+                                 display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
+                       return 0;
+               }
+       }
+
+       return _validate_stripe_params(cmd, stripes, stripe_size);
+}
+
diff --git a/tools/toollib.h b/tools/toollib.h
new file mode 100644 (file)
index 0000000..71e516f
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TOOLLIB_H
+#define _LVM_TOOLLIB_H
+
+#include "metadata-exported.h"
+
+int autobackup_set(void);
+int autobackup_init(const char *backup_dir, int keep_days, int keep_number,
+                   int autobackup);
+int autobackup(struct volume_group *vg);
+
+struct volume_group *recover_vg(struct cmd_context *cmd, const char *vgname,
+                               uint32_t lock_type);
+
+typedef int (*process_single_vg_fn_t) (struct cmd_context * cmd,
+                                      const char *vg_name,
+                                      struct volume_group * vg,
+                                      void *handle);
+typedef int (*process_single_pv_fn_t) (struct cmd_context *cmd,
+                                 struct volume_group *vg,
+                                 struct physical_volume *pv,
+                                 void *handle);
+typedef int (*process_single_lv_fn_t) (struct cmd_context *cmd,
+                                 struct logical_volume *lv,
+                                 void *handle);
+typedef int (*process_single_seg_fn_t) (struct cmd_context * cmd,
+                                       struct lv_segment * seg,
+                                       void *handle);
+typedef int (*process_single_pvseg_fn_t) (struct cmd_context * cmd,
+                                         struct volume_group * vg,
+                                         struct pv_segment * pvseg,
+                                         void *handle);
+
+int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
+                   uint32_t flags, void *handle,
+                   process_single_vg_fn_t process_single_vg);
+
+int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
+                   struct volume_group *vg, uint32_t lock_type,
+                   int scan_label_only, void *handle,
+                   process_single_pv_fn_t process_single_pv);
+
+int process_each_segment_in_pv(struct cmd_context *cmd,
+                              struct volume_group *vg,
+                              struct physical_volume *pv,
+                              void *handle,
+                              process_single_pvseg_fn_t process_single_pvseg);
+
+int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
+                   uint32_t flags, void *handle,
+                   process_single_lv_fn_t process_single_lv);
+
+
+int process_each_segment_in_lv(struct cmd_context *cmd,
+                              struct logical_volume *lv, void *handle,
+                              process_single_seg_fn_t process_single_seg);
+
+int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
+                         const struct dm_list *tags, void *handle,
+                         process_single_pv_fn_t process_single_pv);
+
+
+int process_each_lv_in_vg(struct cmd_context *cmd,
+                         struct volume_group *vg,
+                         const struct dm_list *arg_lvnames,
+                         const struct dm_list *tags,
+                         struct dm_list *failed_lvnames,
+                         void *handle,
+                         process_single_lv_fn_t process_single_lv);
+
+char *default_vgname(struct cmd_context *cmd);
+const char *extract_vgname(struct cmd_context *cmd, const char *lv_name);
+char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
+                  unsigned *dev_dir_found);
+
+/*
+ * Builds a list of pv's from the names in argv.  Used in
+ * lvcreate/extend.
+ */
+struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int argc,
+                           char **argv, int allocatable_only);
+
+struct dm_list *clone_pv_list(struct dm_pool *mem, struct dm_list *pvs);
+
+void vgcreate_params_set_defaults(struct vgcreate_params *vp_def,
+                                struct volume_group *vg);
+int vgcreate_params_set_from_args(struct cmd_context *cmd,
+                                 struct vgcreate_params *vp_new,
+                                 struct vgcreate_params *vp_def);
+int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv);
+int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg);
+void lv_spawn_background_polling(struct cmd_context *cmd,
+                                struct logical_volume *lv);
+int pvcreate_params_validate(struct cmd_context *cmd,
+                            int argc, char **argv,
+                            struct pvcreate_params *pp);
+
+int get_activation_monitoring_mode(struct cmd_context *cmd,
+                                  struct volume_group *vg,
+                                  int *monitoring_mode);
+int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes,
+                     uint32_t *stripe_size);
+
+#endif
diff --git a/tools/tools.h b/tools/tools.h
new file mode 100644 (file)
index 0000000..cfbceeb
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#ifndef _LVM_TOOLS_H
+#define _LVM_TOOLS_H
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+#include <assert.h>
+#include "libdevmapper.h"
+
+#include "lvm-types.h"
+#include "lvm-logging.h"
+#include "activate.h"
+#include "archiver.h"
+#include "lvmcache.h"
+#include "config.h"
+#include "defaults.h"
+#include "dev-cache.h"
+#include "device.h"
+#include "display.h"
+#include "errors.h"
+#include "filter.h"
+#include "filter-composite.h"
+#include "filter-persistent.h"
+#include "filter-regex.h"
+#include "metadata-exported.h"
+#include "locking.h"
+#include "lvm-exec.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "segtype.h"
+#include "str_list.h"
+#include "toolcontext.h"
+#include "toollib.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+#define CMD_LEN 256
+#define MAX_ARGS 64
+
+/* command functions */
+typedef int (*command_fn) (struct cmd_context * cmd, int argc, char **argv);
+
+#define xx(a, b...) int a(struct cmd_context *cmd, int argc, char **argv);
+#include "commands.h"
+#undef xx
+
+/* define the enums for the command line switches */
+enum {
+#define arg(a, b, c, d, e) a ,
+#include "args.h"
+#undef arg
+};
+
+typedef enum {
+       SIGN_NONE = 0,
+       SIGN_PLUS = 1,
+       SIGN_MINUS = 2
+} sign_t;
+
+typedef enum {
+       PERCENT_NONE = 0,
+       PERCENT_VG,
+       PERCENT_FREE,
+       PERCENT_LV,
+       PERCENT_PVS,
+       PERCENT_ORIGIN
+} percent_type_t;
+
+enum {
+       CHANGE_AY = 0,
+       CHANGE_AN = 1,
+       CHANGE_AE = 2,
+       CHANGE_ALY = 3,
+       CHANGE_ALN = 4
+};
+
+#define ARG_COUNTABLE 0x00000001       /* E.g. -vvvv */
+#define ARG_GROUPABLE 0x00000002       /* E.g. --addtag */
+
+struct arg_values {
+       unsigned count;
+       char *value;
+       int32_t i_value;
+       uint32_t ui_value;
+       int64_t i64_value;
+       uint64_t ui64_value;
+       sign_t sign;
+       percent_type_t percent;
+/*     void *ptr; // Currently not used. */
+};
+
+/* a global table of possible arguments */
+struct arg_props {
+       const char short_arg;
+       char _padding[7];
+       const char *long_arg;
+
+       int (*fn) (struct cmd_context *cmd, struct arg_values *av);
+       uint32_t flags;
+};
+
+struct arg_value_group_list {
+        struct dm_list list;
+        struct arg_values arg_values[0];
+};
+
+#define CACHE_VGMETADATA       0x00000001
+#define PERMITTED_READ_ONLY    0x00000002
+
+/* a register of the lvm commands */
+struct command {
+       const char *name;
+       const char *desc;
+       const char *usage;
+       command_fn fn;
+
+       unsigned flags;
+
+       int num_args;
+       int *valid_args;
+};
+
+void usage(const char *name);
+
+/* the argument verify/normalise functions */
+int yes_no_arg(struct cmd_context *cmd, struct arg_values *av);
+int yes_no_excl_arg(struct cmd_context *cmd, struct arg_values *av);
+int size_kb_arg(struct cmd_context *cmd, struct arg_values *av);
+int size_mb_arg(struct cmd_context *cmd, struct arg_values *av);
+int int_arg(struct cmd_context *cmd, struct arg_values *av);
+int int_arg_with_sign(struct cmd_context *cmd, struct arg_values *av);
+int int_arg_with_sign_and_percent(struct cmd_context *cmd, struct arg_values *av);
+int major_arg(struct cmd_context *cmd, struct arg_values *av);
+int minor_arg(struct cmd_context *cmd, struct arg_values *av);
+int string_arg(struct cmd_context *cmd, struct arg_values *av);
+int tag_arg(struct cmd_context *cmd, struct arg_values *av);
+int permission_arg(struct cmd_context *cmd, struct arg_values *av);
+int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av);
+int units_arg(struct cmd_context *cmd, struct arg_values *av);
+int segtype_arg(struct cmd_context *cmd, struct arg_values *av);
+int alloc_arg(struct cmd_context *cmd, struct arg_values *av);
+int readahead_arg(struct cmd_context *cmd, struct arg_values *av);
+int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
+
+/* we use the enums to access the switches */
+unsigned arg_count(const struct cmd_context *cmd, int a);
+unsigned arg_is_set(const struct cmd_context *cmd, int a);
+const char *arg_value(struct cmd_context *cmd, int a);
+const char *arg_str_value(struct cmd_context *cmd, int a, const char *def);
+int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def); 
+uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def);
+int64_t arg_int64_value(struct cmd_context *cmd, int a, const int64_t def);
+uint64_t arg_uint64_value(struct cmd_context *cmd, int a, const uint64_t def);
+const void *arg_ptr_value(struct cmd_context *cmd, int a, const void *def);
+sign_t arg_sign_value(struct cmd_context *cmd, int a, const sign_t def);
+percent_type_t arg_percent_value(struct cmd_context *cmd, int a, const percent_type_t def);
+int arg_count_increment(struct cmd_context *cmd, int a);
+
+unsigned grouped_arg_count(const struct arg_values *av, int a);
+unsigned grouped_arg_is_set(const struct arg_values *av, int a);
+const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def);
+
+const char *command_name(struct cmd_context *cmd);
+
+int pvmove_poll(struct cmd_context *cmd, const char *pv, unsigned background);
+int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, unsigned background);
+
+#endif
diff --git a/tools/vgcfgbackup.c b/tools/vgcfgbackup.c
new file mode 100644 (file)
index 0000000..2be3949
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static char *_expand_filename(const char *template, const char *vg_name,
+                             char **last_filename)
+{
+       char *filename;
+
+       if (security_level())
+               return dm_strdup(template);
+
+       if (!(filename = dm_malloc(PATH_MAX))) {
+               log_error("Failed to allocate filename.");
+               return NULL;
+       }
+
+       if (snprintf(filename, PATH_MAX, template, vg_name) < 0) {
+               log_error("Error processing filename template %s",
+                          template);
+               dm_free(filename);      
+               return NULL;
+       }
+       if (*last_filename && !strncmp(*last_filename, filename, PATH_MAX)) {
+               log_error("VGs must be backed up into different files. "
+                         "Use %%s in filename for VG name.");
+               dm_free(filename);
+               return NULL;
+       }
+
+       dm_free(*last_filename);
+       *last_filename = filename;
+
+       return filename;
+}
+
+static int vg_backup_single(struct cmd_context *cmd, const char *vg_name,
+                           struct volume_group *vg,
+                           void *handle)
+{
+       char **last_filename = (char **)handle;
+       char *filename;
+
+       if (arg_count(cmd, file_ARG)) {
+               if (!(filename = _expand_filename(arg_value(cmd, file_ARG),
+                                                 vg->name, last_filename))) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               if (!backup_to_file(filename, vg->cmd->cmd_line, vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       } else {
+               if (vg_read_error(vg) == FAILED_INCONSISTENT) {
+                       log_error("No backup taken: specify filename with -f "
+                                 "to backup an inconsistent VG");
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               /* just use the normal backup code */
+               backup_enable(cmd, 1);  /* force a backup */
+               if (!backup(vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       log_print("Volume group \"%s\" successfully backed up.", vg_name);
+       return ECMD_PROCESSED;
+}
+
+int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv)
+{
+       int ret;
+       char *last_filename = NULL;
+
+       init_pvmove(1);
+
+       ret = process_each_vg(cmd, argc, argv, READ_ALLOW_INCONSISTENT,
+                             &last_filename, &vg_backup_single);
+
+       dm_free(last_filename);
+
+       init_pvmove(0);
+
+       return ret;
+}
diff --git a/tools/vgcfgrestore.c b/tools/vgcfgrestore.c
new file mode 100644 (file)
index 0000000..dc0158f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv)
+{
+       char *vg_name = NULL;
+
+       if (argc == 1) {
+               vg_name = skip_dev_dir(cmd, argv[0], NULL);
+               if (!validate_name(vg_name)) {
+                       log_error("Volume group name \"%s\" is invalid", vg_name);
+                       return ECMD_FAILED;
+               }
+       } else if (!(arg_count(cmd, list_ARG) && arg_count(cmd, file_ARG))) {
+               log_error("Please specify a *single* volume group to restore.");
+               return ECMD_FAILED;
+       }
+
+       /*
+        * FIXME: overloading the -l arg for now to display a
+        * list of archive files for a particular vg
+        */
+       if (arg_count(cmd, list_ARG)) {
+               if (!(arg_count(cmd,file_ARG) ?
+                           archive_display_file(cmd,
+                               arg_str_value(cmd, file_ARG, "")) :
+                           archive_display(cmd, vg_name))) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               return ECMD_PROCESSED;
+       }
+
+       if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
+               log_error("Unable to lock volume group %s", vg_name);
+               return ECMD_FAILED;
+       }
+
+       if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+               log_error("Unable to lock orphans");
+               unlock_vg(cmd, vg_name);
+               return ECMD_FAILED;
+       }
+
+       cmd->handles_unknown_segments = 1;
+
+       if (!(arg_count(cmd, file_ARG) ?
+             backup_restore_from_file(cmd, vg_name,
+                                      arg_str_value(cmd, file_ARG, "")) :
+             backup_restore(cmd, vg_name))) {
+               unlock_vg(cmd, VG_ORPHANS);
+               unlock_vg(cmd, vg_name);
+               log_error("Restore failed.");
+               return ECMD_FAILED;
+       }
+
+       log_print("Restored volume group %s", vg_name);
+
+       unlock_vg(cmd, VG_ORPHANS);
+       unlock_vg(cmd, vg_name);
+       return ECMD_PROCESSED;
+}
diff --git a/tools/vgchange.c b/tools/vgchange.c
new file mode 100644 (file)
index 0000000..bf7ebd8
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+/*
+ * Increments *count by the number of _new_ monitored devices.
+ */
+static int _monitor_lvs_in_vg(struct cmd_context *cmd,
+                             struct volume_group *vg, int reg, int *count)
+{
+       struct lv_list *lvl;
+       struct logical_volume *lv;
+       struct lvinfo info;
+       int lv_active;
+       int r = 1;
+
+       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;
+
+               /*
+                * FIXME: Need to consider all cases... PVMOVE, etc
+                */
+               if ((lv->status & PVMOVE) || !lv_active)
+                       continue;
+
+               if (!monitor_dev_for_events(cmd, lv, 0, reg)) {
+                       r = 0;
+                       continue;
+               } else
+                       (*count)++;
+       }
+
+       return r;
+}
+
+static int _poll_lvs_in_vg(struct cmd_context *cmd,
+                          struct volume_group *vg)
+{
+       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))) {
+                       lv_spawn_background_polling(cmd, lv);
+                       count++;
+               }
+       }
+
+       /*
+        * returns the number of polled devices
+        * - there is no way to know if lv is already being polled
+        */
+
+       return count;
+}
+
+static int _activate_lvs_in_vg(struct cmd_context *cmd,
+                              struct volume_group *vg, int activate)
+{
+       struct lv_list *lvl;
+       struct logical_volume *lv;
+       int count = 0, expected_count = 0;
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               lv = lvl->lv;
+
+               if (!lv_is_visible(lv))
+                       continue;
+
+               /* Only request activation of snapshot origin devices */
+               if ((lv->status & SNAPSHOT) || lv_is_cow(lv))
+                       continue;
+
+               /* Only request activation of mirror LV */
+               if ((lv->status & MIRROR_IMAGE) || (lv->status & MIRROR_LOG))
+                       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)))
+                       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) ))
+                       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 (lv_is_origin(lv) || (activate == CHANGE_AE)) {
+                       if (!activate_lv_excl(cmd, lv)) {
+                               stack;
+                               continue;
+                       }
+               } else if (activate == CHANGE_ALY) {
+                       if (!activate_lv_local(cmd, lv)) {
+                               stack;
+                               continue;
+                       }
+               } else if (!activate_lv(cmd, lv)) {
+                       stack;
+                       continue;
+               }
+
+               if (background_polling() &&
+                   activate != CHANGE_AN && activate != CHANGE_ALN &&
+                   (lv->status & (PVMOVE|CONVERTING|MERGING)))
+                       lv_spawn_background_polling(cmd, lv);
+
+               count++;
+       }
+
+       if (expected_count)
+               log_verbose("%s %d logical volumes in volume group %s",
+                           (activate == CHANGE_AN || activate == CHANGE_ALN)?
+                           "Deactivated" : "Activated", count, vg->name);
+
+       return (expected_count != count) ? 0 : 1;
+}
+
+static int _vgchange_monitoring(struct cmd_context *cmd, struct volume_group *vg)
+{
+       int r = 1;
+       int monitored = 0;
+
+       if (lvs_in_vg_activated(vg) &&
+           dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) {
+               if (!_monitor_lvs_in_vg(cmd, vg, dmeventd_monitor_mode(), &monitored))
+                       r = 0;
+               log_print("%d logical volume(s) in volume group "
+                           "\"%s\" %smonitored",
+                           monitored, vg->name, (dmeventd_monitor_mode()) ? "" : "un");
+       }
+
+       return r;
+}
+
+static 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 (polled)
+                       log_print("Background polling started for %d logical volume(s) "
+                                 "in volume group \"%s\"",
+                                 polled, vg->name);
+       }
+
+       return 1;
+}
+
+static int _vgchange_available(struct cmd_context *cmd, struct volume_group *vg)
+{
+       int lv_open, active, monitored = 0;
+       int available, r = 1;
+       int activate = 1;
+
+       /*
+        * Safe, since we never write out new metadata here. Required for
+        * partial activation to work.
+        */
+       cmd->handles_missing_pvs = 1;
+
+       available = arg_uint_value(cmd, available_ARG, 0);
+
+       if ((available == CHANGE_AN) || (available == CHANGE_ALN))
+               activate = 0;
+
+       /* FIXME: Force argument to deactivate them? */
+       if (!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;
+       }
+
+       /* FIXME Move into library where clvmd can use it */
+       if (activate)
+               check_current_backup(vg);
+
+       if (activate && (active = lvs_in_vg_activated(vg))) {
+               log_verbose("%d logical volume(s) in volume group \"%s\" "
+                           "already active", active, vg->name);
+               if (dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) {
+                       if (!_monitor_lvs_in_vg(cmd, vg, dmeventd_monitor_mode(), &monitored))
+                               r = 0;
+                       log_verbose("%d existing logical volume(s) in volume "
+                                   "group \"%s\" %smonitored",
+                                   monitored, vg->name,
+                                   dmeventd_monitor_mode() ? "" : "un");
+               }
+       }
+
+       if (!_activate_lvs_in_vg(cmd, vg, available))
+               r = 0;
+
+       /* Print message only if there was not found a missing VG */
+       if (!vg->cmd_missing_vgs)
+               log_print("%d logical volume(s) in volume group \"%s\" now active",
+                         lvs_in_vg_activated(vg), vg->name);
+       return r;
+}
+
+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;
+       }
+
+       return 1;
+}
+
+static int _vgchange_alloc(struct cmd_context *cmd, struct volume_group *vg)
+{
+       alloc_policy_t alloc;
+
+       alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL);
+
+       /* FIXME: make consistent with vg_set_alloc_policy() */
+       if (alloc == vg->alloc) {
+               log_error("Volume group allocation policy is already %s",
+                         get_alloc_string(vg->alloc));
+               return 0;
+       }
+
+       if (!vg_set_alloc_policy(vg, alloc))
+               return_0;
+
+       return 1;
+}
+
+static int _vgchange_resizeable(struct cmd_context *cmd,
+                               struct volume_group *vg)
+{
+       int resizeable = !strcmp(arg_str_value(cmd, resizeable_ARG, "n"), "y");
+
+       if (resizeable && vg_is_resizeable(vg)) {
+               log_error("Volume group \"%s\" is already resizeable",
+                         vg->name);
+               return 0;
+       }
+
+       if (!resizeable && !vg_is_resizeable(vg)) {
+               log_error("Volume group \"%s\" is already not resizeable",
+                         vg->name);
+               return 0;
+       }
+
+       if (resizeable)
+               vg->status |= RESIZEABLE_VG;
+       else
+               vg->status &= ~RESIZEABLE_VG;
+
+       return 1;
+}
+
+static int _vgchange_clustered(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;
+       }
+
+       if (!clustered && !(vg_is_clustered(vg))) {
+               log_error("Volume group \"%s\" is already not clustered",
+                         vg->name);
+               return 0;
+       }
+
+       if (!vg_set_clustered(vg, clustered))
+               return_0;
+
+       return 1;
+}
+
+static int _vgchange_logicalvolume(struct cmd_context *cmd,
+                                  struct volume_group *vg)
+{
+       uint32_t max_lv = arg_uint_value(cmd, logicalvolume_ARG, 0);
+
+       if (!vg_set_max_lv(vg, max_lv))
+               return_0;
+
+       return 1;
+}
+
+static int _vgchange_physicalvolumes(struct cmd_context *cmd,
+                                    struct volume_group *vg)
+{
+       uint32_t max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG, 0);
+
+       if (!vg_set_max_pv(vg, max_pv))
+               return_0;
+
+       return 1;
+}
+
+static int _vgchange_pesize(struct cmd_context *cmd, struct volume_group *vg)
+{
+       uint32_t extent_size;
+
+       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));
+               return 1;
+       }
+
+       if (!vg_set_extent_size(vg, extent_size))
+               return_0;
+
+       return 1;
+}
+
+static int _vgchange_tag(struct cmd_context *cmd, struct volume_group *vg,
+                        int arg)
+{
+       const char *tag;
+       struct arg_value_group_list *current_group;
+
+       dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+               if (!grouped_arg_is_set(current_group->arg_values, arg))
+                       continue;
+
+               if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) {
+                       log_error("Failed to get tag");
+                       return 0;
+               }
+
+               if (!vg_change_tag(vg, tag, arg == addtag_ARG))
+                       return_0;
+
+       }
+
+       return 1;
+}
+
+static int _vgchange_addtag(struct cmd_context *cmd, struct volume_group *vg)
+{
+       return _vgchange_tag(cmd, vg, addtag_ARG);
+}
+
+static int _vgchange_deltag(struct cmd_context *cmd, struct volume_group *vg)
+{
+       return _vgchange_tag(cmd, vg, deltag_ARG);
+}
+
+static int _vgchange_uuid(struct cmd_context *cmd __attribute__((unused)),
+                         struct volume_group *vg)
+{
+       struct lv_list *lvl;
+
+       if (lvs_in_vg_activated(vg)) {
+               log_error("Volume group has active logical volumes");
+               return 0;
+       }
+
+       if (!id_create(&vg->id)) {
+               log_error("Failed to generate new random UUID for VG %s.",
+                         vg->name);
+               return 0;
+       }
+
+       dm_list_iterate_items(lvl, &vg->lvs) {
+               memcpy(&lvl->lv->lvid, &vg->id, sizeof(vg->id));
+       }
+
+       return 1;
+}
+
+static int _vgchange_metadata_copies(struct cmd_context *cmd,
+                                    struct volume_group *vg)
+{
+       uint32_t mda_copies = arg_uint_value(cmd, vgmetadatacopies_ARG, 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);
+               else
+                       log_error("Number of metadata copies for VG %s is already %" PRIu32,
+                                 vg->name, mda_copies);
+               return 1;
+       }
+
+       if (!vg_set_mda_copies(vg, mda_copies))
+               return_0;
+
+       return 1;
+}
+
+static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
+                          struct volume_group *vg,
+                          void *handle __attribute__((unused)))
+{
+       int dmeventd_mode;
+       int archived = 0;
+       int i;
+
+       static 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 },
+               { 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 },
+       };
+
+       if (vg_is_exported(vg)) {
+               log_error("Volume group \"%s\" is exported", vg_name);
+               return ECMD_FAILED;
+       }
+
+       if (!get_activation_monitoring_mode(cmd, vg, &dmeventd_mode))
+               return ECMD_FAILED;
+
+       init_dmeventd_monitor(dmeventd_mode);
+
+       /*
+        * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified".
+        * If --poll is explicitly provided use it; otherwise polling
+        * should only be started if the LV is not already active. So:
+        * 1) change the activation code to say if the LV was actually activated
+        * 2) make polling of an LV tightly coupled with LV activation
+        *
+        * Do not initiate any polling if --sysinit option is used.
+        */
+       init_background_polling(arg_count(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;
+                       }
+               }
+       }
+
+       if (archived) {
+               if (!vg_write(vg) || !vg_commit(vg)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               backup(vg);
+
+               log_print("Volume group \"%s\" successfully changed", vg->name);
+       }
+
+       if (arg_count(cmd, available_ARG)) {
+               if (!_vgchange_available(cmd, vg))
+                       return ECMD_FAILED;
+       }
+
+       if (arg_count(cmd, refresh_ARG)) {
+               /* refreshes the visible LVs (which starts polling) */
+               if (!_vgchange_refresh(cmd, vg))
+                       return ECMD_FAILED;
+       }
+
+       if (!arg_count(cmd, available_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;
+       }
+
+       if (!arg_count(cmd, refresh_ARG) &&
+           arg_count(cmd, poll_ARG))
+               if (!_vgchange_background_polling(cmd, vg))
+                       return ECMD_FAILED;
+
+        return ECMD_PROCESSED;
+}
+
+int vgchange(struct cmd_context *cmd, int argc, char **argv)
+{
+       /* Update commands that can be combined */
+       int update = 
+               arg_count(cmd, logicalvolume_ARG) ||
+               arg_count(cmd, maxphysicalvolumes_ARG) ||
+               arg_count(cmd, resizeable_ARG) ||
+               arg_count(cmd, deltag_ARG) ||
+               arg_count(cmd, addtag_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);
+
+       if (!update &&
+           !arg_count(cmd, available_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");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, available_ARG) && arg_count(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) {
+               log_error("Only -a permitted with --ignorelockingfailure and --sysinit");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, available_ARG) &&
+           (arg_count(cmd, monitor_ARG) || arg_count(cmd, poll_ARG))) {
+               int activate = arg_uint_value(cmd, available_ARG, 0);
+               if (activate == CHANGE_AN || activate == CHANGE_ALN) {
+                       log_error("Only -ay* allowed with --monitor or --poll.");
+                       return EINVALID_CMD_LINE;
+               }
+       }
+
+       if (arg_count(cmd, poll_ARG) && arg_count(cmd, sysinit_ARG)) {
+               log_error("Only one of --poll and --sysinit permitted.");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, available_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) &&
+           arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) {
+               log_error("MaxPhysicalVolumes may not be negative");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, physicalextentsize_ARG) &&
+           arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) {
+               log_error("Physical extent size may not be negative");
+               return EINVALID_CMD_LINE;
+       }
+
+       return process_each_vg(cmd, argc, argv, update ? READ_FOR_UPDATE : 0,
+                              NULL, &vgchange_single);
+}
diff --git a/tools/vgck.c b/tools/vgck.c
new file mode 100644 (file)
index 0000000..bdfee05
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "metadata.h"
+
+static int vgck_single(struct cmd_context *cmd __attribute__((unused)),
+                      const char *vg_name,
+                      struct volume_group *vg,
+                      void *handle __attribute__((unused)))
+{
+       if (!vg_check_status(vg, EXPORTED_VG)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!vg_validate(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (vg_missing_pv_count(vg)) {
+               log_error("The volume group is missing %d physical volumes.",
+                         vg_missing_pv_count(vg));
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int vgck(struct cmd_context *cmd, int argc, char **argv)
+{
+       return process_each_vg(cmd, argc, argv, 0, NULL,
+                              &vgck_single);
+}
diff --git a/tools/vgconvert.c b/tools/vgconvert.c
new file mode 100644 (file)
index 0000000..acae0fc
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int vgconvert_single(struct cmd_context *cmd, const char *vg_name,
+                           struct volume_group *vg,
+                           void *handle __attribute__((unused)))
+{
+       struct physical_volume *pv, *existing_pv;
+       struct logical_volume *lv;
+       struct lv_list *lvl;
+       uint64_t size = 0;
+       struct dm_list mdas;
+       int pvmetadatacopies = 0;
+       uint64_t pvmetadatasize = 0;
+       uint64_t pe_end = 0, pe_start = 0;
+       struct pv_list *pvl;
+       int change_made = 0;
+       struct lvinfo info;
+       int active = 0;
+
+       if (!vg_check_status(vg, LVM_WRITE | EXPORTED_VG)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (vg->fid->fmt == cmd->fmt) {
+               log_error("Volume group \"%s\" already uses format %s",
+                         vg_name, cmd->fmt->name);
+               return ECMD_FAILED;
+       }
+
+       if (cmd->fmt->features & FMT_MDAS) {
+               if (arg_sign_value(cmd, metadatasize_ARG, 0) == SIGN_MINUS) {
+                       log_error("Metadata size may not be negative");
+                       return EINVALID_CMD_LINE;
+               }
+
+               pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG,
+                                                 UINT64_C(0));
+               if (!pvmetadatasize)
+                       pvmetadatasize =
+                           find_config_tree_int(cmd,
+                                           "metadata/pvmetadatasize",
+                                           DEFAULT_PVMETADATASIZE);
+
+               pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1);
+               if (pvmetadatacopies < 0)
+                       pvmetadatacopies =
+                           find_config_tree_int(cmd,
+                                           "metadata/pvmetadatacopies",
+                                            DEFAULT_PVMETADATACOPIES);
+       }
+
+       if (!archive(vg)) {
+               log_error("Archive of \"%s\" metadata failed.", vg_name);
+               return ECMD_FAILED;
+       }
+
+       /* Set PV/LV limit if converting from unlimited metadata format */
+       if (vg->fid->fmt->features & FMT_UNLIMITED_VOLS &&
+           !(cmd->fmt->features & FMT_UNLIMITED_VOLS)) {
+               if (!vg->max_lv)
+                       vg->max_lv = 255;
+               if (!vg->max_pv)
+                       vg->max_pv = 255;
+       }
+
+       /* If converting to restricted lvid, check if lvid is compatible */
+       if (!(vg->fid->fmt->features & FMT_RESTRICTED_LVIDS) &&
+           cmd->fmt->features & FMT_RESTRICTED_LVIDS)
+               dm_list_iterate_items(lvl, &vg->lvs)
+                       if (!lvid_in_restricted_range(&lvl->lv->lvid)) {
+                               log_error("Logical volume %s lvid format is"
+                                         " incompatible with requested"
+                                         " metadata format.", lvl->lv->name);
+                               return ECMD_FAILED;
+                       }
+
+       /* Attempt to change any LVIDs that are too big */
+       if (cmd->fmt->features & FMT_RESTRICTED_LVIDS) {
+               dm_list_iterate_items(lvl, &vg->lvs) {
+                       lv = lvl->lv;
+                       if (lv->status & SNAPSHOT)
+                               continue;
+                       if (lvnum_from_lvid(&lv->lvid) < MAX_RESTRICTED_LVS)
+                               continue;
+                       if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+                               log_error("Logical volume %s must be "
+                                         "deactivated before conversion.",
+                                          lv->name);
+                               active++;
+                               continue;
+                       }
+                       lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv));
+
+               }
+       }
+
+       if (active) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               existing_pv = pvl->pv;
+
+               pe_start = pv_pe_start(existing_pv);
+               pe_end = pv_pe_count(existing_pv) * pv_pe_size(existing_pv)
+                   + pe_start - 1;
+
+               dm_list_init(&mdas);
+               if (!(pv = pv_create(cmd, pv_dev(existing_pv),
+                                    &existing_pv->id, size, 0, 0,
+                                    pe_start, pv_pe_count(existing_pv),
+                                    pv_pe_size(existing_pv), pvmetadatacopies,
+                                    pvmetadatasize, 0, &mdas))) {
+                       log_error("Failed to setup physical volume \"%s\"",
+                                 pv_dev_name(existing_pv));
+                       if (change_made)
+                               log_error("Use pvcreate and vgcfgrestore to "
+                                         "repair from archived metadata.");
+                       return ECMD_FAILED;
+               }
+
+               /* Need to revert manually if it fails after this point */
+               change_made = 1;
+
+               log_verbose("Set up physical volume for \"%s\" with %" PRIu64
+                           " available sectors", pv_dev_name(pv), pv_size(pv));
+
+               /* Wipe existing label first */
+               if (!label_remove(pv_dev(pv))) {
+                       log_error("Failed to wipe existing label on %s",
+                                 pv_dev_name(pv));
+                       log_error("Use pvcreate and vgcfgrestore to repair "
+                                 "from archived metadata.");
+                       return ECMD_FAILED;
+               }
+
+               log_very_verbose("Writing physical volume data to disk \"%s\"",
+                                pv_dev_name(pv));
+               if (!(pv_write(cmd, pv, &mdas,
+                              arg_int64_value(cmd, labelsector_ARG,
+                                              DEFAULT_LABELSECTOR)))) {
+                       log_error("Failed to write physical volume \"%s\"",
+                                 pv_dev_name(pv));
+                       log_error("Use pvcreate and vgcfgrestore to repair "
+                                 "from archived metadata.");
+                       return ECMD_FAILED;
+               }
+               log_verbose("Physical volume \"%s\" successfully created",
+                           pv_dev_name(pv));
+
+       }
+
+       log_verbose("Deleting existing metadata for VG %s", vg_name);
+       if (!vg_remove_mdas(vg)) {
+               log_error("Removal of existing metadata for %s failed.",
+                         vg_name);
+               log_error("Use pvcreate and vgcfgrestore to repair "
+                         "from archived metadata.");
+               return ECMD_FAILED;
+       }
+
+       /* FIXME Cache the label format change so we don't have to skip this */
+       if (test_mode()) {
+               log_verbose("Test mode: Skipping metadata writing for VG %s in"
+                           " format %s", vg_name, cmd->fmt->name);
+               return ECMD_PROCESSED;
+       }
+
+       log_verbose("Writing metadata for VG %s using format %s", vg_name,
+                   cmd->fmt->name);
+       if (!backup_restore_vg(cmd, vg)) {
+               log_error("Conversion failed for volume group %s.", vg_name);
+               log_error("Use pvcreate and vgcfgrestore to repair from "
+                         "archived metadata.");
+               return ECMD_FAILED;
+       }
+       log_print("Volume group %s successfully converted", vg_name);
+
+       backup(vg);
+
+       return ECMD_PROCESSED;
+}
+
+int vgconvert(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (!argc) {
+               log_error("Please enter volume group(s)");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) {
+               log_error("labelsector must be less than %lu",
+                         LABEL_SCAN_SECTORS);
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, metadatacopies_ARG)) {
+               log_error("Invalid option --metadatacopies, "
+                         "use --pvmetadatacopies instead.");
+               return EINVALID_CMD_LINE;
+       }
+       if (!(cmd->fmt->features & FMT_MDAS) &&
+           (arg_count(cmd, pvmetadatacopies_ARG) ||
+            arg_count(cmd, metadatasize_ARG))) {
+               log_error("Metadata parameters only apply to text format");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, pvmetadatacopies_ARG) &&
+           arg_int_value(cmd, pvmetadatacopies_ARG, -1) > 2) {
+               log_error("Metadatacopies may only be 0, 1 or 2");
+               return EINVALID_CMD_LINE;
+       }
+
+       return process_each_vg(cmd, argc, argv, READ_FOR_UPDATE, NULL,
+                              &vgconvert_single);
+}
diff --git a/tools/vgcreate.c b/tools/vgcreate.c
new file mode 100644 (file)
index 0000000..49574f3
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+int vgcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+       struct vgcreate_params vp_new;
+       struct vgcreate_params vp_def;
+       struct volume_group *vg;
+       const char *tag;
+       const char *clustered_message = "";
+       char *vg_name;
+       struct pvcreate_params pp;
+       struct arg_value_group_list *current_group;
+
+       if (!argc) {
+               log_error("Please provide volume group name and "
+                         "physical volumes");
+               return EINVALID_CMD_LINE;
+       }
+
+       vg_name = argv[0];
+       argc--;
+       argv++;
+
+       pvcreate_params_set_defaults(&pp);
+       if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
+               return EINVALID_CMD_LINE;
+       }
+
+       vgcreate_params_set_defaults(&vp_def, NULL);
+       vp_def.vg_name = vg_name;
+       if (vgcreate_params_set_from_args(cmd, &vp_new, &vp_def))
+               return EINVALID_CMD_LINE;
+
+       if (vgcreate_params_validate(cmd, &vp_new))
+           return EINVALID_CMD_LINE;
+
+       /* Create the new VG */
+       vg = vg_create(cmd, vp_new.vg_name);
+       if (vg_read_error(vg)) {
+               if (vg_read_error(vg) == FAILED_EXIST)
+                       log_error("A volume group called %s already exists.", vp_new.vg_name);
+               else
+                       log_error("Can't get lock for %s.", vp_new.vg_name);
+               free_vg(vg);
+               return ECMD_FAILED;
+       }
+
+       if (!vg_set_extent_size(vg, vp_new.extent_size) ||
+           !vg_set_max_lv(vg, vp_new.max_lv) ||
+           !vg_set_max_pv(vg, vp_new.max_pv) ||
+           !vg_set_alloc_policy(vg, vp_new.alloc) ||
+           !vg_set_clustered(vg, vp_new.clustered) ||
+           !vg_set_mda_copies(vg, vp_new.vgmetadatacopies))
+               goto bad_orphan;
+
+       if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+               log_error("Can't get lock for orphan PVs");
+               goto bad_orphan;
+       }
+
+       /* attach the pv's */
+       if (!vg_extend(vg, argc, argv, &pp))
+               goto_bad;
+
+       if (vp_new.max_lv != vg->max_lv)
+               log_warn("WARNING: Setting maxlogicalvolumes to %d "
+                        "(0 means unlimited)", vg->max_lv);
+
+       if (vp_new.max_pv != vg->max_pv)
+               log_warn("WARNING: Setting maxphysicalvolumes to %d "
+                        "(0 means unlimited)", vg->max_pv);
+
+       if (arg_count(cmd, addtag_ARG)) {
+               dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+                       if (!grouped_arg_is_set(current_group->arg_values, addtag_ARG))
+                               continue;
+
+                       if (!(tag = grouped_arg_str_value(current_group->arg_values, addtag_ARG, NULL))) {
+                               log_error("Failed to get tag");
+                               goto bad;
+                       }
+
+                       if (!vg_change_tag(vg, tag, 1))
+                               goto_bad;
+               }
+       }
+
+       if (vg_is_clustered(vg))
+               clustered_message = "Clustered ";
+       else if (locking_is_clustered())
+               clustered_message = "Non-clustered ";
+
+       if (!archive(vg))
+               goto_bad;
+
+       /* Store VG on disk(s) */
+       if (!vg_write(vg) || !vg_commit(vg))
+               goto_bad;
+
+       unlock_vg(cmd, VG_ORPHANS);
+       unlock_vg(cmd, vp_new.vg_name);
+
+       backup(vg);
+
+       log_print("%s%colume group \"%s\" successfully created",
+                 clustered_message, *clustered_message ? 'v' : 'V', vg->name);
+
+       free_vg(vg);
+       return ECMD_PROCESSED;
+
+bad:
+       unlock_vg(cmd, VG_ORPHANS);
+bad_orphan:
+       free_vg(vg);
+       unlock_vg(cmd, vp_new.vg_name);
+       return ECMD_FAILED;
+}
diff --git a/tools/vgdisplay.c b/tools/vgdisplay.c
new file mode 100644 (file)
index 0000000..afc92fe
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name,
+                           struct volume_group *vg,
+                           void *handle __attribute__((unused)))
+{
+       /* FIXME Do the active check here if activevolumegroups_ARG ? */
+       vg_check_status(vg, EXPORTED_VG);
+
+       if (arg_count(cmd, colon_ARG)) {
+               vgdisplay_colons(vg);
+               return ECMD_PROCESSED;
+       }
+
+       if (arg_count(cmd, short_ARG)) {
+               vgdisplay_short(vg);
+               return ECMD_PROCESSED;
+       }
+
+       vgdisplay_full(vg);     /* was vg_show */
+
+       if (arg_count(cmd, verbose_ARG)) {
+               vgdisplay_extents(vg);
+
+               process_each_lv_in_vg(cmd, vg, NULL, NULL, NULL, NULL,
+                                     (process_single_lv_fn_t)lvdisplay_full);
+
+               log_print("--- Physical volumes ---");
+               process_each_pv_in_vg(cmd, vg, NULL, NULL,
+                                     (process_single_pv_fn_t)pvdisplay_short);
+       }
+
+       check_current_backup(vg);
+
+       return ECMD_PROCESSED;
+}
+
+int vgdisplay(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (arg_count(cmd, columns_ARG)) {
+               if (arg_count(cmd, colon_ARG) ||
+                   arg_count(cmd, activevolumegroups_ARG) ||
+                   arg_count(cmd, short_ARG)) {
+                       log_error("Incompatible options selected");
+                       return EINVALID_CMD_LINE;
+               }
+               return vgs(cmd, argc, argv);
+       } else if (arg_count(cmd, aligned_ARG) ||
+                  arg_count(cmd, noheadings_ARG) ||
+                  arg_count(cmd, options_ARG) ||
+                  arg_count(cmd, separator_ARG) ||
+                  arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) {
+               log_error("Incompatible options selected");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, colon_ARG) && arg_count(cmd, short_ARG)) {
+               log_error("Option -c is not allowed with option -s");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (argc && arg_count(cmd, activevolumegroups_ARG)) {
+               log_error("Option -A is not allowed with volume group names");
+               return EINVALID_CMD_LINE;
+       }
+
+/********* FIXME: Do without this - or else 2(+) passes!
+          Figure out longest volume group name
+       for (c = opt; opt < argc; opt++) {
+               len = strlen(argv[opt]);
+               if (len > max_len)
+                       max_len = len;
+       }
+**********/
+
+       return process_each_vg(cmd, argc, argv, 0, NULL,
+                              vgdisplay_single);
+
+/******** FIXME Need to count number processed
+         Add this to process_each_vg if arg_count(cmd,activevolumegroups_ARG) ?
+
+       if (opt == argc) {
+               log_print("no ");
+               if (arg_count(cmd,activevolumegroups_ARG))
+                       printf("active ");
+               printf("volume groups found\n\n");
+               return LVM_E_NO_VG;
+       }
+************/
+}
diff --git a/tools/vgexport.c b/tools/vgexport.c
new file mode 100644 (file)
index 0000000..a1043d2
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int vgexport_single(struct cmd_context *cmd __attribute__((unused)),
+                          const char *vg_name,
+                          struct volume_group *vg,
+                          void *handle __attribute__((unused)))
+{
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+
+       if (lvs_in_vg_activated(vg)) {
+               log_error("Volume group \"%s\" has active logical volumes",
+                         vg_name);
+               goto bad;
+       }
+
+       if (!archive(vg))
+               goto_bad;
+
+       vg->status |= EXPORTED_VG;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+               pv->status |= EXPORTED_VG;
+       }
+
+       if (!vg_write(vg) || !vg_commit(vg))
+               goto_bad;
+
+       backup(vg);
+
+       log_print("Volume group \"%s\" successfully exported", vg->name);
+
+       return ECMD_PROCESSED;
+
+bad:
+       return ECMD_FAILED;
+}
+
+int vgexport(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (!argc && !arg_count(cmd, all_ARG)) {
+               log_error("Please supply volume groups or use -a for all.");
+               return ECMD_FAILED;
+       }
+
+       if (argc && arg_count(cmd, all_ARG)) {
+               log_error("No arguments permitted when using -a for all.");
+               return ECMD_FAILED;
+       }
+
+       return process_each_vg(cmd, argc, argv, READ_FOR_UPDATE, NULL,
+                              &vgexport_single);
+}
diff --git a/tools/vgextend.c b/tools/vgextend.c
new file mode 100644 (file)
index 0000000..a0c166b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int _restore_pv(struct volume_group *vg, char *pv_name)
+{
+       struct pv_list *pvl = NULL;
+       pvl = find_pv_in_vg(vg, pv_name);
+       if (!pvl) {
+               log_warn("WARNING: PV %s not found in VG %s", pv_name, vg->name);
+               return 0;
+       }
+
+       if (!(pvl->pv->status & MISSING_PV)) {
+               log_warn("WARNING: PV %s was not missing in VG %s", pv_name, vg->name);
+               return 0;
+       }
+
+       if (!pvl->pv->dev) {
+               log_warn("WARNING: The PV %s is still missing.", pv_name);
+               return 0;
+       }
+
+       pvl->pv->status &= ~MISSING_PV;
+       return 1;
+}
+
+int vgextend(struct cmd_context *cmd, int argc, char **argv)
+{
+       char *vg_name;
+       struct volume_group *vg = NULL;
+       int r = ECMD_FAILED;
+       struct pvcreate_params pp;
+       int fixed = 0, i = 0;
+
+       if (!argc) {
+               log_error("Please enter volume group name and "
+                         "physical volume(s)");
+               return EINVALID_CMD_LINE;
+       }
+
+       vg_name = skip_dev_dir(cmd, argv[0], NULL);
+       argc--;
+       argv++;
+
+       if (arg_count(cmd, metadatacopies_ARG)) {
+               log_error("Invalid option --metadatacopies, "
+                         "use --pvmetadatacopies instead.");
+               return EINVALID_CMD_LINE;
+       }
+       pvcreate_params_set_defaults(&pp);
+       if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, restoremissing_ARG))
+               cmd->handles_missing_pvs = 1;
+
+       log_verbose("Checking for volume group \"%s\"", vg_name);
+       vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               stack;
+               return ECMD_FAILED;
+       }
+
+       if (!archive(vg))
+               goto_bad;
+
+       if (arg_count(cmd, restoremissing_ARG)) {
+               for (i = 0; i < argc; ++i) {
+                       if (_restore_pv(vg, argv[i]))
+                               ++ fixed;
+               }
+               if (!fixed) {
+                       log_error("No PV has been restored.");
+                       goto_bad;
+               }
+       } else { /* no --restore, normal vgextend */
+               if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+                       log_error("Can't get lock for orphan PVs");
+                       unlock_and_free_vg(cmd, vg, vg_name);
+                       return ECMD_FAILED;
+               }
+
+               if (arg_count(cmd, metadataignore_ARG) &&
+                   (vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) &&
+                   (pp.force == PROMPT) &&
+                   yes_no_prompt("Override preferred number of copies "
+                         "of VG %s metadata? [y/n]: ",
+                                 vg_name) == 'n') {
+                       log_error("Volume group %s not changed", vg_name);
+                       goto_bad;
+               }
+
+               /* extend vg */
+               if (!vg_extend(vg, argc, argv, &pp))
+                       goto_bad;
+
+               if (arg_count(cmd, metadataignore_ARG) &&
+                   (vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) &&
+                   (vg_mda_copies(vg) != vg_mda_used_count(vg))) {
+                       log_warn("WARNING: Changing preferred number of copies of VG %s "
+                        "metadata from %"PRIu32" to %"PRIu32, vg_name,
+                                vg_mda_copies(vg), vg_mda_used_count(vg));
+                       vg_set_mda_copies(vg, vg_mda_used_count(vg));
+               }
+
+               /* ret > 0 */
+               log_verbose("Volume group \"%s\" will be extended by %d new "
+                           "physical volumes", vg_name, argc);
+       }
+
+       /* store vg on disk(s) */
+       if (!vg_write(vg) || !vg_commit(vg))
+               goto_bad;
+
+       backup(vg);
+       log_print("Volume group \"%s\" successfully extended", vg_name);
+       r = ECMD_PROCESSED;
+
+bad:
+       if (!arg_count(cmd, restoremissing_ARG))
+               unlock_vg(cmd, VG_ORPHANS);
+       unlock_and_free_vg(cmd, vg, vg_name);
+       return r;
+}
diff --git a/tools/vgimport.c b/tools/vgimport.c
new file mode 100644 (file)
index 0000000..284f536
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int vgimport_single(struct cmd_context *cmd __attribute__((unused)),
+                          const char *vg_name,
+                          struct volume_group *vg,
+                          void *handle __attribute__((unused)))
+{
+       struct pv_list *pvl;
+       struct physical_volume *pv;
+
+       if (!vg_is_exported(vg)) {
+               log_error("Volume group \"%s\" is not exported", vg_name);
+               goto bad;
+       }
+
+       if (vg_status(vg) & PARTIAL_VG) {
+               log_error("Volume group \"%s\" is partially missing", vg_name);
+               goto bad;
+       }
+
+       if (!archive(vg))
+               goto_bad;
+
+       vg->status &= ~EXPORTED_VG;
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               pv = pvl->pv;
+               pv->status &= ~EXPORTED_VG;
+       }
+
+       if (!vg_write(vg) || !vg_commit(vg))
+               goto_bad;
+
+       backup(vg);
+
+       log_print("Volume group \"%s\" successfully imported", vg->name);
+
+       return ECMD_PROCESSED;
+
+bad:
+       return ECMD_FAILED;
+}
+
+int vgimport(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (!argc && !arg_count(cmd, all_ARG)) {
+               log_error("Please supply volume groups or use -a for all.");
+               return ECMD_FAILED;
+       }
+
+       if (argc && arg_count(cmd, all_ARG)) {
+               log_error("No arguments permitted when using -a for all.");
+               return ECMD_FAILED;
+       }
+
+       return process_each_vg(cmd, argc, argv,
+                              READ_FOR_UPDATE | READ_ALLOW_EXPORTED,
+                              NULL,
+                              &vgimport_single);
+}
diff --git a/tools/vgmerge.c b/tools/vgmerge.c
new file mode 100644 (file)
index 0000000..391764f
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static struct volume_group *_vgmerge_vg_read(struct cmd_context *cmd,
+                                            const char *vg_name)
+{
+       struct volume_group *vg;
+       log_verbose("Checking for volume group \"%s\"", vg_name);
+       vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               return NULL;
+       }
+       return vg;
+}
+
+static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to,
+                          const char *vg_name_from)
+{
+       struct pv_list *pvl, *tpvl;
+       struct volume_group *vg_to, *vg_from;
+       struct lv_list *lvl1, *lvl2;
+       int r = ECMD_FAILED;
+       int lock_vg_from_first = 0;
+
+       if (!strcmp(vg_name_to, vg_name_from)) {
+               log_error("Duplicate volume group name \"%s\"", vg_name_from);
+               return ECMD_FAILED;
+       }
+
+       if (strcmp(vg_name_to, vg_name_from) > 0)
+               lock_vg_from_first = 1;
+
+       if (lock_vg_from_first) {
+               vg_from = _vgmerge_vg_read(cmd, vg_name_from);
+               if (!vg_from) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               vg_to = _vgmerge_vg_read(cmd, vg_name_to);
+               if (!vg_to) {
+                       stack;
+                       unlock_and_free_vg(cmd, vg_from, vg_name_from);
+                       return ECMD_FAILED;
+               }
+       } else {
+               vg_to = _vgmerge_vg_read(cmd, vg_name_to);
+               if (!vg_to) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               vg_from = _vgmerge_vg_read(cmd, vg_name_from);
+               if (!vg_from) {
+                       stack;
+                       unlock_and_free_vg(cmd, vg_to, vg_name_to);
+                       return ECMD_FAILED;
+               }
+       }
+
+       if (!vgs_are_compatible(cmd, vg_from, vg_to))
+               goto_bad;
+
+       /* FIXME List arg: vg_show_with_pv_and_lv(vg_to); */
+
+       if (!archive(vg_from) || !archive(vg_to))
+               goto_bad;
+
+       drop_cached_metadata(vg_from);
+
+       /* Merge volume groups */
+       dm_list_iterate_items_safe(pvl, tpvl, &vg_from->pvs) {
+               del_pvl_from_vgs(vg_from, pvl);
+               add_pvl_to_vgs(vg_to, pvl);
+               pvl->pv->vg_name = dm_pool_strdup(cmd->mem, vg_to->name);
+       }
+
+       /* Fix up LVIDs */
+       dm_list_iterate_items(lvl1, &vg_to->lvs) {
+               union lvid *lvid1 = &lvl1->lv->lvid;
+               char uuid[64] __attribute__((aligned(8)));
+
+               dm_list_iterate_items(lvl2, &vg_from->lvs) {
+                       union lvid *lvid2 = &lvl2->lv->lvid;
+
+                       if (id_equal(&lvid1->id[1], &lvid2->id[1])) {
+                               if (!id_create(&lvid2->id[1])) {
+                                       log_error("Failed to generate new "
+                                                 "random LVID for %s",
+                                                 lvl2->lv->name);
+                                       goto bad;
+                               }
+                               if (!id_write_format(&lvid2->id[1], uuid,
+                                                    sizeof(uuid)))
+                                       goto_bad;
+
+                               log_verbose("Changed LVID for %s to %s",
+                                           lvl2->lv->name, uuid);
+                       }
+               }
+       }
+
+       dm_list_iterate_items(lvl1, &vg_from->lvs) {
+               lvl1->lv->vg = vg_to;
+       }
+
+       while (!dm_list_empty(&vg_from->lvs)) {
+               struct dm_list *lvh = vg_from->lvs.n;
+
+               dm_list_move(&vg_to->lvs, lvh);
+       }
+
+       while (!dm_list_empty(&vg_from->fid->metadata_areas_in_use)) {
+               struct dm_list *mdah = vg_from->fid->metadata_areas_in_use.n;
+
+               dm_list_move(&vg_to->fid->metadata_areas_in_use, mdah);
+       }
+
+       while (!dm_list_empty(&vg_from->fid->metadata_areas_ignored)) {
+               struct dm_list *mdah = vg_from->fid->metadata_areas_ignored.n;
+
+               dm_list_move(&vg_to->fid->metadata_areas_ignored, mdah);
+       }
+
+       vg_to->extent_count += vg_from->extent_count;
+       vg_to->free_count += vg_from->free_count;
+
+       /* store it on disks */
+       log_verbose("Writing out updated volume group");
+       if (!vg_write(vg_to) || !vg_commit(vg_to))
+               goto_bad;
+
+       /* FIXME Remove /dev/vgfrom */
+
+       backup(vg_to);
+       log_print("Volume group \"%s\" successfully merged into \"%s\"",
+                 vg_from->name, vg_to->name);
+       r = ECMD_PROCESSED;
+bad:
+       if (lock_vg_from_first) {
+               unlock_and_free_vg(cmd, vg_to, vg_name_to);
+               unlock_and_free_vg(cmd, vg_from, vg_name_from);
+       } else {
+               unlock_and_free_vg(cmd, vg_from, vg_name_from);
+               unlock_and_free_vg(cmd, vg_to, vg_name_to);
+       }
+       return r;
+}
+
+int vgmerge(struct cmd_context *cmd, int argc, char **argv)
+{
+       char *vg_name_to, *vg_name_from;
+       int opt = 0;
+       int ret = 0, ret_max = 0;
+
+       if (argc < 2) {
+               log_error("Please enter 2 or more volume groups to merge");
+               return EINVALID_CMD_LINE;
+       }
+
+       vg_name_to = skip_dev_dir(cmd, argv[0], NULL);
+       argc--;
+       argv++;
+
+       for (; opt < argc; opt++) {
+               vg_name_from = skip_dev_dir(cmd, argv[opt], NULL);
+
+               ret = _vgmerge_single(cmd, vg_name_to, vg_name_from);
+               if (ret > ret_max)
+                       ret_max = ret;
+       }
+
+       return ret_max;
+}
diff --git a/tools/vgmknodes.c b/tools/vgmknodes.c
new file mode 100644 (file)
index 0000000..0fd273f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int _vgmknodes_single(struct cmd_context *cmd, struct logical_volume *lv,
+                            void *handle __attribute__((unused)))
+{
+       if (arg_count(cmd, refresh_ARG) && lv_is_visible(lv))
+               if (!lv_refresh(cmd, lv)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+       if (!lv_mknodes(cmd, lv)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int vgmknodes(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (!lv_mknodes(cmd, NULL)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return process_each_lv(cmd, argc, argv, LCK_VG_READ, NULL,
+                           &_vgmknodes_single);
+}
diff --git a/tools/vgreduce.c b/tools/vgreduce.c
new file mode 100644 (file)
index 0000000..4aa8f01
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+#include "lv_alloc.h"
+
+static int _remove_pv(struct volume_group *vg, struct pv_list *pvl, int silent)
+{
+       char uuid[64] __attribute__((aligned(8)));
+
+       if (vg->pv_count == 1) {
+               log_error("Volume Groups must always contain at least one PV");
+               return 0;
+       }
+
+       if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid)))
+               return_0;
+
+       log_verbose("Removing PV with UUID %s from VG %s", uuid, vg->name);
+
+       if (pvl->pv->pe_alloc_count) {
+               if (!silent)
+                       log_error("LVs still present on PV with UUID %s: "
+                                 "Can't remove from VG %s", uuid, vg->name);
+               return 0;
+       }
+
+       vg->free_count -= pvl->pv->pe_count;
+       vg->extent_count -= pvl->pv->pe_count;
+       del_pvl_from_vgs(vg, pvl);
+
+       return 1;
+}
+
+static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv,
+                     int *list_unsafe, struct dm_list *lvs_changed)
+{
+       struct lv_segment *snap_seg;
+       struct dm_list *snh, *snht;
+       struct logical_volume *cow;
+       struct lv_list *lvl;
+       struct lvinfo info;
+       int first = 1;
+
+       log_verbose("%s/%s has missing extents: removing (including "
+                   "dependencies)", lv->vg->name, lv->name);
+
+       /* FIXME Cope properly with stacked devices & snapshots. */
+
+       /* If snapshot device is missing, deactivate origin. */
+       if (lv_is_cow(lv) && (snap_seg = find_cow(lv))) {
+               log_verbose("Deactivating (if active) logical volume %s "
+                           "(origin of %s)", snap_seg->origin->name, lv->name);
+
+               if (!test_mode() && !deactivate_lv(cmd, snap_seg->origin)) {
+                       log_error("Failed to deactivate LV %s",
+                                 snap_seg->origin->name);
+                       return 0;
+               }
+
+               /* Use the origin LV */
+               lv = snap_seg->origin;
+       }
+
+       /* Remove snapshot dependencies */
+       dm_list_iterate_safe(snh, snht, &lv->snapshot_segs) {
+               snap_seg = dm_list_struct_base(snh, struct lv_segment,
+                                           origin_list);
+               cow = snap_seg->cow;
+
+               if (first && !test_mode() &&
+                   !deactivate_lv(cmd, snap_seg->origin)) {
+                       log_error("Failed to deactivate LV %s",
+                                 snap_seg->origin->name);
+                       return 0;
+               }
+
+               *list_unsafe = 1;       /* May remove caller's lvht! */
+               if (!vg_remove_snapshot(cow))
+                       return_0;
+               log_verbose("Removing LV %s from VG %s", cow->name,
+                           lv->vg->name);
+               if (!lv_remove(cow))
+                       return_0;
+
+               first = 0;
+       }
+
+       /*
+        * If LV is active, replace it with error segment
+        * and add to list of LVs to be removed later.
+        * Doesn't apply to snapshots/origins yet - they're already deactivated.
+        */
+       /*
+        * If the LV is a part of mirror segment,
+        * the mirrored LV also should be cleaned up.
+        * Clean-up is currently done by caller (_make_vg_consistent()).
+        */
+       if ((lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) ||
+           find_mirror_seg(first_seg(lv))) {
+               if (!replace_lv_with_error_segment(lv))
+                       return_0;
+
+               if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+                       log_error("lv_list alloc failed");
+                       return 0;
+               }
+               lvl->lv = lv;
+               dm_list_add(lvs_changed, &lvl->list);
+       } else {
+               /* Remove LV immediately. */
+               log_verbose("Removing LV %s from VG %s", lv->name, lv->vg->name);
+               if (!lv_remove(lv))
+                       return_0;
+       }
+
+       return 1;
+}
+
+static int _consolidate_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+       struct pv_list *pvl;
+       struct lv_list *lvl;
+       int r = 1;
+
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (lvl->lv->status & PARTIAL_LV) {
+                       log_warn("WARNING: Partial LV %s needs to be repaired "
+                                "or removed. ", lvl->lv->name);
+                       r = 0;
+               }
+
+       if (!r) {
+               cmd->handles_missing_pvs = 1;
+               log_warn("WARNING: There are still partial LVs in VG %s.", vg->name);
+               log_warn("To remove them unconditionally use: vgreduce --removemissing --force.");
+               log_warn("Proceeding to remove empty missing PVs.");
+       }
+
+       dm_list_iterate_items(pvl, &vg->pvs) {
+               if (pvl->pv->dev && !is_missing_pv(pvl->pv))
+                       continue;
+               if (r && !_remove_pv(vg, pvl, 0))
+                       return_0;
+       }
+
+       return r;
+}
+
+static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
+{
+       struct dm_list *pvh, *pvht;
+       struct dm_list *lvh, *lvht;
+       struct pv_list *pvl;
+       struct lv_list *lvl, *lvl2, *lvlt;
+       struct logical_volume *lv;
+       struct physical_volume *pv;
+       struct lv_segment *seg, *mirrored_seg;
+       unsigned s;
+       uint32_t mimages, remove_log;
+       int list_unsafe, only_mirror_images_found;
+       DM_LIST_INIT(lvs_changed);
+       only_mirror_images_found = 1;
+
+       /* Deactivate & remove necessary LVs */
+      restart_loop:
+       list_unsafe = 0;        /* Set if we delete a different list-member */
+
+       dm_list_iterate_safe(lvh, lvht, &vg->lvs) {
+               lv = dm_list_item(lvh, struct lv_list)->lv;
+
+               /* Are any segments of this LV on missing PVs? */
+               dm_list_iterate_items(seg, &lv->segments) {
+                       for (s = 0; s < seg->area_count; s++) {
+                               if (seg_type(seg, s) != AREA_PV)
+                                       continue;
+
+                               /* FIXME Also check for segs on deleted LVs (incl pvmove) */
+
+                               pv = seg_pv(seg, s);
+                               if (!pv || !pv_dev(pv) ||
+                                   is_missing_pv(pv)) {
+                                       if (arg_count(cmd, mirrorsonly_ARG) &&
+                                           !(lv->status & MIRROR_IMAGE)) {
+                                               log_error("Non-mirror-image LV %s found: can't remove.", lv->name);
+                                               only_mirror_images_found = 0;
+                                               continue;
+                                       }
+                                       if (!_remove_lv(cmd, lv, &list_unsafe, &lvs_changed))
+                                               return_0;
+                                       if (list_unsafe)
+                                               goto restart_loop;
+                               }
+                       }
+               }
+       }
+
+       if (!only_mirror_images_found) {
+               log_error("Aborting because --mirrorsonly was specified.");
+               return 0;
+       }
+
+       /*
+        * Remove missing PVs. FIXME: This duplicates _consolidate_vg above,
+        * but we cannot use that right now, since the LV removal code in this
+        * function leaves the VG in a "somewhat inconsistent" state and
+        * _consolidate_vg doesn't like that -- specifically, mirrors are fixed
+        * up *after* the PVs are removed. All this should be gradually
+        * superseded by lvconvert --repair.
+        */
+       dm_list_iterate_safe(pvh, pvht, &vg->pvs) {
+               pvl = dm_list_item(pvh, struct pv_list);
+               if (pvl->pv->dev && !is_missing_pv(pvl->pv))
+                       continue;
+               if (!_remove_pv(vg, pvl, 0))
+                       return_0;
+       }
+
+       /* FIXME Recovery.  For now people must clean up by hand. */
+
+       if (!dm_list_empty(&lvs_changed)) {
+               if (!vg_write(vg)) {
+                       log_error("Failed to write out a consistent VG for %s",
+                                 vg->name);
+                       return 0;
+               }
+
+               if (!test_mode()) {
+                       /* Suspend lvs_changed */
+                       if (!suspend_lvs(cmd, &lvs_changed)) {
+                               stack;
+                               vg_revert(vg);
+                               return 0;
+                       }
+               }
+
+               if (!vg_commit(vg)) {
+                       log_error("Failed to commit consistent VG for %s",
+                                 vg->name);
+                       vg_revert(vg);
+                       return 0;
+               }
+
+               if (!test_mode()) {
+                       if (!resume_lvs(cmd, &lvs_changed)) {
+                               log_error("Failed to resume LVs using error segments.");
+                               return 0;
+                       }
+               }
+
+  lvs_changed_altered:
+               /* Remove lost mirror images from mirrors */
+               dm_list_iterate_items(lvl, &vg->lvs) {
+  mirrored_seg_altered:
+                       mirrored_seg = first_seg(lvl->lv);
+                       if (!seg_is_mirrored(mirrored_seg))
+                               continue;
+
+                       mimages = mirrored_seg->area_count;
+                       remove_log = 0;
+
+                       for (s = 0; s < mirrored_seg->area_count; s++) {
+                               dm_list_iterate_items_safe(lvl2, lvlt, &lvs_changed) {
+                                       if (seg_type(mirrored_seg, s) != AREA_LV ||
+                                           lvl2->lv != seg_lv(mirrored_seg, s))
+                                               continue;
+                                       dm_list_del(&lvl2->list);
+                                       if (!shift_mirror_images(mirrored_seg, s))
+                                               return_0;
+                                       mimages--;      /* FIXME Assumes uniqueness */
+                               }
+                       }
+
+                       if (mirrored_seg->log_lv) {
+                               dm_list_iterate_items(seg, &mirrored_seg->log_lv->segments) {
+                                       /* FIXME: The second test shouldn't be required */
+                                       if ((seg->segtype ==
+                                            get_segtype_from_string(vg->cmd, "error"))) {
+                                               log_print("The log device for %s/%s has failed.",
+                                                         vg->name, mirrored_seg->lv->name);
+                                               remove_log = 1;
+                                               break;
+                                       }
+                                       if (!strcmp(seg->segtype->name, "error")) {
+                                               log_print("Log device for %s/%s has failed.",
+                                                         vg->name, mirrored_seg->lv->name);
+                                               remove_log = 1;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if ((mimages != mirrored_seg->area_count) || remove_log){
+                               if (!reconfigure_mirror_images(mirrored_seg, mimages,
+                                                              NULL, remove_log))
+                                       return_0;
+
+                               if (!vg_write(vg)) {
+                                       log_error("Failed to write out updated "
+                                                 "VG for %s", vg->name);
+                                       return 0;
+                               }
+               
+                               if (!vg_commit(vg)) {
+                                       log_error("Failed to commit updated VG "
+                                                 "for %s", vg->name);
+                                       vg_revert(vg);
+                                       return 0;
+                               }
+
+                               /* mirrored LV no longer has valid mimages.
+                                * So add it to lvs_changed for removal.
+                                * For this LV may be an area of other mirror,
+                                * restart the loop. */
+                               if (!mimages) {
+                                       if (!_remove_lv(cmd, lvl->lv,
+                                                &list_unsafe, &lvs_changed))
+                                               return_0;
+                                       goto lvs_changed_altered;
+                               }
+
+                               /* As a result of reconfigure_mirror_images(),
+                                * first_seg(lv) may now be different seg.
+                                * e.g. a temporary layer might be removed.
+                                * So check the mirrored_seg again. */
+                               goto mirrored_seg_altered;
+                       }
+               }
+
+               /* Deactivate error LVs */
+               if (!test_mode()) {
+                       dm_list_iterate_items_safe(lvl, lvlt, &lvs_changed) {
+                               log_verbose("Deactivating (if active) logical volume %s",
+                                           lvl->lv->name);
+
+                               if (!deactivate_lv(cmd, lvl->lv)) {
+                                       log_error("Failed to deactivate LV %s",
+                                                 lvl->lv->name);
+                                       /*
+                                        * We failed to deactivate.
+                                        * Probably because this was a mirror log.
+                                        * Don't try to lv_remove it.
+                                        * Continue work on others.
+                                        */
+                                       dm_list_del(&lvl->list);
+                               }
+                       }
+               }
+
+               /* Remove remaining LVs */
+               dm_list_iterate_items(lvl, &lvs_changed) {
+                       log_verbose("Removing LV %s from VG %s", lvl->lv->name,
+                                   lvl->lv->vg->name);
+                               /* Skip LVs already removed by mirror code */
+                               if (find_lv_in_vg(vg, lvl->lv->name) &&
+                                   !lv_remove(lvl->lv))
+                                       return_0;
+               }
+       }
+
+       return 1;
+}
+
+/* Or take pv_name instead? */
+static int _vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
+                           struct physical_volume *pv,
+                           void *handle __attribute__((unused)))
+{
+       struct pv_list *pvl;
+       struct volume_group *orphan_vg = NULL;
+       int r = ECMD_FAILED;
+       const char *name = pv_dev_name(pv);
+
+       if (pv_pe_alloc_count(pv)) {
+               log_error("Physical volume \"%s\" still in use", name);
+               return ECMD_FAILED;
+       }
+
+       if (vg->pv_count == 1) {
+               log_error("Can't remove final physical volume \"%s\" from "
+                         "volume group \"%s\"", name, vg->name);
+               return ECMD_FAILED;
+       }
+
+       if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+               log_error("Can't get lock for orphan PVs");
+               return ECMD_FAILED;
+       }
+
+       pvl = find_pv_in_vg(vg, name);
+
+       if (!archive(vg))
+               goto_bad;
+
+       log_verbose("Removing \"%s\" from volume group \"%s\"", name, vg->name);
+
+       if (pvl)
+               del_pvl_from_vgs(vg, pvl);
+
+       pv->vg_name = vg->fid->fmt->orphan_vg_name;
+       pv->status = ALLOCATABLE_PV;
+
+       if (!dev_get_size(pv_dev(pv), &pv->size)) {
+               log_error("%s: Couldn't get size.", pv_dev_name(pv));
+               goto bad;
+       }
+
+       vg->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+       vg->extent_count -= pv_pe_count(pv);
+
+       orphan_vg = vg_read_for_update(cmd, vg->fid->fmt->orphan_vg_name,
+                                      NULL, 0);
+
+       if (vg_read_error(orphan_vg))
+               goto bad;
+
+       if (!vg_split_mdas(cmd, vg, orphan_vg) || !vg->pv_count) {
+               log_error("Cannot remove final metadata area on \"%s\" from \"%s\"",
+                         name, vg->name);
+               goto bad;
+       }
+
+       if (!vg_write(vg) || !vg_commit(vg)) {
+               log_error("Removal of physical volume \"%s\" from "
+                         "\"%s\" failed", name, vg->name);
+               goto bad;
+       }
+
+       if (!pv_write(cmd, pv, NULL, INT64_C(-1))) {
+               log_error("Failed to clear metadata from physical "
+                         "volume \"%s\" "
+                         "after removal from \"%s\"", name, vg->name);
+               goto bad;
+       }
+
+       backup(vg);
+
+       log_print("Removed \"%s\" from volume group \"%s\"", name, vg->name);
+       r = ECMD_PROCESSED;
+bad:
+       unlock_and_free_vg(cmd, orphan_vg, VG_ORPHANS);
+       return r;
+}
+
+int vgreduce(struct cmd_context *cmd, int argc, char **argv)
+{
+       struct volume_group *vg;
+       char *vg_name;
+       int ret = ECMD_FAILED;
+       int fixed = 1;
+       int repairing = arg_count(cmd, removemissing_ARG);
+       int saved_ignore_suspended_devices = ignore_suspended_devices();
+
+       if (!argc && !repairing) {
+               log_error("Please give volume group name and "
+                         "physical volume paths");
+               return EINVALID_CMD_LINE;
+       }
+       
+       if (!argc && repairing) {
+               log_error("Please give volume group name");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, mirrorsonly_ARG) && !repairing) {
+               log_error("--mirrorsonly requires --removemissing");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (argc == 1 && !arg_count(cmd, all_ARG) && !repairing) {
+               log_error("Please enter physical volume paths or option -a");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (argc > 1 && arg_count(cmd, all_ARG)) {
+               log_error("Option -a and physical volume paths mutually "
+                         "exclusive");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (argc > 1 && repairing) {
+               log_error("Please only specify the volume group");
+               return EINVALID_CMD_LINE;
+       }
+
+       vg_name = skip_dev_dir(cmd, argv[0], NULL);
+       argv++;
+       argc--;
+
+       log_verbose("Finding volume group \"%s\"", vg_name);
+
+       if (repairing) {
+               init_ignore_suspended_devices(1);
+               cmd->handles_missing_pvs = 1;
+       }
+
+       vg = vg_read_for_update(cmd, vg_name, NULL, READ_ALLOW_EXPORTED);
+       if (vg_read_error(vg) == FAILED_ALLOCATION ||
+           vg_read_error(vg) == FAILED_NOTFOUND)
+               goto_out;
+
+       /* FIXME We want to allow read-only VGs to be changed here? */
+       if (vg_read_error(vg) && vg_read_error(vg) != FAILED_READ_ONLY
+           && !arg_count(cmd, removemissing_ARG))
+               goto_out;
+
+       if (repairing) {
+               if (!vg_read_error(vg) && !vg_missing_pv_count(vg)) {
+                       log_error("Volume group \"%s\" is already consistent",
+                                 vg_name);
+                       ret = ECMD_PROCESSED;
+                       goto out;
+               }
+
+               free_vg(vg);
+               log_verbose("Trying to open VG %s for recovery...", vg_name);
+
+               vg = vg_read_for_update(cmd, vg_name, NULL,
+                                       READ_ALLOW_INCONSISTENT
+                                       | READ_ALLOW_EXPORTED);
+
+               if (vg_read_error(vg) && vg_read_error(vg) != FAILED_READ_ONLY
+                   && vg_read_error(vg) != FAILED_INCONSISTENT)
+                       goto_out;
+
+               if (!archive(vg))
+                       goto_out;
+
+               if (arg_count(cmd, force_ARG)) {
+                       if (!_make_vg_consistent(cmd, vg))
+                               goto_out;
+               } else
+                       fixed = _consolidate_vg(cmd, vg);
+
+               if (!vg_write(vg) || !vg_commit(vg)) {
+                       log_error("Failed to write out a consistent VG for %s",
+                                 vg_name);
+                       goto out;
+               }
+               backup(vg);
+
+               if (fixed) {
+                       log_print("Wrote out consistent volume group %s",
+                                 vg_name);
+                       ret = ECMD_PROCESSED;
+               } else
+                       ret = ECMD_FAILED;
+
+       } else {
+               if (!vg_check_status(vg, EXPORTED_VG | LVM_WRITE | RESIZEABLE_VG))
+                       goto_out;
+
+               /* FIXME: Pass private struct through to all these functions */
+               /* and update in batch here? */
+               ret = process_each_pv(cmd, argc, argv, vg, READ_FOR_UPDATE, 0, NULL,
+                                     _vgreduce_single);
+
+       }
+out:
+       init_ignore_suspended_devices(saved_ignore_suspended_devices);
+       unlock_and_free_vg(cmd, vg, vg_name);
+
+       return ret;
+
+/******* FIXME
+       log_error ("no empty physical volumes found in volume group \"%s\"", vg_name);
+
+       log_verbose
+           ("volume group \"%s\" will be reduced by %d physical volume%s",
+            vg_name, np, np > 1 ? "s" : "");
+       log_verbose ("reducing volume group \"%s\" by physical volume \"%s\"",
+                    vg_name, pv_names[p]);
+
+       log_print
+           ("volume group \"%s\" %ssuccessfully reduced by physical volume%s:",
+            vg_name, error > 0 ? "NOT " : "", p > 1 ? "s" : "");
+               log_print("%s", pv_this[p]->pv_name);
+********/
+
+}
diff --git a/tools/vgremove.c b/tools/vgremove.c
new file mode 100644 (file)
index 0000000..67e3767
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int vgremove_single(struct cmd_context *cmd, const char *vg_name,
+                          struct volume_group *vg,
+                          void *handle __attribute__((unused)))
+{
+       unsigned lv_count, missing;
+       force_t force;
+
+       if (!vg_check_status(vg, EXPORTED_VG)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       lv_count = vg_visible_lvs(vg);
+
+       force = arg_count(cmd, force_ARG);
+       if (lv_count) {
+               if (force == PROMPT) {
+                       if ((missing = vg_missing_pv_count(vg)))
+                               log_warn("WARNING: %d physical volumes are currently missing "
+                                        "from the system.", missing);
+                       if (yes_no_prompt("Do you really want to remove volume "
+                                         "group \"%s\" containing %u "
+                                         "logical volumes? [y/n]: ",
+                                         vg_name, lv_count) == 'n') {
+                               log_error("Volume group \"%s\" not removed", vg_name);
+                               return ECMD_FAILED;
+                       }
+               }
+               if (!remove_lvs_in_vg(cmd, vg, force)) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+       }
+
+       if (!force && !vg_remove_check(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       vg_remove_pvs(vg);
+
+       if (!vg_remove(vg)) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
+int vgremove(struct cmd_context *cmd, int argc, char **argv)
+{
+       int ret;
+
+       if (!argc) {
+               log_error("Please enter one or more volume group paths");
+               return EINVALID_CMD_LINE;
+       }
+
+       cmd->handles_missing_pvs = 1;
+       ret = process_each_vg(cmd, argc, argv,
+                             READ_FOR_UPDATE,
+                             NULL, &vgremove_single);
+
+       return ret;
+}
diff --git a/tools/vgrename.c b/tools/vgrename.c
new file mode 100644 (file)
index 0000000..98be9a9
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static struct volume_group *_get_old_vg_for_rename(struct cmd_context *cmd,
+                                                  const char *vg_name_old,
+                                                  const char *vgid)
+{
+       struct volume_group *vg;
+
+       /* FIXME we used to print an error about EXPORTED, but proceeded
+          nevertheless. */
+       vg = vg_read_for_update(cmd, vg_name_old, vgid, READ_ALLOW_EXPORTED);
+       if (vg_read_error(vg)) {
+               free_vg(vg);
+               return_NULL;
+       }
+
+       return vg;
+}
+
+static int _lock_new_vg_for_rename(struct cmd_context *cmd,
+                                  const char *vg_name_new)
+{
+       int rc;
+
+       log_verbose("Checking for new volume group \"%s\"", vg_name_new);
+
+       rc = vg_lock_newname(cmd, vg_name_new);
+
+       if (rc == FAILED_LOCKING) {
+               log_error("Can't get lock for %s", vg_name_new);
+               return 0;
+       }
+
+       if (rc == FAILED_EXIST) {
+               log_error("New volume group \"%s\" already exists",
+                         vg_name_new);
+               return 0;
+       }
+       return 1;
+}
+
+static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path,
+                         const char *new_vg_path)
+{
+       char *dev_dir;
+       struct id id;
+       int match = 0;
+       int found_id = 0;
+       struct dm_list *vgids;
+       struct str_list *sl;
+       char *vg_name_new;
+       const char *vgid = NULL, *vg_name, *vg_name_old;
+       char old_path[NAME_LEN], new_path[NAME_LEN];
+       struct volume_group *vg = NULL;
+       int lock_vg_old_first = 1;
+
+       vg_name_old = skip_dev_dir(cmd, old_vg_path, NULL);
+       vg_name_new = skip_dev_dir(cmd, new_vg_path, NULL);
+
+       dev_dir = cmd->dev_dir;
+
+       if (!validate_vg_rename_params(cmd, vg_name_old, vg_name_new))
+               return_0;
+
+       log_verbose("Checking for existing volume group \"%s\"", vg_name_old);
+
+       /* Avoid duplicates */
+       if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) {
+               log_error("No complete volume groups found");
+               return 0;
+       }
+
+       dm_list_iterate_items(sl, vgids) {
+               vgid = sl->str;
+               if (!vgid || !(vg_name = vgname_from_vgid(NULL, vgid)))
+                       continue;
+               if (!strcmp(vg_name, vg_name_old)) {
+                       if (match) {
+                               log_error("Found more than one VG called %s. "
+                                         "Please supply VG uuid.", vg_name_old);
+                               return 0;
+                       }
+                       match = 1;
+               }
+       }
+
+       log_suppress(2);
+       found_id = id_read_format(&id, vg_name_old);
+       log_suppress(0);
+       if (found_id && (vg_name = vgname_from_vgid(cmd->mem, (char *)id.uuid))) {
+               vg_name_old = vg_name;
+               vgid = (char *)id.uuid;
+       } else
+               vgid = NULL;
+
+       if (strcmp(vg_name_new, vg_name_old) < 0)
+               lock_vg_old_first = 0;
+
+       if (lock_vg_old_first) {
+               vg = _get_old_vg_for_rename(cmd, vg_name_old, vgid);
+               if (!vg)
+                       return_0;
+
+               if (!_lock_new_vg_for_rename(cmd, vg_name_new)) {
+                       unlock_and_free_vg(cmd, vg, vg_name_old);
+                       return_0;
+               }
+       } else {
+               if (!_lock_new_vg_for_rename(cmd, vg_name_new))
+                       return_0;
+
+               vg = _get_old_vg_for_rename(cmd, vg_name_old, vgid);
+               if (!vg) {
+                       unlock_vg(cmd, vg_name_new);
+                       return_0;
+               }
+       }
+
+       if (!archive(vg))
+               goto error;
+
+       /* Remove references based on old name */
+       drop_cached_metadata(vg);
+
+       /* Change the volume group name */
+       vg_rename(cmd, vg, vg_name_new);
+
+       /* store it on disks */
+       log_verbose("Writing out updated volume group");
+       if (!vg_write(vg) || !vg_commit(vg)) {
+               goto error;
+       }
+
+       sprintf(old_path, "%s%s", dev_dir, vg_name_old);
+       sprintf(new_path, "%s%s", dev_dir, vg_name_new);
+
+       if (activation() && dir_exists(old_path)) {
+               log_verbose("Renaming \"%s\" to \"%s\"", old_path, new_path);
+
+               if (test_mode())
+                       log_verbose("Test mode: Skipping rename.");
+
+               else if (lvs_in_vg_activated(vg)) {
+                       if (!vg_refresh_visible(cmd, vg)) {
+                               log_error("Renaming \"%s\" to \"%s\" failed", 
+                                       old_path, new_path);
+                               goto error;
+                       }
+               }
+       }
+
+       backup(vg);
+       backup_remove(cmd, vg_name_old);
+
+       unlock_vg(cmd, vg_name_new);
+       unlock_and_free_vg(cmd, vg, vg_name_old);
+
+       log_print("Volume group \"%s\" successfully renamed to \"%s\"",
+                 vg_name_old, vg_name_new);
+
+       /* FIXME lvmcache corruption - vginfo duplicated instead of renamed */
+       persistent_filter_wipe(cmd->filter);
+       lvmcache_destroy(cmd, 1);
+
+       return 1;
+
+      error:
+       if (lock_vg_old_first) {
+               unlock_vg(cmd, vg_name_new);
+               unlock_and_free_vg(cmd, vg, vg_name_old);
+       } else {
+               unlock_and_free_vg(cmd, vg, vg_name_old);
+               unlock_vg(cmd, vg_name_new);
+       }
+       return 0;
+}
+
+int vgrename(struct cmd_context *cmd, int argc, char **argv)
+{
+       if (argc != 2) {
+               log_error("Old and new volume group names need specifying");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (!vg_rename_path(cmd, argv[0], argv[1])) {
+               stack;
+               return ECMD_FAILED;
+       }
+
+       return ECMD_PROCESSED;
+}
+
diff --git a/tools/vgscan.c b/tools/vgscan.c
new file mode 100644 (file)
index 0000000..4e12914
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+static int vgscan_single(struct cmd_context *cmd, const char *vg_name,
+                        struct volume_group *vg,
+                        void *handle __attribute__((unused)))
+{
+       log_print("Found %svolume group \"%s\" using metadata type %s",
+                 vg_is_exported(vg) ? "exported " : "", vg_name,
+                 vg->fid->fmt->name);
+
+       check_current_backup(vg);
+
+       return ECMD_PROCESSED;
+}
+
+int vgscan(struct cmd_context *cmd, int argc, char **argv)
+{
+       int maxret, ret;
+
+       if (argc) {
+               log_error("Too many parameters on command line");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE)) {
+               log_error("Unable to obtain global lock.");
+               return ECMD_FAILED;
+       }
+
+       persistent_filter_wipe(cmd->filter);
+       lvmcache_destroy(cmd, 1);
+
+       log_print("Reading all physical volumes.  This may take a while...");
+
+       maxret = process_each_vg(cmd, argc, argv, 0, NULL,
+                                &vgscan_single);
+
+       if (arg_count(cmd, mknodes_ARG)) {
+               ret = vgmknodes(cmd, argc, argv);
+               if (ret > maxret)
+                       maxret = ret;
+       }
+
+       unlock_vg(cmd, VG_GLOBAL);
+       return maxret;
+}
diff --git a/tools/vgsplit.c b/tools/vgsplit.c
new file mode 100644 (file)
index 0000000..bd2f0ab
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * 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
+ */
+
+#include "tools.h"
+
+/* FIXME Why not (lv->vg == vg) ? */
+static int _lv_is_in_vg(struct volume_group *vg, struct logical_volume *lv)
+{
+       struct lv_list *lvl;
+
+       dm_list_iterate_items(lvl, &vg->lvs)
+               if (lv == lvl->lv)
+                        return 1;
+
+       return 0;
+}
+
+static int _move_one_lv(struct volume_group *vg_from,
+                        struct volume_group *vg_to,
+                        struct dm_list *lvh)
+{
+       struct logical_volume *lv = dm_list_item(lvh, struct lv_list)->lv;
+
+       dm_list_move(&vg_to->lvs, lvh);
+       lv->vg = vg_to;
+
+       if (lv_is_active(lv)) {
+               log_error("Logical volume \"%s\" must be inactive", lv->name);
+               return 0;
+       }
+
+       return 1;
+}
+
+static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
+{
+       struct dm_list *lvh, *lvht;
+       struct logical_volume *lv;
+       struct lv_segment *seg;
+       struct physical_volume *pv;
+       struct volume_group *vg_with;
+       unsigned s;
+
+       dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
+               lv = dm_list_item(lvh, struct lv_list)->lv;
+
+               if ((lv->status & SNAPSHOT))
+                       continue;
+
+               if ((lv->status & MIRRORED))
+                       continue;
+
+               /* Ensure all the PVs used by this LV remain in the same */
+               /* VG as each other */
+               vg_with = NULL;
+               dm_list_iterate_items(seg, &lv->segments) {
+                       for (s = 0; s < seg->area_count; s++) {
+                               /* FIXME Check AREA_LV too */
+                               if (seg_type(seg, s) != AREA_PV)
+                                       continue;
+
+                               pv = seg_pv(seg, s);
+                               if (vg_with) {
+                                       if (!pv_is_in_vg(vg_with, pv)) {
+                                               log_error("Can't split Logical "
+                                                         "Volume %s between "
+                                                         "two Volume Groups",
+                                                         lv->name);
+                                               return 0;
+                                       }
+                                       continue;
+                               }
+
+                               if (pv_is_in_vg(vg_from, pv)) {
+                                       vg_with = vg_from;
+                                       continue;
+                               }
+                               if (pv_is_in_vg(vg_to, pv)) {
+                                       vg_with = vg_to;
+                                       continue;
+                               }
+                               log_error("Physical Volume %s not found",
+                                         pv_dev_name(pv));
+                               return 0;
+                       }
+
+               }
+
+               if (vg_with == vg_from)
+                       continue;
+
+               /* Move this LV */
+               if (!_move_one_lv(vg_from, vg_to, lvh))
+                       return_0;
+       }
+
+       /* FIXME Ensure no LVs contain segs pointing at LVs in the other VG */
+
+       return 1;
+}
+
+/*
+ * Move the hidden / internal "snapshotN" LVs.from 'vg_from' to 'vg_to'.
+ */
+static int _move_snapshots(struct volume_group *vg_from,
+                          struct volume_group *vg_to)
+{
+       struct dm_list *lvh, *lvht;
+       struct logical_volume *lv;
+       struct lv_segment *seg;
+       int cow_from = 0;
+       int origin_from = 0;
+
+       dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
+               lv = dm_list_item(lvh, struct lv_list)->lv;
+
+               if (!(lv->status & SNAPSHOT))
+                       continue;
+
+               dm_list_iterate_items(seg, &lv->segments) {
+                       cow_from = _lv_is_in_vg(vg_from, seg->cow);
+                       origin_from = _lv_is_in_vg(vg_from, seg->origin);
+
+                       if (cow_from && origin_from)
+                               continue;
+                       if ((!cow_from && origin_from) ||
+                            (cow_from && !origin_from)) {
+                               log_error("Can't split snapshot %s between"
+                                         " two Volume Groups", seg->cow->name);
+                               return 0;
+                       }
+
+                       /*
+                        * At this point, the cow and origin should already be
+                        * in vg_to.
+                        */
+                       if (_lv_is_in_vg(vg_to, seg->cow) &&
+                           _lv_is_in_vg(vg_to, seg->origin)) {
+                               if (!_move_one_lv(vg_from, vg_to, lvh))
+                                       return_0;
+                       }
+               }
+
+       }
+
+       return 1;
+}
+
+static int _move_mirrors(struct volume_group *vg_from,
+                        struct volume_group *vg_to)
+{
+       struct dm_list *lvh, *lvht;
+       struct logical_volume *lv;
+       struct lv_segment *seg;
+       unsigned s, seg_in, log_in;
+
+       dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
+               lv = dm_list_item(lvh, struct lv_list)->lv;
+
+               if (!(lv->status & MIRRORED))
+                       continue;
+
+               seg = first_seg(lv);
+
+               seg_in = 0;
+               for (s = 0; s < seg->area_count; s++)
+                       if (_lv_is_in_vg(vg_to, seg_lv(seg, s)))
+                           seg_in++;
+
+               log_in = (!seg->log_lv || _lv_is_in_vg(vg_to, seg->log_lv));
+
+               if ((seg_in && seg_in < seg->area_count) ||
+                   (seg_in && seg->log_lv && !log_in) ||
+                   (!seg_in && seg->log_lv && log_in)) {
+                       log_error("Can't split mirror %s between "
+                                 "two Volume Groups", lv->name);
+                       return 0;
+               }
+
+               if (seg_in == seg->area_count && log_in) {
+                       if (!_move_one_lv(vg_from, vg_to, lvh))
+                               return_0;
+               }
+       }
+
+       return 1;
+}
+
+/*
+ * Create or open the destination of the vgsplit operation.
+ * Returns
+ * - non-NULL: VG handle w/VG lock held
+ * - NULL: no VG lock held
+ */
+static struct volume_group *_vgsplit_to(struct cmd_context *cmd,
+                                       const char *vg_name_to,
+                                       int *existing_vg)
+{
+       struct volume_group *vg_to = NULL;
+
+       log_verbose("Checking for new volume group \"%s\"", vg_name_to);
+       /*
+        * First try to create a new VG.  If we cannot create it,
+        * and we get FAILED_EXIST (we will not be holding a lock),
+        * a VG must already exist with this name.  We then try to
+        * read the existing VG - the vgsplit will be into an existing VG.
+        *
+        * Otherwise, if the lock was successful, it must be the case that
+        * we obtained a WRITE lock and could not find the vgname in the
+        * system.  Thus, the split will be into a new VG.
+        */
+       vg_to = vg_create(cmd, vg_name_to);
+       if (vg_read_error(vg_to) == FAILED_LOCKING) {
+               log_error("Can't get lock for %s", vg_name_to);
+               free_vg(vg_to);
+               return NULL;
+       }
+       if (vg_read_error(vg_to) == FAILED_EXIST) {
+               *existing_vg = 1;
+               free_vg(vg_to);
+               vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0);
+
+               if (vg_read_error(vg_to)) {
+                       free_vg(vg_to);
+                       stack;
+                       return NULL;
+               }
+
+       } else if (vg_read_error(vg_to) == SUCCESS) {
+               *existing_vg = 0;
+       }
+       return vg_to;
+}
+
+/*
+ * Open the source of the vgsplit operation.
+ * Returns
+ * - non-NULL: VG handle w/VG lock held
+ * - NULL: no VG lock held
+ */
+static struct volume_group *_vgsplit_from(struct cmd_context *cmd,
+                                         const char *vg_name_from)
+{
+       struct volume_group *vg_from;
+
+       log_verbose("Checking for volume group \"%s\"", vg_name_from);
+
+       vg_from = vg_read_for_update(cmd, vg_name_from, NULL, 0);
+       if (vg_read_error(vg_from)) {
+               free_vg(vg_from);
+               return NULL;
+       }
+       return vg_from;
+}
+
+/*
+ * Has the user given an option related to a new vg as the split destination?
+ */
+static int new_vg_option_specified(struct cmd_context *cmd)
+{
+       return(arg_count(cmd, clustered_ARG) ||
+              arg_count(cmd, alloc_ARG) ||
+              arg_count(cmd, maxphysicalvolumes_ARG) ||
+              arg_count(cmd, maxlogicalvolumes_ARG) ||
+              arg_count(cmd, vgmetadatacopies_ARG));
+}
+
+int vgsplit(struct cmd_context *cmd, int argc, char **argv)
+{
+       struct vgcreate_params vp_new;
+       struct vgcreate_params vp_def;
+       char *vg_name_from, *vg_name_to;
+       struct volume_group *vg_to = NULL, *vg_from = NULL;
+       int opt;
+       int existing_vg = 0;
+       int r = ECMD_FAILED;
+       const char *lv_name;
+       int lock_vg_from_first = 1;
+
+       if ((arg_count(cmd, name_ARG) + argc) < 3) {
+               log_error("Existing VG, new VG and either physical volumes "
+                         "or logical volume required.");
+               return EINVALID_CMD_LINE;
+       }
+
+       if (arg_count(cmd, name_ARG) && (argc > 2)) {
+               log_error("A logical volume name cannot be given with "
+                         "physical volumes.");
+               return ECMD_FAILED;
+       }
+
+       if (arg_count(cmd, name_ARG))
+               lv_name = arg_value(cmd, name_ARG);
+       else
+               lv_name = NULL;
+
+       vg_name_from = skip_dev_dir(cmd, argv[0], NULL);
+       vg_name_to = skip_dev_dir(cmd, argv[1], NULL);
+       argc -= 2;
+       argv += 2;
+
+       if (!strcmp(vg_name_to, vg_name_from)) {
+               log_error("Duplicate volume group name \"%s\"", vg_name_from);
+               return ECMD_FAILED;
+       }
+
+       if (strcmp(vg_name_to, vg_name_from) < 0)
+               lock_vg_from_first = 0;
+
+       if (lock_vg_from_first) {
+               vg_from = _vgsplit_from(cmd, vg_name_from);
+               if (!vg_from) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               /*
+                * Set metadata format of original VG.
+                * NOTE: We must set the format before calling vg_create()
+                * since vg_create() calls the per-format constructor.
+                */
+               cmd->fmt = vg_from->fid->fmt;
+
+               vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg);
+               if (!vg_to) {
+                       unlock_and_free_vg(cmd, vg_from, vg_name_from);
+                       stack;
+                       return ECMD_FAILED;
+               }
+       } else {
+               vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg);
+               if (!vg_to) {
+                       stack;
+                       return ECMD_FAILED;
+               }
+               vg_from = _vgsplit_from(cmd, vg_name_from);
+               if (!vg_from) {
+                       unlock_and_free_vg(cmd, vg_to, vg_name_to);
+                       stack;
+                       return ECMD_FAILED;
+               }
+
+               if (cmd->fmt != vg_from->fid->fmt) {
+                       /* In this case we don't know the vg_from->fid->fmt */
+                       log_error("Unable to set new VG metadata type based on "
+                                 "source VG format - use -M option.");
+                       goto bad;
+               }
+       }
+
+       if (existing_vg) {
+               if (new_vg_option_specified(cmd)) {
+                       log_error("Volume group \"%s\" exists, but new VG "
+                                   "option specified", vg_name_to);
+                       goto bad;
+               }
+               if (!vgs_are_compatible(cmd, vg_from,vg_to))
+                       goto_bad;
+       } else {
+               vgcreate_params_set_defaults(&vp_def, vg_from);
+               vp_def.vg_name = vg_name_to;
+               if (vgcreate_params_set_from_args(cmd, &vp_new, &vp_def)) {
+                       r = EINVALID_CMD_LINE;
+                       goto_bad;
+               }
+
+               if (vgcreate_params_validate(cmd, &vp_new)) {
+                       r = EINVALID_CMD_LINE;
+                       goto_bad;
+               }
+
+               if (!vg_set_extent_size(vg_to, vp_new.extent_size) ||
+                   !vg_set_max_lv(vg_to, vp_new.max_lv) ||
+                   !vg_set_max_pv(vg_to, vp_new.max_pv) ||
+                   !vg_set_alloc_policy(vg_to, vp_new.alloc) ||
+                   !vg_set_clustered(vg_to, vp_new.clustered) ||
+                   !vg_set_mda_copies(vg_to, vp_new.vgmetadatacopies))
+                       goto_bad;
+       }
+
+       /* Archive vg_from before changing it */
+       if (!archive(vg_from))
+               goto_bad;
+
+       /* Move PVs across to new structure */
+       for (opt = 0; opt < argc; opt++) {
+               unescape_colons_and_at_signs(argv[opt], NULL, NULL);
+               if (!move_pv(vg_from, vg_to, argv[opt]))
+                       goto_bad;
+       }
+
+       /* If an LV given on the cmdline, move used_by PVs */
+       if (lv_name && !move_pvs_used_by_lv(vg_from, vg_to, lv_name))
+               goto_bad;
+
+       /* Move required LVs across, checking consistency */
+       if (!(_move_lvs(vg_from, vg_to)))
+               goto_bad;
+
+       /* FIXME Separate the 'move' from the 'validation' to fix dev stacks */
+       /* Move required mirrors across */
+       if (!(_move_mirrors(vg_from, vg_to)))
+               goto_bad;
+
+       /* Move required snapshots across */
+       if (!(_move_snapshots(vg_from, vg_to)))
+               goto_bad;
+
+       /* Split metadata areas and check if both vgs have at least one area */
+       if (!(vg_split_mdas(cmd, vg_from, vg_to)) && vg_from->pv_count) {
+               log_error("Cannot split: Nowhere to store metadata for new Volume Group");
+               goto bad;
+       }
+
+       /* Set proper name for all PVs in new VG */
+       if (!vg_rename(cmd, vg_to, vg_name_to))
+               goto_bad;
+
+       /* store it on disks */
+       log_verbose("Writing out updated volume groups");
+
+       /*
+        * First, write out the new VG as EXPORTED.  We do this first in case
+        * there is a crash - we will still have the new VG information, in an
+        * exported state.  Recovery after this point would be removal of the
+        * new VG and redoing the vgsplit.
+        * FIXME: recover automatically or instruct the user?
+        */
+       vg_to->status |= EXPORTED_VG;
+
+       if (!archive(vg_to))
+               goto_bad;
+
+       if (!vg_write(vg_to) || !vg_commit(vg_to))
+               goto_bad;
+
+       backup(vg_to);
+
+       /*
+        * Next, write out the updated old VG.  If we crash after this point,
+        * recovery is a vgimport on the new VG.
+        * FIXME: recover automatically or instruct the user?
+        */
+       if (vg_from->pv_count) {
+               if (!vg_write(vg_from) || !vg_commit(vg_from))
+                       goto_bad;
+
+               backup(vg_from);
+       }
+
+       /*
+        * Finally, remove the EXPORTED flag from the new VG and write it out.
+        */
+       if (!test_mode()) {
+               free_vg(vg_to);
+               vg_to = vg_read_for_update(cmd, vg_name_to, NULL,
+                                          READ_ALLOW_EXPORTED);
+               if (vg_read_error(vg_to)) {
+                       log_error("Volume group \"%s\" became inconsistent: "
+                                 "please fix manually", vg_name_to);
+                       goto bad;
+               }
+       }
+
+       vg_to->status &= ~EXPORTED_VG;
+
+       if (!vg_write(vg_to) || !vg_commit(vg_to))
+               goto_bad;
+
+       backup(vg_to);
+
+       log_print("%s volume group \"%s\" successfully split from \"%s\"",
+                 existing_vg ? "Existing" : "New",
+                 vg_to->name, vg_from->name);
+
+       r = ECMD_PROCESSED;
+
+bad:
+       if (lock_vg_from_first) {
+               unlock_and_free_vg(cmd, vg_to, vg_name_to);
+               unlock_and_free_vg(cmd, vg_from, vg_name_from);
+       } else {
+               unlock_and_free_vg(cmd, vg_from, vg_name_from);
+               unlock_and_free_vg(cmd, vg_to, vg_name_to);
+       }
+       return r;
+}
diff --git a/udev/10-dm.rules.in b/udev/10-dm.rules.in
new file mode 100644 (file)
index 0000000..4d50ed3
--- /dev/null
@@ -0,0 +1,130 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules create a DM control node in /dev/(DM_DIR) directory.
+# The rules also create nodes named dm-x (x is a number) in /dev
+# directory and symlinks to these nodes with names given by
+# the actual DM names. Some udev environment variables are set
+# for use in later rules:
+#   DM_NAME - actual DM device's name
+#   DM_UUID - UUID set for DM device (blank if not specified)
+#   DM_SUSPENDED - suspended state of DM device (0 or 1)
+#   DM_UDEV_RULES_VSN - DM udev rules version
+
+KERNEL=="device-mapper", NAME="(DM_DIR)/control"
+
+SUBSYSTEM!="block", GOTO="dm_end"
+KERNEL!="dm-[0-9]*", GOTO="dm_end"
+
+# Set proper sbin path, /sbin has higher priority than /usr/sbin.
+ENV{DM_SBIN_PATH}="/sbin"
+TEST!="$env{DM_SBIN_PATH}/dmsetup", ENV{DM_SBIN_PATH}="/usr/sbin"
+TEST!="$env{DM_SBIN_PATH}/dmsetup", GOTO="dm_end"
+
+# Decode udev control flags and set environment variables appropriately.
+# These flags are encoded in DM_COOKIE variable that was introduced in
+# kernel version 2.6.31. Therefore, we can use this feature with
+# kernels >= 2.6.31 only.
+ENV{DM_COOKIE}=="?*", IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup udevflags $env{DM_COOKIE}"
+
+# Device created, major and minor number assigned - "add" event generated.
+# Table loaded - no event generated.
+# Device resumed (or renamed) - "change" event generated.
+# Device removed - "remove" event generated.
+#
+# The dm-X nodes are always created, even on "add" event, we can't suppress
+# that (the node is created even earlier with devtmpfs). All the symlinks
+# (e.g. /dev/mapper) are created in right time after a device has its table
+# loaded and is properly resumed. For this reason, direct use of dm-X nodes
+# is not recommended.
+ACTION!="add|change", GOTO="dm_end"
+
+# Rule out easy-to-detect inappropriate events first.
+ENV{DISK_RO}=="1", GOTO="dm_disable"
+
+# There is no cookie set nor any flags encoded in events not originating
+# in libdevmapper so we need to detect this and try to behave correctly.
+# For such spurious events, regenerate all flags from current udev database content
+# (this information would normally be inaccessible for spurious ADD and CHANGE events).
+ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}=="1", GOTO="dm_flags_done"
+IMPORT{db}="DM_UDEV_DISABLE_DM_RULES_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_DISK_RULES_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_OTHER_RULES_FLAG"
+IMPORT{db}="DM_UDEV_LOW_PRIORITY_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_LIBRARY_FALLBACK_FLAG"
+IMPORT{db}="DM_UDEV_PRIMARY_SOURCE_FLAG"
+IMPORT{db}="DM_UDEV_FLAG7"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG0"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG2"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG3"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG4"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG5"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG6"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG7"
+IMPORT{db}="DM_UDEV_RULES_VSN"
+LABEL="dm_flags_done"
+
+# Normally, we operate on "change" events. But when coldplugging, there's an
+# "add" event present. We have to recognize this and do our actions in this
+# particular situation, too. Also, we don't want the nodes to be created
+# prematurely on "add" events while not coldplugging. We check
+# DM_UDEV_PRIMARY_SOURCE_FLAG to see if the device was activated correctly
+# before and if not, we ignore the "add" event totally. This way we can support
+# udev triggers generating "add" events (e.g. "udevadm trigger --action=add" or
+# "echo add > /sys/block/<dm_device>/uevent"). The trigger with "add" event is
+# also used at boot to reevaluate udev rules for all existing devices activated
+# before (e.g. in initrd). If udev is used in initrd, we require the udev init
+# script to not remove the existing udev database so we can reuse the information
+# stored at the time of device activation in the initrd.
+ACTION=="add", ENV{DM_UDEV_RULES_VSN}!="1", ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", GOTO="dm_disable"
+
+# "dm" sysfs subdirectory is available in newer versions of DM
+# only (kernels >= 2.6.29). We have to check for its existence
+# and use dmsetup tool instead to get the DM name, uuid and 
+# suspended state if the "dm" subdirectory is not present.
+# The "suspended" item was added even later (kernels >= 2.6.31),
+# so we also have to call dmsetup if the kernel version used
+# is in between these releases.
+TEST=="dm", ENV{DM_NAME}="$attr{dm/name}", ENV{DM_UUID}="$attr{dm/uuid}", ENV{DM_SUSPENDED}="$attr{dm/suspended}"
+TEST!="dm", IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup info -j %M -m %m -c --nameprefixes --noheadings --rows -o name,uuid,suspended"
+ENV{DM_SUSPENDED}!="?*", IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup info -j %M -m %m -c --nameprefixes --noheadings --rows -o suspended"
+
+# dmsetup tool provides suspended state information in textual
+# form with values "Suspended"/"Active". We translate it to
+# 0/1 respectively to be consistent with sysfs values.
+ENV{DM_SUSPENDED}=="Active", ENV{DM_SUSPENDED}="0"
+ENV{DM_SUSPENDED}=="Suspended", ENV{DM_SUSPENDED}="1"
+
+# This variable provides a reliable way to check that device-mapper
+# rules were installed. It means that all needed variables are set
+# by these rules directly so there's no need to acquire them again
+# later. Other rules can alternate the functionality based on this
+# fact (e.g. fallback to rules that behave correctly even without
+# these rules installed). It also provides versioning for any
+# possible future changes.
+# VSN 1 - original rules
+# VSN 2 - add support for synthesized events
+ENV{DM_UDEV_RULES_VSN}="2"
+
+ENV{DM_UDEV_DISABLE_DM_RULES_FLAG}!="1", ENV{DM_NAME}=="?*", SYMLINK+="(DM_DIR)/$env{DM_NAME}"
+
+# We have to ignore further rule application for inappropriate events
+# and devices. But still send the notification if cookie exists.
+ENV{DM_UUID}=="mpath-?*", ENV{DM_ACTION}=="PATH_FAILED", GOTO="dm_disable"
+ENV{DM_UUID}=="CRYPT-TEMP-?*", GOTO="dm_disable"
+ENV{DM_UUID}!="?*", ENV{DM_NAME}=="temporary-cryptsetup-?*", GOTO="dm_disable"
+
+GOTO="dm_end"
+
+LABEL="dm_disable"
+ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+OPTIONS:="nowatch"
+
+LABEL="dm_end"
diff --git a/udev/11-dm-lvm.rules b/udev/11-dm-lvm.rules
new file mode 100644 (file)
index 0000000..8244464
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for LVM.
+#
+# These rules create symlinks for LVM logical volumes in
+# /dev/VG directory (VG is an actual VG name). Some udev
+# environment variables are set (they can be used in later
+# rules as well):
+#   DM_LV_NAME - logical volume name
+#   DM_VG_NAME - volume group name
+#   DM_LV_LAYER - logical volume layer (blank if not set)
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="lvm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="lvm_end"
+ENV{DM_UUID}!="LVM-?*", GOTO="lvm_end"
+
+# Use DM name and split it up into its VG/LV/layer constituents.
+IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup splitname --nameprefixes --noheadings --rows $env{DM_NAME}"
+
+ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}=="1", GOTO="lvm_end"
+
+# Do not create symlinks for inappropriate subdevices.
+ENV{DM_LV_NAME}=="pvmove?*|?*_vorigin", GOTO="lvm_disable"
+ENV{DM_LV_LAYER}=="?*", GOTO="lvm_disable"
+
+# Create symlinks for top-level devices only.
+ENV{DM_VG_NAME}=="?*", ENV{DM_LV_NAME}=="?*", SYMLINK+="$env{DM_VG_NAME}/$env{DM_LV_NAME}", GOTO="lvm_end"
+
+LABEL="lvm_disable"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+OPTIONS:="nowatch"
+
+LABEL="lvm_end"
diff --git a/udev/12-dm-permissions.rules b/udev/12-dm-permissions.rules
new file mode 100644 (file)
index 0000000..a9d4c32
--- /dev/null
@@ -0,0 +1,81 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules set permissions for DM devices.
+#
+# This file is considered to be a template where users can put their
+# own entries and then put a copy of it manually to a usual place with
+# user-edited udev rules (usually /etc/udev/rules.d).
+#
+# There are some environment variables set that can be used:
+#   DM_UDEV_RULES_VSN - DM udev rules version
+#   DM_NAME - actual DM device's name
+#   DM_UUID - UUID set for DM device (blank if not specified)
+#   DM_SUSPENDED - suspended state of DM device (0 or 1)
+#   DM_LV_NAME - logical volume name (not set if LVM device not present)
+#   DM_VG_NAME - volume group name (not set if LVM device not present)
+#   DM_LV_LAYER - logical volume layer (not set if LVM device not present)
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="dm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="dm_end"
+
+# A few demonstrational examples...
+
+
+# PLAIN DM DEVICES
+#
+# Set permissions for a DM device named 'my_device' exactly
+# ENV{DM_NAME}=="my_device", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all DM devices having 'MY_UUID-' UUID prefix
+# ENV{DM_UUID}=="MY_UUID-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# LVM DEVICES
+#
+# Set permissions for all LVM devices
+# ENV{DM_UUID}=="LVM-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all devices that belong to one LVM VG
+# ENV{DM_VG_NAME}=="VolGroup00", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for an LVM device with VG named VolGroup00 and LV named LogVol00 exactly
+# ENV{DM_VG_NAME}=="VolGroup00", ENV{DM_LV_NAME}=="LogVol00", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all LVM devices that does not belong to a VG named VolGroup00
+# ENV{DM_VG_NAME}!="VolGroup00", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# ENCRYPTED DEVICES (using cryptsetup >= 1.1)
+#
+# Set permissions for all encrypted devices created by cryptsetup (plain devices)
+# ENV{DM_UUID}=="CRYPT-PLAIN-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all encrypted devices created by cryptsetup (LUKS extension)
+# ENV{DM_UUID}=="CRYPT-LUKS1-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for an encrypted device created by cryptsetup and having an exact luks UUID
+# ENV{DM_UUID}=="CRYPT-LUKS1-22fce5c8313c43c68d84b50a3b0fee78-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# MULTIPATH DEVICES
+#
+# Set permissions for all multipath devices
+# ENV{DM_UUID}=="mpath-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for first two partitions created on a multipath device (and detected by kpartx)
+# ENV{DM_UUID}=="part[1-2]-mpath-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# ...you can use any combination of the comparisons with the environment variables
+# listed at the beginning of this file (udev provides simple pattern matching by
+# using *, ? and [] that you can use, see 'man udev' for more information).
+
+# Set default permissions for all DM devices if not set before.
+# OWNER:="root", GROUP:="root", MODE:="660"
+
+LABEL="dm_end"
diff --git a/udev/13-dm-disk.rules b/udev/13-dm-disk.rules
new file mode 100644 (file)
index 0000000..271ca22
--- /dev/null
@@ -0,0 +1,27 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules create symlinks in /dev/disk directory.
+# Symlinks that depend on probing filesystem type,
+# label and uuid are created only if the device is not
+# suspended.
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="dm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="dm_end"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}=="1", GOTO="dm_end"
+
+SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}"
+ENV{DM_UUID}=="?*", SYMLINK+="disk/by-id/dm-uuid-$env{DM_UUID}"
+
+ENV{DM_SUSPENDED}=="1", GOTO="dm_end"
+
+IMPORT{program}="$env{DM_SBIN_PATH}/blkid -o udev -p $tempnode"
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}=="1", OPTIONS="link_priority=-100"
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
+ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+
+LABEL="dm_end"
diff --git a/udev/95-dm-notify.rules b/udev/95-dm-notify.rules
new file mode 100644 (file)
index 0000000..72cc609
--- /dev/null
@@ -0,0 +1,12 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules are responsible for sending a notification to a process
+# waiting for completion of udev rules. The process is identified by
+# a cookie value sent within "change" and "remove" events (the cookie
+# value is set before by that process for every action requested).
+
+ENV{DM_COOKIE}=="?*", RUN+="$env{DM_SBIN_PATH}/dmsetup udevcomplete $env{DM_COOKIE}"
diff --git a/udev/Makefile.in b/udev/Makefile.in
new file mode 100644 (file)
index 0000000..11635bb
--- /dev/null
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+DM_RULES=10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
+LVM_RULES=11-dm-lvm.rules
+DM_DIR=$(shell grep "\#define DM_DIR" $(top_srcdir)/libdm/misc/dm-ioctl.h | awk '{print $$3}')
+
+CLEAN_TARGETS=10-dm.rules
+
+include $(top_builddir)/make.tmpl
+
+vpath %.rules $(srcdir)
+
+%.rules: %.rules.in
+       $(SED) -e "s/(DM_DIR)/$(DM_DIR)/" $< >$@
+
+%_install: %.rules
+       $(INSTALL_DATA) -D $< $(udevdir)/$(<F)
+
+install_device-mapper: $(DM_RULES:.rules=_install)
+install_lvm2: $(LVM_RULES:.rules=_install)
+
+install: install_lvm2 install_device-mapper
diff --git a/unit-tests/datastruct/Makefile.in b/unit-tests/datastruct/Makefile.in
new file mode 100644 (file)
index 0000000..e99198d
--- /dev/null
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES=\
+       bitset_t.c
+
+TARGETS=\
+       bitset_t
+
+include $(top_builddir)/make.tmpl
+
+INCLUDES += -I$(top_srcdir)/libdm
+DM_DEPS = $(top_builddir)/libdm/libdevmapper.so
+DM_LIBS = -ldevmapper $(LIBS)
+
+bitset_t: bitset_t.o $(DM_DEPS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ bitset_t.o $(DM_LIBS)
diff --git a/unit-tests/datastruct/TESTS b/unit-tests/datastruct/TESTS
new file mode 100644 (file)
index 0000000..ba88fb7
--- /dev/null
@@ -0,0 +1 @@
+bitset iteration:$TEST_TOOL ./bitset_t
\ No newline at end of file
diff --git a/unit-tests/datastruct/bitset_t.c b/unit-tests/datastruct/bitset_t.c
new file mode 100644 (file)
index 0000000..83b297b
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "libdevmapper.h"
+
+#include <assert.h>
+
+enum {
+        NR_BITS = 137
+};
+
+static void test_get_next(struct dm_pool *mem)
+{
+        int i, j, last, first;
+        dm_bitset_t bs = dm_bitset_create(mem, NR_BITS);
+
+        for (i = 0; i < NR_BITS; i++)
+                assert(!dm_bit(bs, i));
+
+        for (i = 0, j = 1; i < NR_BITS; i += j, j++)
+                dm_bit_set(bs, i);
+
+        first = 1;
+        for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
+                if (first) {
+                        last = dm_bit_get_first(bs);
+                        first = 0;
+                } else
+                        last = dm_bit_get_next(bs, last);
+
+                assert(last == i);
+        }
+
+        assert(dm_bit_get_next(bs, last) == -1);
+}
+
+static void bit_flip(dm_bitset_t bs, int bit)
+{
+        int old = dm_bit(bs, bit);
+        if (old)
+                dm_bit_clear(bs, bit);
+        else
+                dm_bit_set(bs, bit);
+}
+
+static void test_equal(struct dm_pool *mem)
+{
+        dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS);
+        dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS);
+
+        int i, j;
+        for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
+                dm_bit_set(bs1, i);
+                dm_bit_set(bs2, i);
+        }
+
+        assert(dm_bitset_equal(bs1, bs2));
+        assert(dm_bitset_equal(bs2, bs1));
+
+        for (i = 0; i < NR_BITS; i++) {
+                bit_flip(bs1, i);
+                assert(!dm_bitset_equal(bs1, bs2));
+                assert(!dm_bitset_equal(bs2, bs1));
+
+                assert(dm_bitset_equal(bs1, bs1)); /* comparing with self */
+                bit_flip(bs1, i);
+        }
+}
+
+static void test_and(struct dm_pool *mem)
+{
+        dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS);
+        dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS);
+        dm_bitset_t bs3 = dm_bitset_create(mem, NR_BITS);
+
+        int i, j;
+        for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
+                dm_bit_set(bs1, i);
+                dm_bit_set(bs2, i);
+        }
+
+        dm_bit_and(bs3, bs1, bs2);
+
+        assert(dm_bitset_equal(bs1, bs2));
+        assert(dm_bitset_equal(bs1, bs3));
+        assert(dm_bitset_equal(bs2, bs3));
+
+        dm_bit_clear_all(bs1);
+        dm_bit_clear_all(bs2);
+
+        for (i = 0; i < NR_BITS; i++) {
+                if (i % 2)
+                        dm_bit_set(bs1, i);
+                else
+                        dm_bit_set(bs2, i);
+        }
+
+        dm_bit_and(bs3, bs1, bs2);
+        for (i = 0; i < NR_BITS; i++)
+                assert(!dm_bit(bs3, i));
+}
+
+int main(int argc, char **argv)
+{
+        typedef void (*test_fn)(struct dm_pool *);
+        static test_fn tests[] = {
+                test_get_next,
+                test_equal,
+                test_and
+        };
+
+        int i;
+        for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+                struct dm_pool *mem = dm_pool_create("bitset test", 1024);
+                assert(mem);
+                tests[i](mem);
+                dm_pool_destroy(mem);
+        }
+
+        return 0;
+}
+
diff --git a/unit-tests/mm/Makefile.in b/unit-tests/mm/Makefile.in
new file mode 100644 (file)
index 0000000..1b3499e
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+VPATH = @srcdir@
+
+SOURCES=\
+       pool_valgrind_t.c
+
+TARGETS=\
+       pool_valgrind_t
+
+include $(top_builddir)/make.tmpl
+DM_LIBS = -ldevmapper $(LIBS)
+
+pool_valgrind_t: pool_valgrind_t.o
+       $(CC) $(CFLAGS) -o $@ pool_valgrind_t.o $(LDFLAGS) $(DM_LIBS)
+
diff --git a/unit-tests/mm/TESTS b/unit-tests/mm/TESTS
new file mode 100644 (file)
index 0000000..3bf3154
--- /dev/null
@@ -0,0 +1 @@
+valgrind pool awareness:valgrind ./pool_valgrind_t 2>&1 | ./check_results
diff --git a/unit-tests/mm/check_results b/unit-tests/mm/check_results
new file mode 100755 (executable)
index 0000000..a7b0975
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby1.9
+
+require 'pp'
+
+patterns = [
+            /Invalid read of size 1/,
+            /Invalid write of size 1/,
+            /Invalid read of size 1/,
+            /still reachable: [0-9,]+ bytes in 3 blocks/
+           ]
+
+lines = STDIN.readlines
+pp lines
+
+result = catch(:done) do
+  patterns.each do |pat|
+    loop do
+      throw(:done, false) if lines.size == 0
+
+      line = lines.shift
+      if line =~ pat
+        STDERR.puts "matched #{pat}"
+        break;
+      end
+    end
+  end
+
+  throw(:done, true)
+end
+
+exit(result ? 0 : 1)
diff --git a/unit-tests/mm/pool_valgrind_t.c b/unit-tests/mm/pool_valgrind_t.c
new file mode 100644 (file)
index 0000000..b430a9c
--- /dev/null
@@ -0,0 +1,183 @@
+#include "libdevmapper.h"
+
+#include <assert.h>
+
+/*
+ * Checks that valgrind is picking up unallocated pool memory as
+ * uninitialised, even if the chunk has been recycled.
+ *
+ *     $ valgrind --track-origins=yes ./pool_valgrind_t
+ *
+ *     ==7023== Memcheck, a memory error detector
+ *     ==7023== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
+ *     ==7023== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
+ *     ==7023== Command: ./pool_valgrind_t
+ *     ==7023==
+ *     first branch worked (as expected)
+ *     ==7023== Conditional jump or move depends on uninitialised value(s)
+ *     ==7023==    at 0x4009AC: main (in /home/ejt/work/lvm2/unit-tests/mm/pool_valgrind_t)
+ *     ==7023==  Uninitialised value was created by a client request
+ *     ==7023==    at 0x4E40CB8: dm_pool_free (in /home/ejt/work/lvm2/libdm/ioctl/libdevmapper.so.1.02)
+ *     ==7023==    by 0x4009A8: main (in /home/ejt/work/lvm2/unit-tests/mm/pool_valgrind_t)
+ *     ==7023==
+ *     second branch worked (valgrind should have flagged this as an error)
+ *     ==7023==
+ *     ==7023== HEAP SUMMARY:
+ *     ==7023==     in use at exit: 0 bytes in 0 blocks
+ *     ==7023==   total heap usage: 2 allocs, 2 frees, 2,104 bytes allocated
+ *     ==7023==
+ *     ==7023== All heap blocks were freed -- no leaks are possible
+ *     ==7023==
+ *     ==7023== For counts of detected and suppressed errors, rerun with: -v
+ *     ==7023== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
+ */
+
+#define COUNT 10
+
+static void check_free()
+{
+        int i;
+        char *blocks[COUNT];
+        struct dm_pool *p = dm_pool_create("blah", 1024);
+
+        for (i = 0; i < COUNT; i++)
+                blocks[i] = dm_pool_alloc(p, 37);
+
+        /* check we can access the last block */
+        blocks[COUNT - 1][0] = 'E';
+        if (blocks[COUNT - 1][0] == 'E')
+                printf("first branch worked (as expected)\n");
+
+        dm_pool_free(p, blocks[5]);
+
+        if (blocks[COUNT - 1][0] == 'E')
+                printf("second branch worked (valgrind should have flagged this as an error)\n");
+
+        dm_pool_destroy(p);
+}
+
+/* Checks that freed chunks are marked NOACCESS */
+static void check_free2()
+{
+       struct dm_pool *p = dm_pool_create("", 900); /* 900 will get
+                                                     * rounded up to 1024,
+                                                     * 1024 would have got
+                                                     * rounded up to
+                                                     * 2048 */
+       char *data1, *data2;
+
+       assert(p);
+       data1 = dm_pool_alloc(p, 123);
+       assert(data1);
+
+       data1 = dm_pool_alloc(p, 1024);
+       assert(data1);
+
+       data2 = dm_pool_alloc(p, 123);
+       assert(data2);
+
+       data2[0] = 'A';         /* should work fine */
+
+       dm_pool_free(p, data1);
+
+       /*
+        * so now the first chunk is active, the second chunk has become
+        * the free one.
+        */
+       data2[0] = 'B';         /* should prompt an invalid write error */
+
+       dm_pool_destroy(p);
+}
+
+static void check_alignment()
+{
+       /*
+        * Pool always tries to allocate blocks with particular alignment.
+        * So there are potentially small gaps between allocations.  This
+        * test checks that valgrind is spotting illegal accesses to these
+        * gaps.
+        */
+
+       int i, sum;
+       struct dm_pool *p = dm_pool_create("blah", 1024);
+       char *data1, *data2;
+       char buffer[16];
+
+
+       data1 = dm_pool_alloc_aligned(p, 1, 4);
+       assert(data1);
+       data2 = dm_pool_alloc_aligned(p, 1, 4);
+       assert(data1);
+
+       snprintf(buffer, sizeof(buffer), "%c", *(data1 + 1)); /* invalid read size 1 */
+       dm_pool_destroy(p);
+}
+
+/*
+ * Looking at the code I'm not sure allocations that are near the chunk
+ * size are working.  So this test is trying to exhibit a specific problem.
+ */
+static void check_allocation_near_chunk_size()
+{
+       int i;
+       char *data;
+       struct dm_pool *p = dm_pool_create("", 900);
+
+       /*
+        * allocate a lot and then free everything so we know there
+        * is a spare chunk.
+        */
+       for (i = 0; i < 1000; i++) {
+               data = dm_pool_alloc(p, 37);
+               memset(data, 0, 37);
+               assert(data);
+       }
+
+       dm_pool_empty(p);
+
+       /* now we allocate something close to the chunk size ... */
+       data = dm_pool_alloc(p, 1020);
+       assert(data);
+       memset(data, 0, 1020);
+
+       dm_pool_destroy(p);
+}
+
+/* FIXME: test the dbg_malloc at exit (this test should be in dbg_malloc) */
+static void check_leak_detection()
+{
+       int i;
+       struct dm_pool *p = dm_pool_create("", 1024);
+
+       for (i = 0; i < 10; i++)
+               dm_pool_alloc(p, (i + 1) * 37);
+}
+
+/* we shouldn't get any errors from this one */
+static void check_object_growth()
+{
+       int i;
+       struct dm_pool *p = dm_pool_create("", 32);
+       char data[100];
+       void *obj;
+
+       memset(data, 0, sizeof(data));
+
+       dm_pool_begin_object(p, 43);
+       for (i = 1; i < 100; i++)
+               dm_pool_grow_object(p, data, i);
+       obj = dm_pool_end_object(p);
+
+       dm_pool_destroy(p);
+}
+
+int main(int argc, char **argv)
+{
+       check_free();
+       check_free2();
+       check_alignment();
+       check_allocation_near_chunk_size();
+       check_leak_detection();
+       check_object_growth();
+        return 0;
+}
diff --git a/unit-tests/regex/Makefile.in b/unit-tests/regex/Makefile.in
new file mode 100644 (file)
index 0000000..b15b231
--- /dev/null
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES=\
+       parse_t.c \
+       matcher_t.c
+
+TARGETS=\
+       parse_t \
+       matcher_t
+
+include $(top_builddir)/make.tmpl
+
+INCLUDES += -I$(top_srcdir)/libdm
+DM_DEPS = $(top_builddir)/libdm/libdevmapper.so
+DM_LIBS = -ldevmapper $(LIBS)
+
+parse_t: parse_t.o $(DM_DEPS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ parse_t.o $(DM_LIBS)
+
+matcher_t: matcher_t.o $(DM_DEPS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ matcher_t.o $(DM_LIBS)
diff --git a/unit-tests/regex/TESTS b/unit-tests/regex/TESTS
new file mode 100644 (file)
index 0000000..3297942
--- /dev/null
@@ -0,0 +1,3 @@
+dfa matching:$TEST_TOOL ./matcher_t --fingerprint dev_patterns < devices.list > matcher_t.output && diff -u matcher_t.expected matcher_t.output
+dfa matching:$TEST_TOOL ./matcher_t --fingerprint random_regexes < /dev/null > matcher_t.output && diff -u matcher_t.expected2 matcher_t.output
+dfa with non-print regex chars:$TEST_TOOL ./matcher_t nonprint_regexes < nonprint_input > matcher_t.output && diff -u matcher_t.expected3 matcher_t.output
\ No newline at end of file
diff --git a/unit-tests/regex/dev_patterns b/unit-tests/regex/dev_patterns
new file mode 100644 (file)
index 0000000..34459ba
--- /dev/null
@@ -0,0 +1,2 @@
+"loop/[0-9]+"
+"hd[a-d][0-5]+"
diff --git a/unit-tests/regex/devices.list b/unit-tests/regex/devices.list
new file mode 100644 (file)
index 0000000..91af305
--- /dev/null
@@ -0,0 +1,880 @@
+/dev
+/dev/.devfsd
+/dev/cpu
+/dev/cpu/mtrr
+/dev/netlink
+/dev/netlink/route
+/dev/netlink/skip
+/dev/netlink/USERSOCK
+/dev/netlink/fwmonitor
+/dev/netlink/ARPD
+/dev/netlink/ROUTE6
+/dev/netlink/IP6_FW
+/dev/netlink/tap0
+/dev/netlink/tap1
+/dev/netlink/tap2
+/dev/netlink/tap3
+/dev/netlink/tap4
+/dev/netlink/tap5
+/dev/netlink/tap6
+/dev/netlink/tap7
+/dev/netlink/tap8
+/dev/netlink/tap9
+/dev/netlink/tap10
+/dev/netlink/tap11
+/dev/netlink/tap12
+/dev/netlink/tap13
+/dev/netlink/tap14
+/dev/netlink/tap15
+/dev/shm
+/dev/mem
+/dev/kmem
+/dev/null
+/dev/port
+/dev/zero
+/dev/full
+/dev/random
+/dev/urandom
+/dev/tty
+/dev/console
+/dev/vc
+/dev/vc/1
+/dev/vc/2
+/dev/vc/3
+/dev/vc/4
+/dev/vc/5
+/dev/vc/6
+/dev/vc/7
+/dev/vc/8
+/dev/vc/9
+/dev/vc/10
+/dev/vc/11
+/dev/vc/12
+/dev/vc/13
+/dev/vc/14
+/dev/vc/15
+/dev/vc/16
+/dev/vc/17
+/dev/vc/18
+/dev/vc/19
+/dev/vc/20
+/dev/vc/21
+/dev/vc/22
+/dev/vc/23
+/dev/vc/24
+/dev/vc/25
+/dev/vc/26
+/dev/vc/27
+/dev/vc/28
+/dev/vc/29
+/dev/vc/30
+/dev/vc/31
+/dev/vc/32
+/dev/vc/33
+/dev/vc/34
+/dev/vc/35
+/dev/vc/36
+/dev/vc/37
+/dev/vc/38
+/dev/vc/39
+/dev/vc/40
+/dev/vc/41
+/dev/vc/42
+/dev/vc/43
+/dev/vc/44
+/dev/vc/45
+/dev/vc/46
+/dev/vc/47
+/dev/vc/48
+/dev/vc/49
+/dev/vc/50
+/dev/vc/51
+/dev/vc/52
+/dev/vc/53
+/dev/vc/54
+/dev/vc/55
+/dev/vc/56
+/dev/vc/57
+/dev/vc/58
+/dev/vc/59
+/dev/vc/60
+/dev/vc/61
+/dev/vc/62
+/dev/vc/63
+/dev/vc/0
+/dev/ptmx
+/dev/misc
+/dev/misc/psaux
+/dev/pty
+/dev/pty/m0
+/dev/pty/m1
+/dev/pty/m2
+/dev/pty/m3
+/dev/pty/m4
+/dev/pty/m5
+/dev/pty/m6
+/dev/pty/m7
+/dev/pty/m8
+/dev/pty/m9
+/dev/pty/m10
+/dev/pty/m11
+/dev/pty/m12
+/dev/pty/m13
+/dev/pty/m14
+/dev/pty/m15
+/dev/pty/m16
+/dev/pty/m17
+/dev/pty/m18
+/dev/pty/m19
+/dev/pty/m20
+/dev/pty/m21
+/dev/pty/m22
+/dev/pty/m23
+/dev/pty/m24
+/dev/pty/m25
+/dev/pty/m26
+/dev/pty/m27
+/dev/pty/m28
+/dev/pty/m29
+/dev/pty/m30
+/dev/pty/m31
+/dev/pty/m32
+/dev/pty/m33
+/dev/pty/m34
+/dev/pty/m35
+/dev/pty/m36
+/dev/pty/m37
+/dev/pty/m38
+/dev/pty/m39
+/dev/pty/m40
+/dev/pty/m41
+/dev/pty/m42
+/dev/pty/m43
+/dev/pty/m44
+/dev/pty/m45
+/dev/pty/m46
+/dev/pty/m47
+/dev/pty/m48
+/dev/pty/m49
+/dev/pty/m50
+/dev/pty/m51
+/dev/pty/m52
+/dev/pty/m53
+/dev/pty/m54
+/dev/pty/m55
+/dev/pty/m56
+/dev/pty/m57
+/dev/pty/m58
+/dev/pty/m59
+/dev/pty/m60
+/dev/pty/m61
+/dev/pty/m62
+/dev/pty/m63
+/dev/pty/m64
+/dev/pty/m65
+/dev/pty/m66
+/dev/pty/m67
+/dev/pty/m68
+/dev/pty/m69
+/dev/pty/m70
+/dev/pty/m71
+/dev/pty/m72
+/dev/pty/m73
+/dev/pty/m74
+/dev/pty/m75
+/dev/pty/m76
+/dev/pty/m77
+/dev/pty/m78
+/dev/pty/m79
+/dev/pty/m80
+/dev/pty/m81
+/dev/pty/m82
+/dev/pty/m83
+/dev/pty/m84
+/dev/pty/m85
+/dev/pty/m86
+/dev/pty/m87
+/dev/pty/m88
+/dev/pty/m89
+/dev/pty/m90
+/dev/pty/m91
+/dev/pty/m92
+/dev/pty/m93
+/dev/pty/m94
+/dev/pty/m95
+/dev/pty/m96
+/dev/pty/m97
+/dev/pty/m98
+/dev/pty/m99
+/dev/pty/m100
+/dev/pty/m101
+/dev/pty/m102
+/dev/pty/m103
+/dev/pty/m104
+/dev/pty/m105
+/dev/pty/m106
+/dev/pty/m107
+/dev/pty/m108
+/dev/pty/m109
+/dev/pty/m110
+/dev/pty/m111
+/dev/pty/m112
+/dev/pty/m113
+/dev/pty/m114
+/dev/pty/m115
+/dev/pty/m116
+/dev/pty/m117
+/dev/pty/m118
+/dev/pty/m119
+/dev/pty/m120
+/dev/pty/m121
+/dev/pty/m122
+/dev/pty/m123
+/dev/pty/m124
+/dev/pty/m125
+/dev/pty/m126
+/dev/pty/m127
+/dev/pty/m128
+/dev/pty/m129
+/dev/pty/m130
+/dev/pty/m131
+/dev/pty/m132
+/dev/pty/m133
+/dev/pty/m134
+/dev/pty/m135
+/dev/pty/m136
+/dev/pty/m137
+/dev/pty/m138
+/dev/pty/m139
+/dev/pty/m140
+/dev/pty/m141
+/dev/pty/m142
+/dev/pty/m143
+/dev/pty/m144
+/dev/pty/m145
+/dev/pty/m146
+/dev/pty/m147
+/dev/pty/m148
+/dev/pty/m149
+/dev/pty/m150
+/dev/pty/m151
+/dev/pty/m152
+/dev/pty/m153
+/dev/pty/m154
+/dev/pty/m155
+/dev/pty/m156
+/dev/pty/m157
+/dev/pty/m158
+/dev/pty/m159
+/dev/pty/m160
+/dev/pty/m161
+/dev/pty/m162
+/dev/pty/m163
+/dev/pty/m164
+/dev/pty/m165
+/dev/pty/m166
+/dev/pty/m167
+/dev/pty/m168
+/dev/pty/m169
+/dev/pty/m170
+/dev/pty/m171
+/dev/pty/m172
+/dev/pty/m173
+/dev/pty/m174
+/dev/pty/m175
+/dev/pty/m176
+/dev/pty/m177
+/dev/pty/m178
+/dev/pty/m179
+/dev/pty/m180
+/dev/pty/m181
+/dev/pty/m182
+/dev/pty/m183
+/dev/pty/m184
+/dev/pty/m185
+/dev/pty/m186
+/dev/pty/m187
+/dev/pty/m188
+/dev/pty/m189
+/dev/pty/m190
+/dev/pty/m191
+/dev/pty/m192
+/dev/pty/m193
+/dev/pty/m194
+/dev/pty/m195
+/dev/pty/m196
+/dev/pty/m197
+/dev/pty/m198
+/dev/pty/m199
+/dev/pty/m200
+/dev/pty/m201
+/dev/pty/m202
+/dev/pty/m203
+/dev/pty/m204
+/dev/pty/m205
+/dev/pty/m206
+/dev/pty/m207
+/dev/pty/m208
+/dev/pty/m209
+/dev/pty/m210
+/dev/pty/m211
+/dev/pty/m212
+/dev/pty/m213
+/dev/pty/m214
+/dev/pty/m215
+/dev/pty/m216
+/dev/pty/m217
+/dev/pty/m218
+/dev/pty/m219
+/dev/pty/m220
+/dev/pty/m221
+/dev/pty/m222
+/dev/pty/m223
+/dev/pty/m224
+/dev/pty/m225
+/dev/pty/m226
+/dev/pty/m227
+/dev/pty/m228
+/dev/pty/m229
+/dev/pty/m230
+/dev/pty/m231
+/dev/pty/m232
+/dev/pty/m233
+/dev/pty/m234
+/dev/pty/m235
+/dev/pty/m236
+/dev/pty/m237
+/dev/pty/m238
+/dev/pty/m239
+/dev/pty/m240
+/dev/pty/m241
+/dev/pty/m242
+/dev/pty/m243
+/dev/pty/m244
+/dev/pty/m245
+/dev/pty/m246
+/dev/pty/m247
+/dev/pty/m248
+/dev/pty/m249
+/dev/pty/m250
+/dev/pty/m251
+/dev/pty/m252
+/dev/pty/m253
+/dev/pty/m254
+/dev/pty/m255
+/dev/pts
+/dev/pts/0
+/dev/pts/1
+/dev/pts/2
+/dev/pts/3
+/dev/pts/4
+/dev/pts/5
+/dev/pts/6
+/dev/pts/7
+/dev/vcc
+/dev/vcc/0
+/dev/vcc/a
+/dev/vcc/1
+/dev/vcc/a1
+/dev/vcc/2
+/dev/vcc/a2
+/dev/vcc/3
+/dev/vcc/a3
+/dev/vcc/5
+/dev/vcc/a5
+/dev/vcc/4
+/dev/vcc/a4
+/dev/vcc/6
+/dev/vcc/a6
+/dev/vcc/7
+/dev/vcc/a7
+/dev/tts
+/dev/tts/0
+/dev/cua
+/dev/cua/0
+/dev/ide
+/dev/ide/host0
+/dev/ide/host0/bus0
+/dev/ide/host0/bus0/target0
+/dev/ide/host0/bus0/target0/lun0
+/dev/ide/host0/bus0/target0/lun0/disc
+/dev/ide/host0/bus0/target0/lun0/part1
+/dev/ide/host0/bus0/target0/lun0/part2
+/dev/ide/host0/bus0/target0/lun0/part3
+/dev/ide/host0/bus0/target0/lun0/part4
+/dev/ide/host0/bus0/target0/lun0/part5
+/dev/ide/host0/bus0/target0/lun0/part6
+/dev/ide/host0/bus0/target0/lun0/part7
+/dev/ide/host0/bus0/target0/lun0/part8
+/dev/ide/host0/bus0/target1
+/dev/ide/host0/bus0/target1/lun0
+/dev/ide/host0/bus0/target1/lun0/disc
+/dev/ide/host0/bus0/target1/lun0/part1
+/dev/ide/host0/bus1
+/dev/ide/host0/bus1/target0
+/dev/ide/host0/bus1/target0/lun0
+/dev/ide/host0/bus1/target0/lun0/disc
+/dev/ide/host0/bus1/target0/lun0/part1
+/dev/ide/host0/bus1/target1
+/dev/ide/host0/bus1/target1/lun0
+/dev/discs
+/dev/discs/disc0
+/dev/discs/disc1
+/dev/discs/disc2
+/dev/floppy
+/dev/floppy/0u1440
+/dev/floppy/0u1680
+/dev/floppy/0u1722
+/dev/floppy/0u1743
+/dev/floppy/0u1760
+/dev/floppy/0u1920
+/dev/floppy/0u1840
+/dev/floppy/0u1600
+/dev/floppy/0u360
+/dev/floppy/0u720
+/dev/floppy/0u820
+/dev/floppy/0u830
+/dev/floppy/0u1040
+/dev/floppy/0u1120
+/dev/floppy/0u800
+/dev/floppy/0
+/dev/loop
+/dev/loop/0
+/dev/loop/1
+/dev/loop/2
+/dev/loop/3
+/dev/loop/4
+/dev/loop/5
+/dev/loop/6
+/dev/loop/7
+/dev/cdroms
+/dev/sound
+/dev/sound/dsp
+/dev/sound/dsp1
+/dev/sound/mixer
+/dev/sound/midi
+/dev/usb
+/dev/root
+/dev/initctl
+/dev/xconsole
+/dev/fd
+/dev/stdin
+/dev/stdout
+/dev/stderr
+/dev/route
+/dev/skip
+/dev/USERSOCK
+/dev/fwmonitor
+/dev/ARPD
+/dev/ROUTE6
+/dev/IP6_FW
+/dev/tap0
+/dev/tap1
+/dev/tap2
+/dev/tap3
+/dev/tap4
+/dev/tap5
+/dev/tap6
+/dev/tap7
+/dev/tap8
+/dev/tap9
+/dev/tap10
+/dev/tap11
+/dev/tap12
+/dev/tap13
+/dev/tap14
+/dev/tap15
+/dev/tty1
+/dev/tty2
+/dev/tty3
+/dev/tty4
+/dev/tty5
+/dev/tty6
+/dev/tty7
+/dev/tty8
+/dev/tty9
+/dev/tty10
+/dev/tty11
+/dev/tty12
+/dev/tty13
+/dev/tty14
+/dev/tty15
+/dev/tty16
+/dev/tty17
+/dev/tty18
+/dev/tty19
+/dev/tty20
+/dev/tty21
+/dev/tty22
+/dev/tty23
+/dev/tty24
+/dev/tty25
+/dev/tty26
+/dev/tty27
+/dev/tty28
+/dev/tty29
+/dev/tty30
+/dev/tty31
+/dev/tty32
+/dev/tty33
+/dev/tty34
+/dev/tty35
+/dev/tty36
+/dev/tty37
+/dev/tty38
+/dev/tty39
+/dev/tty40
+/dev/tty41
+/dev/tty42
+/dev/tty43
+/dev/tty44
+/dev/tty45
+/dev/tty46
+/dev/tty47
+/dev/tty48
+/dev/tty49
+/dev/tty50
+/dev/tty51
+/dev/tty52
+/dev/tty53
+/dev/tty54
+/dev/tty55
+/dev/tty56
+/dev/tty57
+/dev/tty58
+/dev/tty59
+/dev/tty60
+/dev/tty61
+/dev/tty62
+/dev/tty63
+/dev/tty0
+/dev/psaux
+/dev/ptyp0
+/dev/ptyp1
+/dev/ptyp2
+/dev/ptyp3
+/dev/ptyp4
+/dev/ptyp5
+/dev/ptyp6
+/dev/ptyp7
+/dev/ptyp8
+/dev/ptyp9
+/dev/ptypa
+/dev/ptypb
+/dev/ptypc
+/dev/ptypd
+/dev/ptype
+/dev/ptypf
+/dev/ptyq0
+/dev/ptyq1
+/dev/ptyq2
+/dev/ptyq3
+/dev/ptyq4
+/dev/ptyq5
+/dev/ptyq6
+/dev/ptyq7
+/dev/ptyq8
+/dev/ptyq9
+/dev/ptyqa
+/dev/ptyqb
+/dev/ptyqc
+/dev/ptyqd
+/dev/ptyqe
+/dev/ptyqf
+/dev/ptyr0
+/dev/ptyr1
+/dev/ptyr2
+/dev/ptyr3
+/dev/ptyr4
+/dev/ptyr5
+/dev/ptyr6
+/dev/ptyr7
+/dev/ptyr8
+/dev/ptyr9
+/dev/ptyra
+/dev/ptyrb
+/dev/ptyrc
+/dev/ptyrd
+/dev/ptyre
+/dev/ptyrf
+/dev/ptys0
+/dev/ptys1
+/dev/ptys2
+/dev/ptys3
+/dev/ptys4
+/dev/ptys5
+/dev/ptys6
+/dev/ptys7
+/dev/ptys8
+/dev/ptys9
+/dev/ptysa
+/dev/ptysb
+/dev/ptysc
+/dev/ptysd
+/dev/ptyse
+/dev/ptysf
+/dev/ptyt0
+/dev/ptyt1
+/dev/ptyt2
+/dev/ptyt3
+/dev/ptyt4
+/dev/ptyt5
+/dev/ptyt6
+/dev/ptyt7
+/dev/ptyt8
+/dev/ptyt9
+/dev/ptyta
+/dev/ptytb
+/dev/ptytc
+/dev/ptytd
+/dev/ptyte
+/dev/ptytf
+/dev/ptyu0
+/dev/ptyu1
+/dev/ptyu2
+/dev/ptyu3
+/dev/ptyu4
+/dev/ptyu5
+/dev/ptyu6
+/dev/ptyu7
+/dev/ptyu8
+/dev/ptyu9
+/dev/ptyua
+/dev/ptyub
+/dev/ptyuc
+/dev/ptyud
+/dev/ptyue
+/dev/ptyuf
+/dev/ptyv0
+/dev/ptyv1
+/dev/ptyv2
+/dev/ptyv3
+/dev/ptyv4
+/dev/ptyv5
+/dev/ptyv6
+/dev/ptyv7
+/dev/ptyv8
+/dev/ptyv9
+/dev/ptyva
+/dev/ptyvb
+/dev/ptyvc
+/dev/ptyvd
+/dev/ptyve
+/dev/ptyvf
+/dev/ptyw0
+/dev/ptyw1
+/dev/ptyw2
+/dev/ptyw3
+/dev/ptyw4
+/dev/ptyw5
+/dev/ptyw6
+/dev/ptyw7
+/dev/ptyw8
+/dev/ptyw9
+/dev/ptywa
+/dev/ptywb
+/dev/ptywc
+/dev/ptywd
+/dev/ptywe
+/dev/ptywf
+/dev/ptyx0
+/dev/ptyx1
+/dev/ptyx2
+/dev/ptyx3
+/dev/ptyx4
+/dev/ptyx5
+/dev/ptyx6
+/dev/ptyx7
+/dev/ptyx8
+/dev/ptyx9
+/dev/ptyxa
+/dev/ptyxb
+/dev/ptyxc
+/dev/ptyxd
+/dev/ptyxe
+/dev/ptyxf
+/dev/ptyy0
+/dev/ptyy1
+/dev/ptyy2
+/dev/ptyy3
+/dev/ptyy4
+/dev/ptyy5
+/dev/ptyy6
+/dev/ptyy7
+/dev/ptyy8
+/dev/ptyy9
+/dev/ptyya
+/dev/ptyyb
+/dev/ptyyc
+/dev/ptyyd
+/dev/ptyye
+/dev/ptyyf
+/dev/ptyz0
+/dev/ptyz1
+/dev/ptyz2
+/dev/ptyz3
+/dev/ptyz4
+/dev/ptyz5
+/dev/ptyz6
+/dev/ptyz7
+/dev/ptyz8
+/dev/ptyz9
+/dev/ptyza
+/dev/ptyzb
+/dev/ptyzc
+/dev/ptyzd
+/dev/ptyze
+/dev/ptyzf
+/dev/ptya0
+/dev/ptya1
+/dev/ptya2
+/dev/ptya3
+/dev/ptya4
+/dev/ptya5
+/dev/ptya6
+/dev/ptya7
+/dev/ptya8
+/dev/ptya9
+/dev/ptyaa
+/dev/ptyab
+/dev/ptyac
+/dev/ptyad
+/dev/ptyae
+/dev/ptyaf
+/dev/ptyb0
+/dev/ptyb1
+/dev/ptyb2
+/dev/ptyb3
+/dev/ptyb4
+/dev/ptyb5
+/dev/ptyb6
+/dev/ptyb7
+/dev/ptyb8
+/dev/ptyb9
+/dev/ptyba
+/dev/ptybb
+/dev/ptybc
+/dev/ptybd
+/dev/ptybe
+/dev/ptybf
+/dev/ptyc0
+/dev/ptyc1
+/dev/ptyc2
+/dev/ptyc3
+/dev/ptyc4
+/dev/ptyc5
+/dev/ptyc6
+/dev/ptyc7
+/dev/ptyc8
+/dev/ptyc9
+/dev/ptyca
+/dev/ptycb
+/dev/ptycc
+/dev/ptycd
+/dev/ptyce
+/dev/ptycf
+/dev/ptyd0
+/dev/ptyd1
+/dev/ptyd2
+/dev/ptyd3
+/dev/ptyd4
+/dev/ptyd5
+/dev/ptyd6
+/dev/ptyd7
+/dev/ptyd8
+/dev/ptyd9
+/dev/ptyda
+/dev/ptydb
+/dev/ptydc
+/dev/ptydd
+/dev/ptyde
+/dev/ptydf
+/dev/ptye0
+/dev/ptye1
+/dev/ptye2
+/dev/ptye3
+/dev/ptye4
+/dev/ptye5
+/dev/ptye6
+/dev/ptye7
+/dev/ptye8
+/dev/ptye9
+/dev/ptyea
+/dev/ptyeb
+/dev/ptyec
+/dev/ptyed
+/dev/ptyee
+/dev/ptyef
+/dev/vcs
+/dev/vcsa
+/dev/vcs1
+/dev/vcsa1
+/dev/ttyS0
+/dev/cua0
+/dev/hda
+/dev/hda1
+/dev/hda2
+/dev/hda3
+/dev/hda4
+/dev/hda5
+/dev/hda6
+/dev/hda7
+/dev/hda8
+/dev/hdb
+/dev/hdb1
+/dev/hdc
+/dev/hdc1
+/dev/fd0u1440
+/dev/fd0u1680
+/dev/fd0u1722
+/dev/fd0u1743
+/dev/fd0u1760
+/dev/fd0u1920
+/dev/fd0u1840
+/dev/fd0u1600
+/dev/fd0u360
+/dev/fd0u720
+/dev/fd0u820
+/dev/fd0u830
+/dev/fd0u1040
+/dev/fd0u1120
+/dev/fd0u800
+/dev/fd0
+/dev/loop0
+/dev/loop1
+/dev/loop2
+/dev/loop3
+/dev/loop4
+/dev/loop5
+/dev/loop6
+/dev/loop7
+/dev/dsp
+/dev/dsp1
+/dev/mixer
+/dev/midi
+/dev/lvm
+/dev/vg0
+/dev/vg0/group
+/dev/vg0/packages
+/dev/vg0/photos
+/dev/vg0/music
+/dev/log
+/dev/MAKEDEV
+/dev/printer
+/dev/vcs2
+/dev/vcsa2
+/dev/vcs3
+/dev/vcsa3
+/dev/vcs5
+/dev/vcsa5
+/dev/vcs4
+/dev/vcsa4
+/dev/vcs6
+/dev/vcsa6
+/dev/nvidia0
+/dev/nvidia1
+/dev/nvidia2
+/dev/nvidia3
+/dev/nvidiactl
+/dev/vcs7
+/dev/vcsa7
diff --git a/unit-tests/regex/matcher_t.c b/unit-tests/regex/matcher_t.c
new file mode 100644 (file)
index 0000000..40c4651
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include "libdevmapper.h"
+#include "log.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+
+static int _read_spec(const char *file, char ***regex, int *nregex)
+{
+       char buffer[1024], *start, *ptr;
+       FILE *fp = fopen(file, "r");
+       int asize = 100;
+       char **rx = dm_malloc(sizeof(*rx) * asize);
+       int nr = 0;
+
+       if (!fp)
+               return 0;
+
+       while (fgets(buffer, sizeof(buffer),fp)) {
+
+               /* trim leading whitespace */
+               for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++);
+
+               if (!*ptr || *ptr == '#')
+                       continue;
+
+               if (*ptr == '\"') {
+                       ptr++;
+                       start = ptr;
+                       while (*ptr && *ptr != '\"') {
+                               if (*ptr == '\\')
+                                       ptr++;
+                               ptr++;
+                       }
+
+                       if (!*ptr) {
+                               fprintf(stderr, "Formatting error : "
+                                       "No terminating quote\n");
+                               return 0;
+                       }
+
+                       rx[nr] = dm_malloc((ptr - start) + 1);
+                       strncpy(rx[nr], start, ptr - start);
+                       rx[nr][ptr - start] = '\0';
+                       nr++;
+               } else {
+                       fprintf(stderr, "%s", ptr);
+                       fprintf(stderr, "Formatting error : \"<regex>\" "
+                               "<token_name>\n");
+                       return 0;
+               }
+       }
+
+       *regex = rx;
+       *nregex = nr;
+       return 1;
+}
+
+static void _free_regex(char **regex, int nregex)
+{
+       int i;
+       for (i = 0; i < nregex; i++)
+               dm_free(regex[i]);
+
+       dm_free(regex);
+}
+
+static void _scan_input(struct dm_regex *m, char **regex)
+{
+       char buffer[256], *ptr;
+       int r;
+
+       while (fgets(buffer, sizeof(buffer), stdin)) {
+               if ((ptr = strchr(buffer, '\n')))
+                       *ptr = '\0';
+
+               r = dm_regex_match(m, buffer);
+
+               if (r >= 0)
+                       printf("%s : %s\n", buffer, regex[r]);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       struct dm_pool *mem;
+       struct dm_regex *scanner;
+       char **regex;
+       int nregex;
+       int ret = 0;
+       int want_finger_print = 0, i;
+       const char *pattern_file = NULL;
+
+       for (i = 1; i < argc; i++)
+               if (!strcmp(argv[i], "--fingerprint"))
+                       want_finger_print = 1;
+
+               else
+                       pattern_file = argv[i];
+
+       if (!pattern_file) {
+               fprintf(stderr, "Usage : %s [--fingerprint] <pattern_file>\n", argv[0]);
+               exit(1);
+       }
+
+       dm_log_init_verbose(_LOG_DEBUG);
+
+       if (!(mem = dm_pool_create("match_regex", 10 * 1024))) {
+               fprintf(stderr, "Couldn't create pool\n");
+               ret = 2;
+               goto err;
+       }
+
+       if (!_read_spec(pattern_file, &regex, &nregex)) {
+               fprintf(stderr, "Couldn't read the lex specification\n");
+               ret = 3;
+               goto err;
+       }
+
+       if (!(scanner = dm_regex_create(mem, (const char **)regex, nregex))) {
+               fprintf(stderr, "Couldn't build the lexer\n");
+               ret = 4;
+               goto err;
+       }
+
+       if (want_finger_print)
+               printf("fingerprint: %x\n", dm_regex_fingerprint(scanner));
+       _scan_input(scanner, regex);
+       _free_regex(regex, nregex);
+
+    err:
+       dm_pool_destroy(mem);
+
+       return ret;
+}
diff --git a/unit-tests/regex/matcher_t.expected b/unit-tests/regex/matcher_t.expected
new file mode 100644 (file)
index 0000000..0b986b0
--- /dev/null
@@ -0,0 +1,16 @@
+fingerprint: 352b6c4f
+/dev/loop/0 : loop/[0-9]+
+/dev/loop/1 : loop/[0-9]+
+/dev/loop/2 : loop/[0-9]+
+/dev/loop/3 : loop/[0-9]+
+/dev/loop/4 : loop/[0-9]+
+/dev/loop/5 : loop/[0-9]+
+/dev/loop/6 : loop/[0-9]+
+/dev/loop/7 : loop/[0-9]+
+/dev/hda1 : hd[a-d][0-5]+
+/dev/hda2 : hd[a-d][0-5]+
+/dev/hda3 : hd[a-d][0-5]+
+/dev/hda4 : hd[a-d][0-5]+
+/dev/hda5 : hd[a-d][0-5]+
+/dev/hdb1 : hd[a-d][0-5]+
+/dev/hdc1 : hd[a-d][0-5]+
diff --git a/unit-tests/regex/matcher_t.expected2 b/unit-tests/regex/matcher_t.expected2
new file mode 100644 (file)
index 0000000..7359937
--- /dev/null
@@ -0,0 +1 @@
+fingerprint: eed8ceb8
diff --git a/unit-tests/regex/matcher_t.expected3 b/unit-tests/regex/matcher_t.expected3
new file mode 100644 (file)
index 0000000..fa56149
--- /dev/null
@@ -0,0 +1,3 @@
+foo\80bar : \80
+fooÂb : fooÂb
+\80 : \80
diff --git a/unit-tests/regex/nonprint_input b/unit-tests/regex/nonprint_input
new file mode 100644 (file)
index 0000000..92a1807
--- /dev/null
@@ -0,0 +1,4 @@
+foo.bar
+foo\80bar
+fooÂb
+\80
diff --git a/unit-tests/regex/nonprint_regexes b/unit-tests/regex/nonprint_regexes
new file mode 100644 (file)
index 0000000..e164c21
--- /dev/null
@@ -0,0 +1,3 @@
+"foo\80bar"
+"fooÂb"
+"\80"
diff --git a/unit-tests/regex/parse_t.c b/unit-tests/regex/parse_t.c
new file mode 100644 (file)
index 0000000..54ebb0b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+/* hack - using unexported internal function */
+#define DEBUG
+#include "regex/parse_rx.c"
+
+#include <stdio.h>
+#include <ctype.h>
+
+static void _pretty_print(struct rx_node *rx, int depth)
+{
+       int i;
+       for (i = 0; i < depth; i++)
+               printf(" ");
+
+       /* display info about the node */
+       switch (rx->type) {
+       case CAT:
+               printf("Cat");
+               break;
+
+       case OR:
+               printf("Or");
+               break;
+
+       case STAR:
+               printf("Star");
+               break;
+
+       case PLUS:
+               printf("Plus");
+               break;
+
+       case QUEST:
+               printf("Quest");
+               break;
+
+       case CHARSET:
+               printf("Charset : ");
+               for (i = 0; i < 256; i++) {
+                       if (dm_bit(rx->charset, i) && isprint(i))
+                               printf("%c", (char) i);
+               }
+               break;
+
+       default:
+               printf("Unknown type");
+       }
+       printf("\n");
+
+       if (rx->left)
+               _pretty_print(rx->left, depth + 1);
+
+       if (rx->right)
+               _pretty_print(rx->right, depth + 1);
+}
+
+int main(int argc, char **argv)
+{
+       struct dm_pool *mem;
+       struct rx_node *rx;
+       int regex_print = 0;
+       int show_nodes = 0;
+       int regex_arg = 1;
+
+       if (argc == 3 && !strcmp(argv[1], "-r")) {
+               regex_print++;
+               regex_arg++;
+               argc--;
+       }
+
+       if (argc == 3 && !strcmp(argv[1], "-R")) {
+               regex_print++;
+               show_nodes++;
+               regex_arg++;
+               argc--;
+       }
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage : %s [-r] <regex>\n", argv[0]);
+               exit(0);
+       }
+
+       dm_log_init_verbose(_LOG_DEBUG);
+
+       if (!(mem = dm_pool_create("parse_regex", 1024))) {
+               fprintf(stderr, "Couldn't create pool\n");
+               exit(1);
+       }
+
+       if (!(rx = rx_parse_str(mem, argv[regex_arg]))) {
+               dm_pool_destroy(mem);
+               fprintf(stderr, "Couldn't parse regex\n");
+               exit(1);
+       }
+
+       if (regex_print)
+               _regex_print(rx, 0, show_nodes);
+       else
+               _pretty_print(rx, 0);
+
+       dm_pool_destroy(mem);
+
+       return 0;
+}
diff --git a/unit-tests/regex/random_regexes b/unit-tests/regex/random_regexes
new file mode 100644 (file)
index 0000000..7b9362d
--- /dev/null
@@ -0,0 +1,100 @@
+"(((a?)(([Ub]*)|z))((([qr]|X)+)([Qn]*)))+"
+"[HZejtuw]*"
+"((B|s)*)|(((([Fv]l)(N+))(([el]|C)(tJ)))?)"
+"((([Ma]?)|(t*))*)|((([cm]E)|(M?))|(([BE][EV])|([Qj][Mh])))"
+"(((([bw]*)|([IO]*))((zK)*))|(((pU)|(i|q))|((z?)|([HL]?))))*"
+"((([Pt]?)|[Tr])?)((Hq)*)"
+"[HOXcfgikosvwxz]"
+"[BCEFGHNPTUWfjlprsy]"
+"((((aD)*)|([Xo]+))+)(([HKn](([Eq]|[JQ])(I*)))*)"
+"([LNWYeghv]|e)*"
+"(((y(L*))*)|((([EP]+)(W+))*))*"
+"U*"
+"((((R+)(W|[Qr]))|([py]+))+)([LM]*)"
+"(([DOjx](D(b?)))|([Ke]*))*"
+"((([ls](c|[FT]))*)([JS]*))*"
+"((l?)|(([Gz]+)|(D*)))*"
+"[ABgjn]"
+"(((q|[dg])?)|([Uk]*))((([Fl]?)|([Ry]+))|(([IR]|c)|(T?)))"
+"((([an]|P)|[Jw])((a*)|(m*)))*"
+"((((R[ht])(h+))?)|(([pz](n?))+))+"
+"(((([Dc]b)([Sp][Ii]))|((k|F)*))|[Uiovz])*"
+"[Res]*"
+"[Zl]|a"
+"^[ANZdf]$"
+"[En]|(((Q+)(U+))([pt]*))"
+"[ADEIMQUWXZhklrsvz]"
+"(((S(y*))*)|(j*))*"
+"n*"
+"[NUau]*"
+"((((Z*)(D|[Nd]))|(([np]|B)+))|(([Xy][Fi])*))+"
+"((([EZ]?)|(d[HR]))*)((([Hg]|q)(P+))*)"
+"q"
+"((m*)|(p|B))|((((x?)|(t+))(([Sb][PX])(O|[HM])))+)"
+"((((A*)(z[RS]))*)|(((z+)(Q*))+))*"
+"(((M*)([Uu]*))+)|[Uk]"
+"[imv]"
+"[GLSchtw](([Yw]((F[Dd])|([Tw]+)))?)"
+"([MOZj]*)(S|[Wknr])"
+"((G|q)*)[BHKN]"
+"((((NW)|([Ao]?))|((l|[UV])+))+)|((i|(z*))*)"
+"((((Z+)|([IR]?))|(L*))|([JKQ]+))+"
+"([Bdin](S*))+"
+"[HLNSTp]*"
+"(((J*)([Bq]|[Yu]))*)|([Kv]*)"
+"(((([BJ]|[Zy])(wI))*)(y*))+"
+"(((hF)+)|(H*))*"
+"((([QU][Pj])([GQ]?))+)|[PWo]"
+"(((([cq][BX])?)|((f[DI])*))*)(([GM]*)[SVYr])"
+"(([Zt]*)|((qx)|(([BV]+)(f?))))*"
+"[ILWYhsx]*"
+"(([Uy]*)|[sv])|([NSc]*)"
+"((c*)|([JUfhy]?))+"
+"(((q*)([So]*))(((g[jq])(j?))+))*"
+"((b+)|(((T+)([fw]T))?))*"
+"((([DS]?)|([Th]|u))(Q*))*"
+"[FKLX]|((([fw](L?))(([gq]*)|(O?)))?)"
+"((([HZ]+)u)*)|[APWijn]"
+"(e*)|(((v?)|((J+)(Hb)))?)"
+"(e|((w+)f))*"
+"[BEHKPQVdelnqy]"
+"((((B|N)(s*))|[Rr])(((g?)|([rv]+))+))+"
+"(((s*)|(K*))([AP]G))*"
+"[CELTp]"
+"(([Fq]?)|([Al]+))*"
+"((((r?)|(y[jx]))|([mp]*))+)|((B(S*))*)"
+"((([Eq]+)|(Y[ds]))|(x|(i|[Ku])))[IJNrvy]"
+"((([NO]*)[Ix])+)([Jenq]+)"
+"(((([HP]*)(j|y))*)[Ylqvy])*"
+"[PTv]+"
+"[AINSZhpx]|([EOYZ]*)"
+"([ABCFQv]*)((([Zx]|h)+)|([ej]*))"
+"((([pr]*)|(([Dq]|p)|(H?)))?)([NRUXmoq]*)"
+"(([er]*)|([mx]*))(((nV)([am]?))+)"
+"[BHPRlpu]"
+"(((([Ah]|[tx])|(e|[uy]))?)((([fl]+)([Vz]|v))*))*"
+"[AGdm]"
+"(((K*)^(O*)$)|(B?))*"
+"((([Ks]|[Ka])*)|([FSTab]?))?"
+"(([kw]+)[ei])(([Hy]*)(([Mc]*)|(G|f)))"
+"((((e*)|(Zf))|(R|[nq]))((([Jz]v)([Rj]+))+))*"
+"(((a?)|(e?))(([Uc]*)(S+)))*"
+"((((E+)([MZ]?))+)|(((s|[Az])|z)*))?"
+"((((i[MO])*)|((LH)*))|(((BA)|([AI]+))|[Ug]))*"
+"[EGHILcho]*"
+"(((Z[vw])?)((z|g)+))(((H|U)([iv]Q))|([qw]?))"
+"(([ehmr]|((L[Uw])*))+)((a+)I)"
+"[EKNSWYagj](((v|[TX])|([Uk]+))*)"
+"(((R[Mo])|(O*))|([Fm]|([qw]*)))((m*)|((S|[Ki])?))"
+"((((kP)|c)?)((([do]+)|([Gi]?))*))*"
+"((^(B|W)$|([Ww]+))([no]*))|((([iv]?)|(M*))|((x|L)?))"
+"[AEGPRSbcfhsy]"
+"[Wbcf]|((([MO]?)|([NT]|m))(([Oo]?)([Wg]*)))"
+"(((YZ)*)[PQVei])*"
+"[GJKYt][AEGWdegmnt]"
+"^[CDEGJKNUVYZagkv]$"
+"([DPWbx]*)|(((q|B)|(P|u))((M[Bq])*))"
+"[FHIJRTVYZdiorsuvz]*"
+"([MWoqvz]*)|^(l*)"
+"(((I|[Rx])*)((X[Mf])([Xa]L)))([Ha]|([HY]*))"
+"(((l|[Sd])*)((([Ix]+)|([XY]?))(Z*)))+"