From 62fb420f515209b54a1c4ab33db0e76ca2015084 Mon Sep 17 00:00:00 2001 From: Jinkun Jang Date: Wed, 13 Mar 2013 01:41:50 +0900 Subject: [PATCH] Tizen 2.1 base --- COPYING | 340 + COPYING.LIB | 504 + INSTALL | 31 + Makefile.in | 156 + README | 27 + VERSION | 1 + VERSION_DM | 1 + WHATS_NEW | 2418 +++ WHATS_NEW_DM | 593 + autoconf/config.guess | 1526 ++ autoconf/config.sub | 1658 ++ autoconf/install-sh | 507 + configure | 19637 +++++++++++++++++++ configure.in | 1413 ++ daemons/Makefile.in | 43 + daemons/clvmd/Makefile.in | 117 + daemons/clvmd/clvm.h | 74 + daemons/clvmd/clvmd-cman.c | 504 + daemons/clvmd/clvmd-command.c | 413 + daemons/clvmd/clvmd-common.h | 33 + daemons/clvmd/clvmd-comms.h | 125 + daemons/clvmd/clvmd-corosync.c | 626 + daemons/clvmd/clvmd-gulm.c | 1010 + daemons/clvmd/clvmd-gulm.h | 9 + daemons/clvmd/clvmd-openais.c | 693 + daemons/clvmd/clvmd-singlenode.c | 287 + daemons/clvmd/clvmd.c | 2239 +++ daemons/clvmd/clvmd.h | 123 + daemons/clvmd/lvm-functions.c | 939 + daemons/clvmd/lvm-functions.h | 40 + daemons/clvmd/refresh_clvmd.c | 380 + daemons/clvmd/refresh_clvmd.h | 19 + daemons/clvmd/tcp-comms.c | 502 + daemons/clvmd/tcp-comms.h | 13 + daemons/cmirrord/Makefile.in | 38 + daemons/cmirrord/clogd.c | 234 + daemons/cmirrord/cluster.c | 1661 ++ daemons/cmirrord/cluster.h | 76 + daemons/cmirrord/common.h | 33 + daemons/cmirrord/compat.c | 210 + daemons/cmirrord/compat.h | 25 + daemons/cmirrord/functions.c | 1937 ++ daemons/cmirrord/functions.h | 34 + daemons/cmirrord/link_mon.c | 151 + daemons/cmirrord/link_mon.h | 20 + daemons/cmirrord/local.c | 416 + daemons/cmirrord/local.h | 20 + daemons/cmirrord/logging.c | 57 + daemons/cmirrord/logging.h | 77 + daemons/dmeventd/.exported_symbols | 3 + daemons/dmeventd/Makefile.in | 108 + daemons/dmeventd/dmeventd.c | 1883 ++ daemons/dmeventd/dmeventd.h | 76 + daemons/dmeventd/libdevmapper-event.c | 826 + daemons/dmeventd/libdevmapper-event.h | 111 + daemons/dmeventd/libdevmapper-event.pc.in | 11 + daemons/dmeventd/plugins/Makefile.in | 25 + daemons/dmeventd/plugins/lvm2/.exported_symbols | 6 + daemons/dmeventd/plugins/lvm2/Makefile.in | 33 + daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c | 152 + daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h | 39 + daemons/dmeventd/plugins/mirror/.exported_symbols | 3 + daemons/dmeventd/plugins/mirror/Makefile.in | 39 + daemons/dmeventd/plugins/mirror/dmeventd_mirror.c | 245 + .../dmeventd/plugins/snapshot/.exported_symbols | 3 + daemons/dmeventd/plugins/snapshot/Makefile.in | 35 + .../dmeventd/plugins/snapshot/dmeventd_snapshot.c | 262 + doc/Makefile.in | 31 + doc/example.conf.in | 577 + doc/example_cmdlib.c | 49 + doc/lvm_fault_handling.txt | 221 + doc/pvmove_outline.txt | 52 + doc/tagging.txt | 165 + doc/testing.txt | 41 + include/.symlinks.in | 67 + include/Makefile.in | 35 + lib/Makefile.in | 184 + lib/activate/activate.c | 1385 ++ lib/activate/activate.h | 124 + lib/activate/dev_manager.c | 1824 ++ lib/activate/dev_manager.h | 71 + lib/activate/fs.c | 402 + lib/activate/fs.h | 34 + lib/activate/targets.h | 32 + lib/cache/lvmcache.c | 1444 ++ lib/cache/lvmcache.h | 124 + lib/commands/errors.h | 26 + lib/commands/toolcontext.c | 1397 ++ lib/commands/toolcontext.h | 121 + lib/config/config.c | 1382 ++ lib/config/config.h | 123 + lib/config/defaults.h | 155 + lib/datastruct/btree.c | 137 + lib/datastruct/btree.h | 32 + lib/datastruct/lvm-types.h | 32 + lib/datastruct/str_list.c | 127 + lib/datastruct/str_list.h | 28 + lib/device/dev-cache.c | 876 + lib/device/dev-cache.h | 58 + lib/device/dev-io.c | 751 + lib/device/dev-luks.c | 43 + lib/device/dev-md.c | 335 + lib/device/dev-swap.c | 85 + lib/device/device.c | 482 + lib/device/device.h | 118 + lib/display/display.c | 851 + lib/display/display.h | 65 + lib/error/errseg.c | 116 + lib/filters/filter-composite.c | 79 + lib/filters/filter-composite.h | 23 + lib/filters/filter-md.c | 78 + lib/filters/filter-md.h | 23 + lib/filters/filter-persistent.c | 356 + lib/filters/filter-persistent.h | 28 + lib/filters/filter-regex.c | 220 + lib/filters/filter-regex.h | 32 + lib/filters/filter-sysfs.c | 339 + lib/filters/filter-sysfs.h | 23 + lib/filters/filter.c | 346 + lib/filters/filter.h | 46 + lib/format1/.exported_symbols | 1 + lib/format1/Makefile.in | 33 + lib/format1/disk-rep.c | 735 + lib/format1/disk-rep.h | 250 + lib/format1/format1.c | 608 + lib/format1/format1.h | 29 + lib/format1/import-export.c | 681 + lib/format1/import-extents.c | 372 + lib/format1/layout.c | 172 + lib/format1/lvm1-label.c | 129 + lib/format1/lvm1-label.h | 23 + lib/format1/vg_number.c | 62 + lib/format_pool/.exported_symbols | 1 + lib/format_pool/Makefile.in | 30 + lib/format_pool/disk_rep.c | 374 + lib/format_pool/disk_rep.h | 156 + lib/format_pool/format_pool.c | 340 + lib/format_pool/format_pool.h | 28 + lib/format_pool/import_export.c | 287 + lib/format_pool/pool_label.c | 106 + lib/format_pool/pool_label.h | 23 + lib/format_pool/sptype_names.h | 42 + lib/format_text/archive.c | 382 + lib/format_text/archiver.c | 460 + lib/format_text/archiver.h | 63 + lib/format_text/export.c | 800 + lib/format_text/flags.c | 185 + lib/format_text/format-text.c | 2210 +++ lib/format_text/format-text.h | 62 + lib/format_text/import-export.h | 88 + lib/format_text/import.c | 160 + lib/format_text/import_vsn1.c | 903 + lib/format_text/layout.h | 109 + lib/format_text/tags.c | 82 + lib/format_text/text_export.h | 50 + lib/format_text/text_import.h | 26 + lib/format_text/text_label.c | 385 + lib/freeseg/freeseg.c | 60 + lib/label/label.c | 396 + lib/label/label.h | 106 + lib/locking/.exported_symbols | 5 + lib/locking/Makefile.in | 26 + lib/locking/cluster_locking.c | 599 + lib/locking/external_locking.c | 98 + lib/locking/file_locking.c | 365 + lib/locking/locking.c | 566 + lib/locking/locking.h | 184 + lib/locking/locking_types.h | 49 + lib/locking/no_locking.c | 100 + lib/log/log.c | 383 + lib/log/log.h | 89 + lib/log/lvm-logging.h | 63 + lib/metadata/lv.c | 289 + lib/metadata/lv.h | 72 + lib/metadata/lv_alloc.h | 83 + lib/metadata/lv_manip.c | 3427 ++++ lib/metadata/merge.c | 390 + lib/metadata/metadata-exported.h | 739 + lib/metadata/metadata.c | 4106 ++++ lib/metadata/metadata.h | 417 + lib/metadata/mirror.c | 2079 ++ lib/metadata/pv.c | 304 + lib/metadata/pv.h | 83 + lib/metadata/pv_alloc.h | 30 + lib/metadata/pv_manip.c | 451 + lib/metadata/pv_map.c | 226 + lib/metadata/pv_map.h | 73 + lib/metadata/replicator_manip.c | 693 + lib/metadata/segtype.c | 37 + lib/metadata/segtype.h | 135 + lib/metadata/snapshot_manip.c | 226 + lib/metadata/vg.c | 482 + lib/metadata/vg.h | 135 + lib/mirror/.exported_symbols | 1 + lib/mirror/Makefile.in | 26 + lib/mirror/mirrored.c | 655 + lib/misc/configure.h.in | 618 + lib/misc/crc.c | 116 + lib/misc/crc.h | 23 + lib/misc/crc_gen.c | 47 + lib/misc/intl.h | 26 + lib/misc/last-path-component.h | 27 + lib/misc/lib.h | 48 + lib/misc/lvm-exec.c | 102 + lib/misc/lvm-exec.h | 24 + lib/misc/lvm-file.c | 285 + lib/misc/lvm-file.h | 63 + lib/misc/lvm-globals.c | 249 + lib/misc/lvm-globals.h | 69 + lib/misc/lvm-percent.c | 40 + lib/misc/lvm-percent.h | 43 + lib/misc/lvm-string.c | 382 + lib/misc/lvm-string.h | 73 + lib/misc/lvm-version.h.in | 30 + lib/misc/lvm-wrappers.c | 48 + lib/misc/lvm-wrappers.h | 41 + lib/misc/sharedlib.c | 67 + lib/misc/sharedlib.h | 27 + lib/misc/timestamp.c | 130 + lib/misc/timestamp.h | 33 + lib/misc/util.c | 22 + lib/misc/util.h | 36 + lib/mm/memlock.c | 411 + lib/mm/memlock.h | 28 + lib/mm/xlate.h | 64 + lib/replicator/.exported_symbols | 1 + lib/replicator/Makefile.in | 25 + lib/replicator/replicator.c | 790 + lib/report/columns.h | 143 + lib/report/properties.c | 388 + lib/report/properties.h | 53 + lib/report/report.c | 1026 + lib/report/report.h | 46 + lib/snapshot/.exported_symbols | 1 + lib/snapshot/Makefile.in | 26 + lib/snapshot/snapshot.c | 270 + lib/striped/striped.c | 238 + lib/unknown/unknown.c | 108 + lib/uuid/uuid.c | 223 + lib/uuid/uuid.h | 59 + lib/zero/zero.c | 113 + libdm/.exported_symbols | 2 + libdm/Makefile.in | 97 + libdm/datastruct/bitset.c | 105 + libdm/datastruct/hash.c | 268 + libdm/datastruct/list.c | 168 + libdm/ioctl/libdm-compat.h | 123 + libdm/ioctl/libdm-iface.c | 2141 ++ libdm/ioctl/libdm-targets.h | 79 + libdm/libdevmapper.h | 1226 ++ libdm/libdevmapper.pc.in | 11 + libdm/libdm-common.c | 1363 ++ libdm/libdm-common.h | 36 + libdm/libdm-deptree.c | 2449 +++ libdm/libdm-file.c | 210 + libdm/libdm-report.c | 1125 ++ libdm/libdm-string.c | 163 + libdm/misc/dm-ioctl.h | 333 + libdm/misc/dm-log-userspace.h | 397 + libdm/misc/dm-logging.h | 37 + libdm/misc/dmlib.h | 26 + libdm/misc/kdev_t.h | 22 + libdm/mm/dbg_malloc.c | 272 + libdm/mm/dbg_malloc.h | 46 + libdm/mm/pool-debug.c | 272 + libdm/mm/pool-fast.c | 286 + libdm/mm/pool.c | 77 + libdm/regex/matcher.c | 534 + libdm/regex/parse_rx.c | 670 + libdm/regex/parse_rx.h | 55 + libdm/regex/ttree.c | 117 + libdm/regex/ttree.h | 26 + liblvm/.exported_symbols | 0 liblvm/Doxyfile | 254 + liblvm/Makefile.in | 81 + liblvm/liblvm2app.pc.in | 11 + liblvm/lvm2app.h | 1610 ++ liblvm/lvm_base.c | 119 + liblvm/lvm_lv.c | 290 + liblvm/lvm_misc.c | 121 + liblvm/lvm_misc.h | 27 + liblvm/lvm_pv.c | 129 + liblvm/lvm_vg.c | 368 + make.tmpl.in | 390 + man/Makefile.in | 97 + man/clvmd.8.in | 96 + man/cmirrord.8.in | 30 + man/dmeventd.8.in | 44 + man/dmsetup.8.in | 471 + man/fsadm.8.in | 68 + man/lvchange.8.in | 122 + man/lvconvert.8.in | 224 + man/lvcreate.8.in | 258 + man/lvdisplay.8.in | 102 + man/lvextend.8.in | 101 + man/lvm.8.in | 328 + man/lvm.conf.5.in | 497 + man/lvmchange.8.in | 10 + man/lvmconf.8.in | 39 + man/lvmdiskscan.8.in | 24 + man/lvmdump.8.in | 54 + man/lvmsadc.8.in | 14 + man/lvmsar.8.in | 14 + man/lvreduce.8.in | 89 + man/lvremove.8.in | 49 + man/lvrename.8.in | 55 + man/lvresize.8.in | 98 + man/lvs.8.in | 117 + man/lvscan.8.in | 34 + man/pvchange.8.in | 42 + man/pvck.8.in | 33 + man/pvcreate.8.in | 180 + man/pvdisplay.8.in | 81 + man/pvmove.8.in | 106 + man/pvremove.8.in | 22 + man/pvresize.8.in | 49 + man/pvs.8.in | 94 + man/pvscan.8.in | 34 + man/vgcfgbackup.8.in | 32 + man/vgcfgrestore.8.in | 44 + man/vgchange.8.in | 198 + man/vgck.8.in | 15 + man/vgconvert.8.in | 39 + man/vgcreate.8.in | 150 + man/vgdisplay.8.in | 93 + man/vgexport.8.in | 27 + man/vgextend.8.in | 62 + man/vgimport.8.in | 25 + man/vgimportclone.8.in | 57 + man/vgmerge.8.in | 29 + man/vgmknodes.8.in | 26 + man/vgreduce.8.in | 39 + man/vgremove.8.in | 35 + man/vgrename.8.in | 49 + man/vgs.8.in | 100 + man/vgscan.8.in | 28 + man/vgsplit.8.in | 75 + packaging/device-mapper.changes | 14 + packaging/device-mapper.spec | 81 + po/Makefile.in | 65 + po/de.po | 10 + po/lvm2.po | 7630 +++++++ po/pogen.h | 26 + report-generators/lib/log.rb | 40 + report-generators/lib/report_templates.rb | 38 + report-generators/lib/reports.rb | 58 + report-generators/lib/schedule_file.rb | 56 + report-generators/lib/string-store.rb | 42 + report-generators/memcheck.rb | 86 + report-generators/templates/boiler_plate.rhtml | 25 + report-generators/templates/index.rhtml | 17 + report-generators/templates/memcheck.rhtml | 30 + report-generators/templates/unit_detail.rhtml | 37 + report-generators/templates/unit_test.rhtml | 23 + report-generators/test/example.schedule | 4 + .../test/strings/more_strings/test3.txt | 1 + report-generators/test/strings/test1.txt | 1 + report-generators/test/strings/test2 | 3 + report-generators/test/tc_log.rb | 36 + report-generators/test/tc_schedule_file.rb | 38 + report-generators/test/tc_string_store.rb | 29 + report-generators/test/ts.rb | 13 + report-generators/title_page.rb | 42 + report-generators/unit_test.rb | 56 + reports/stylesheet.css | 77 + scripts/Makefile.in | 56 + scripts/VolumeGroup.ocf | 279 + scripts/clvmd_fix_conf.sh | 162 + scripts/clvmd_init_red_hat.in | 218 + scripts/cmirrord_init_red_hat.in | 106 + scripts/fsadm.sh | 489 + scripts/last_cvs_update.sh | 163 + scripts/lvm2_monitoring_init_red_hat.in | 119 + scripts/lvm2_monitoring_init_rhel4 | 100 + scripts/lvm2create_initrd/Makefile | 6 + scripts/lvm2create_initrd/README | 40 + scripts/lvm2create_initrd/lvm2create_initrd | 502 + scripts/lvm2create_initrd/lvm2create_initrd.8 | 281 + scripts/lvm2create_initrd/lvm2create_initrd.pod | 187 + scripts/lvm2create_initrd/lvm2udev | 33 + scripts/lvmconf.sh | 262 + scripts/lvmconf_lockingtype2.sh | 259 + scripts/lvmdump.sh | 235 + scripts/relpath.awk | 40 + scripts/vg_convert | 18 + scripts/vgimportclone.sh | 366 + test/.gitignore | 4 + test/Makefile.in | 130 + test/api/Makefile.in | 61 + test/api/percent.c | 64 + test/api/percent.sh | 23 + test/api/test.c | 1091 ++ test/api/vgtest.c | 164 + test/api/vgtest.sh | 18 + test/check.sh | 198 + test/harness.c | 221 + test/harness.sh | 50 + test/lvm-utils.sh | 153 + test/mkdtemp | 120 + test/not.c | 65 + test/t-000-basic.sh | 30 + test/t-activate-missing.sh | 87 + test/t-activate-partial.sh | 30 + test/t-covercmd.sh | 82 + test/t-dmeventd-restart.sh | 32 + test/t-fsadm.sh | 123 + test/t-inconsistent-metadata.sh | 75 + test/t-listings.sh | 83 + test/t-lock-blocking.sh | 36 + test/t-lvchange-mirror.sh | 28 + test/t-lvconvert-mirror-basic-0.sh | 12 + test/t-lvconvert-mirror-basic-1.sh | 12 + test/t-lvconvert-mirror-basic-2.sh | 12 + test/t-lvconvert-mirror-basic-3.sh | 12 + test/t-lvconvert-mirror-basic.sh | 148 + test/t-lvconvert-mirror.sh | 242 + test/t-lvconvert-repair-dmeventd.sh | 26 + test/t-lvconvert-repair-policy.sh | 80 + test/t-lvconvert-repair-replace.sh | 61 + test/t-lvconvert-repair-transient.sh | 28 + test/t-lvconvert-repair.sh | 89 + test/t-lvconvert-twostep.sh | 21 + test/t-lvcreate-mirror.sh | 39 + test/t-lvcreate-operation.sh | 42 + test/t-lvcreate-pvtags.sh | 42 + test/t-lvcreate-small-snap.sh | 30 + test/t-lvcreate-usage.sh | 141 + test/t-lvextend-percent-extents.sh | 101 + test/t-lvextend-snapshot-dmeventd.sh | 51 + test/t-lvextend-snapshot-policy.sh | 47 + test/t-lvm-init.sh | 21 + test/t-lvmcache-exercise.sh | 23 + test/t-lvresize-mirror.sh | 38 + test/t-lvresize-usage.sh | 20 + test/t-mdata-strings.sh | 33 + test/t-metadata-balance.sh | 232 + test/t-metadata.sh | 80 + test/t-mirror-names.sh | 156 + test/t-mirror-vgreduce-removemissing.sh | 421 + test/t-nomda-missing.sh | 83 + test/t-pool-labels.sh | 39 + test/t-pv-range-overflow.sh | 32 + test/t-pvchange-usage.sh | 66 + test/t-pvcreate-metadata0.sh | 32 + test/t-pvcreate-operation-md.sh | 143 + test/t-pvcreate-operation.sh | 121 + test/t-pvcreate-usage.sh | 191 + test/t-pvmove-basic.sh | 374 + test/t-pvremove-usage.sh | 68 + test/t-read-ahead.sh | 62 + test/t-snapshot-autoumount-dmeventd.sh | 41 + test/t-snapshot-merge.sh | 132 + test/t-snapshots-of-mirrors.sh | 44 + test/t-tags.sh | 74 + test/t-test-partition.sh | 30 + test/t-topology-support.sh | 104 + test/t-unknown-segment.sh | 34 + test/t-unlost-pv.sh | 38 + test/t-vgcfgbackup-usage.sh | 54 + test/t-vgchange-maxlv.sh | 31 + test/t-vgchange-usage.sh | 44 + test/t-vgcreate-usage.sh | 163 + test/t-vgextend-restoremissing.sh | 30 + test/t-vgextend-usage.sh | 129 + test/t-vgmerge-operation.sh | 81 + test/t-vgmerge-usage.sh | 73 + test/t-vgreduce-usage.sh | 85 + test/t-vgrename-usage.sh | 41 + test/t-vgsplit-operation.sh | 290 + test/t-vgsplit-stacked.sh | 28 + test/t-vgsplit-usage.sh | 187 + test/test-utils.sh | 457 + tools/.exported_symbols | 0 tools/Makefile.in | 207 + tools/args.h | 149 + tools/cmdnames.h | 18 + tools/commands.h | 1034 + tools/dmsetup.c | 3412 ++++ tools/dumpconfig.c | 28 + tools/formats.c | 24 + tools/lvchange.c | 784 + tools/lvconvert.c | 1763 ++ tools/lvcreate.c | 566 + tools/lvdisplay.c | 59 + tools/lvextend.c | 21 + tools/lvm-static.c | 29 + tools/lvm.c | 253 + tools/lvm2cmd-static.c | 21 + tools/lvm2cmd.c | 27 + tools/lvm2cmd.h | 73 + tools/lvm2cmdline.h | 41 + tools/lvmchange.c | 23 + tools/lvmcmdlib.c | 117 + tools/lvmcmdline.c | 1495 ++ tools/lvmdiskscan.c | 154 + tools/lvreduce.c | 21 + tools/lvremove.c | 48 + tools/lvrename.c | 128 + tools/lvresize.c | 752 + tools/lvscan.c | 85 + tools/polldaemon.c | 343 + tools/polldaemon.h | 75 + tools/pvchange.c | 300 + tools/pvck.c | 41 + tools/pvcreate.c | 125 + tools/pvdisplay.c | 119 + tools/pvmove.c | 667 + tools/pvremove.c | 154 + tools/pvresize.c | 218 + tools/pvscan.c | 205 + tools/reporter.c | 467 + tools/segtypes.c | 24 + tools/stub.h | 43 + tools/toollib.c | 1546 ++ tools/toollib.h | 118 + tools/tools.h | 189 + tools/vgcfgbackup.c | 103 + tools/vgcfgrestore.c | 76 + tools/vgchange.c | 612 + tools/vgck.c | 47 + tools/vgconvert.c | 234 + tools/vgcreate.c | 132 + tools/vgdisplay.c | 105 + tools/vgexport.c | 69 + tools/vgextend.c | 140 + tools/vgimport.c | 75 + tools/vgmerge.c | 188 + tools/vgmknodes.c | 44 + tools/vgreduce.c | 592 + tools/vgremove.c | 82 + tools/vgrename.c | 207 + tools/vgscan.c | 61 + tools/vgsplit.c | 498 + udev/10-dm.rules.in | 130 + udev/11-dm-lvm.rules | 37 + udev/12-dm-permissions.rules | 81 + udev/13-dm-disk.rules | 27 + udev/95-dm-notify.rules | 12 + udev/Makefile.in | 37 + unit-tests/datastruct/Makefile.in | 32 + unit-tests/datastruct/TESTS | 1 + unit-tests/datastruct/bitset_t.c | 133 + unit-tests/mm/Makefile.in | 31 + unit-tests/mm/TESTS | 1 + unit-tests/mm/check_results | 31 + unit-tests/mm/pool_valgrind_t.c | 183 + unit-tests/regex/Makefile.in | 37 + unit-tests/regex/TESTS | 3 + unit-tests/regex/dev_patterns | 2 + unit-tests/regex/devices.list | 880 + unit-tests/regex/matcher_t.c | 156 + unit-tests/regex/matcher_t.expected | 16 + unit-tests/regex/matcher_t.expected2 | 1 + unit-tests/regex/matcher_t.expected3 | 3 + unit-tests/regex/nonprint_input | 4 + unit-tests/regex/nonprint_regexes | 3 + unit-tests/regex/parse_t.c | 118 + unit-tests/regex/random_regexes | 100 + 558 files changed, 160375 insertions(+) create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 README create mode 100644 VERSION create mode 100644 VERSION_DM create mode 100644 WHATS_NEW create mode 100644 WHATS_NEW_DM create mode 100755 autoconf/config.guess create mode 100755 autoconf/config.sub create mode 100755 autoconf/install-sh create mode 100755 configure create mode 100644 configure.in create mode 100644 daemons/Makefile.in create mode 100644 daemons/clvmd/Makefile.in create mode 100644 daemons/clvmd/clvm.h create mode 100644 daemons/clvmd/clvmd-cman.c create mode 100644 daemons/clvmd/clvmd-command.c create mode 100644 daemons/clvmd/clvmd-common.h create mode 100644 daemons/clvmd/clvmd-comms.h create mode 100644 daemons/clvmd/clvmd-corosync.c create mode 100644 daemons/clvmd/clvmd-gulm.c create mode 100644 daemons/clvmd/clvmd-gulm.h create mode 100644 daemons/clvmd/clvmd-openais.c create mode 100644 daemons/clvmd/clvmd-singlenode.c create mode 100644 daemons/clvmd/clvmd.c create mode 100644 daemons/clvmd/clvmd.h create mode 100644 daemons/clvmd/lvm-functions.c create mode 100644 daemons/clvmd/lvm-functions.h create mode 100644 daemons/clvmd/refresh_clvmd.c create mode 100644 daemons/clvmd/refresh_clvmd.h create mode 100644 daemons/clvmd/tcp-comms.c create mode 100644 daemons/clvmd/tcp-comms.h create mode 100644 daemons/cmirrord/Makefile.in create mode 100644 daemons/cmirrord/clogd.c create mode 100644 daemons/cmirrord/cluster.c create mode 100644 daemons/cmirrord/cluster.h create mode 100644 daemons/cmirrord/common.h create mode 100644 daemons/cmirrord/compat.c create mode 100644 daemons/cmirrord/compat.h create mode 100644 daemons/cmirrord/functions.c create mode 100644 daemons/cmirrord/functions.h create mode 100644 daemons/cmirrord/link_mon.c create mode 100644 daemons/cmirrord/link_mon.h create mode 100644 daemons/cmirrord/local.c create mode 100644 daemons/cmirrord/local.h create mode 100644 daemons/cmirrord/logging.c create mode 100644 daemons/cmirrord/logging.h create mode 100644 daemons/dmeventd/.exported_symbols create mode 100644 daemons/dmeventd/Makefile.in create mode 100644 daemons/dmeventd/dmeventd.c create mode 100644 daemons/dmeventd/dmeventd.h create mode 100644 daemons/dmeventd/libdevmapper-event.c create mode 100644 daemons/dmeventd/libdevmapper-event.h create mode 100644 daemons/dmeventd/libdevmapper-event.pc.in create mode 100644 daemons/dmeventd/plugins/Makefile.in create mode 100644 daemons/dmeventd/plugins/lvm2/.exported_symbols create mode 100644 daemons/dmeventd/plugins/lvm2/Makefile.in create mode 100644 daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c create mode 100644 daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h create mode 100644 daemons/dmeventd/plugins/mirror/.exported_symbols create mode 100644 daemons/dmeventd/plugins/mirror/Makefile.in create mode 100644 daemons/dmeventd/plugins/mirror/dmeventd_mirror.c create mode 100644 daemons/dmeventd/plugins/snapshot/.exported_symbols create mode 100644 daemons/dmeventd/plugins/snapshot/Makefile.in create mode 100644 daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c create mode 100644 doc/Makefile.in create mode 100644 doc/example.conf.in create mode 100644 doc/example_cmdlib.c create mode 100644 doc/lvm_fault_handling.txt create mode 100644 doc/pvmove_outline.txt create mode 100644 doc/tagging.txt create mode 100644 doc/testing.txt create mode 100644 include/.symlinks.in create mode 100644 include/Makefile.in create mode 100644 lib/Makefile.in create mode 100644 lib/activate/activate.c create mode 100644 lib/activate/activate.h create mode 100644 lib/activate/dev_manager.c create mode 100644 lib/activate/dev_manager.h create mode 100644 lib/activate/fs.c create mode 100644 lib/activate/fs.h create mode 100644 lib/activate/targets.h create mode 100644 lib/cache/lvmcache.c create mode 100644 lib/cache/lvmcache.h create mode 100644 lib/commands/errors.h create mode 100644 lib/commands/toolcontext.c create mode 100644 lib/commands/toolcontext.h create mode 100644 lib/config/config.c create mode 100644 lib/config/config.h create mode 100644 lib/config/defaults.h create mode 100644 lib/datastruct/btree.c create mode 100644 lib/datastruct/btree.h create mode 100644 lib/datastruct/lvm-types.h create mode 100644 lib/datastruct/str_list.c create mode 100644 lib/datastruct/str_list.h create mode 100644 lib/device/dev-cache.c create mode 100644 lib/device/dev-cache.h create mode 100644 lib/device/dev-io.c create mode 100644 lib/device/dev-luks.c create mode 100644 lib/device/dev-md.c create mode 100644 lib/device/dev-swap.c create mode 100644 lib/device/device.c create mode 100644 lib/device/device.h create mode 100644 lib/display/display.c create mode 100644 lib/display/display.h create mode 100644 lib/error/errseg.c create mode 100644 lib/filters/filter-composite.c create mode 100644 lib/filters/filter-composite.h create mode 100644 lib/filters/filter-md.c create mode 100644 lib/filters/filter-md.h create mode 100644 lib/filters/filter-persistent.c create mode 100644 lib/filters/filter-persistent.h create mode 100644 lib/filters/filter-regex.c create mode 100644 lib/filters/filter-regex.h create mode 100644 lib/filters/filter-sysfs.c create mode 100644 lib/filters/filter-sysfs.h create mode 100644 lib/filters/filter.c create mode 100644 lib/filters/filter.h create mode 100644 lib/format1/.exported_symbols create mode 100644 lib/format1/Makefile.in create mode 100644 lib/format1/disk-rep.c create mode 100644 lib/format1/disk-rep.h create mode 100644 lib/format1/format1.c create mode 100644 lib/format1/format1.h create mode 100644 lib/format1/import-export.c create mode 100644 lib/format1/import-extents.c create mode 100644 lib/format1/layout.c create mode 100644 lib/format1/lvm1-label.c create mode 100644 lib/format1/lvm1-label.h create mode 100644 lib/format1/vg_number.c create mode 100644 lib/format_pool/.exported_symbols create mode 100644 lib/format_pool/Makefile.in create mode 100644 lib/format_pool/disk_rep.c create mode 100644 lib/format_pool/disk_rep.h create mode 100644 lib/format_pool/format_pool.c create mode 100644 lib/format_pool/format_pool.h create mode 100644 lib/format_pool/import_export.c create mode 100644 lib/format_pool/pool_label.c create mode 100644 lib/format_pool/pool_label.h create mode 100644 lib/format_pool/sptype_names.h create mode 100644 lib/format_text/archive.c create mode 100644 lib/format_text/archiver.c create mode 100644 lib/format_text/archiver.h create mode 100644 lib/format_text/export.c create mode 100644 lib/format_text/flags.c create mode 100644 lib/format_text/format-text.c create mode 100644 lib/format_text/format-text.h create mode 100644 lib/format_text/import-export.h create mode 100644 lib/format_text/import.c create mode 100644 lib/format_text/import_vsn1.c create mode 100644 lib/format_text/layout.h create mode 100644 lib/format_text/tags.c create mode 100644 lib/format_text/text_export.h create mode 100644 lib/format_text/text_import.h create mode 100644 lib/format_text/text_label.c create mode 100644 lib/freeseg/freeseg.c create mode 100644 lib/label/label.c create mode 100644 lib/label/label.h create mode 100644 lib/locking/.exported_symbols create mode 100644 lib/locking/Makefile.in create mode 100644 lib/locking/cluster_locking.c create mode 100644 lib/locking/external_locking.c create mode 100644 lib/locking/file_locking.c create mode 100644 lib/locking/locking.c create mode 100644 lib/locking/locking.h create mode 100644 lib/locking/locking_types.h create mode 100644 lib/locking/no_locking.c create mode 100644 lib/log/log.c create mode 100644 lib/log/log.h create mode 100644 lib/log/lvm-logging.h create mode 100644 lib/metadata/lv.c create mode 100644 lib/metadata/lv.h create mode 100644 lib/metadata/lv_alloc.h create mode 100644 lib/metadata/lv_manip.c create mode 100644 lib/metadata/merge.c create mode 100644 lib/metadata/metadata-exported.h create mode 100644 lib/metadata/metadata.c create mode 100644 lib/metadata/metadata.h create mode 100644 lib/metadata/mirror.c create mode 100644 lib/metadata/pv.c create mode 100644 lib/metadata/pv.h create mode 100644 lib/metadata/pv_alloc.h create mode 100644 lib/metadata/pv_manip.c create mode 100644 lib/metadata/pv_map.c create mode 100644 lib/metadata/pv_map.h create mode 100644 lib/metadata/replicator_manip.c create mode 100644 lib/metadata/segtype.c create mode 100644 lib/metadata/segtype.h create mode 100644 lib/metadata/snapshot_manip.c create mode 100644 lib/metadata/vg.c create mode 100644 lib/metadata/vg.h create mode 100644 lib/mirror/.exported_symbols create mode 100644 lib/mirror/Makefile.in create mode 100644 lib/mirror/mirrored.c create mode 100644 lib/misc/configure.h.in create mode 100644 lib/misc/crc.c create mode 100644 lib/misc/crc.h create mode 100644 lib/misc/crc_gen.c create mode 100644 lib/misc/intl.h create mode 100644 lib/misc/last-path-component.h create mode 100644 lib/misc/lib.h create mode 100644 lib/misc/lvm-exec.c create mode 100644 lib/misc/lvm-exec.h create mode 100644 lib/misc/lvm-file.c create mode 100644 lib/misc/lvm-file.h create mode 100644 lib/misc/lvm-globals.c create mode 100644 lib/misc/lvm-globals.h create mode 100644 lib/misc/lvm-percent.c create mode 100644 lib/misc/lvm-percent.h create mode 100644 lib/misc/lvm-string.c create mode 100644 lib/misc/lvm-string.h create mode 100644 lib/misc/lvm-version.h.in create mode 100644 lib/misc/lvm-wrappers.c create mode 100644 lib/misc/lvm-wrappers.h create mode 100644 lib/misc/sharedlib.c create mode 100644 lib/misc/sharedlib.h create mode 100644 lib/misc/timestamp.c create mode 100644 lib/misc/timestamp.h create mode 100644 lib/misc/util.c create mode 100644 lib/misc/util.h create mode 100644 lib/mm/memlock.c create mode 100644 lib/mm/memlock.h create mode 100644 lib/mm/xlate.h create mode 100644 lib/replicator/.exported_symbols create mode 100644 lib/replicator/Makefile.in create mode 100644 lib/replicator/replicator.c create mode 100644 lib/report/columns.h create mode 100644 lib/report/properties.c create mode 100644 lib/report/properties.h create mode 100644 lib/report/report.c create mode 100644 lib/report/report.h create mode 100644 lib/snapshot/.exported_symbols create mode 100644 lib/snapshot/Makefile.in create mode 100644 lib/snapshot/snapshot.c create mode 100644 lib/striped/striped.c create mode 100644 lib/unknown/unknown.c create mode 100644 lib/uuid/uuid.c create mode 100644 lib/uuid/uuid.h create mode 100644 lib/zero/zero.c create mode 100644 libdm/.exported_symbols create mode 100644 libdm/Makefile.in create mode 100644 libdm/datastruct/bitset.c create mode 100644 libdm/datastruct/hash.c create mode 100644 libdm/datastruct/list.c create mode 100644 libdm/ioctl/libdm-compat.h create mode 100644 libdm/ioctl/libdm-iface.c create mode 100644 libdm/ioctl/libdm-targets.h create mode 100644 libdm/libdevmapper.h create mode 100644 libdm/libdevmapper.pc.in create mode 100644 libdm/libdm-common.c create mode 100644 libdm/libdm-common.h create mode 100644 libdm/libdm-deptree.c create mode 100644 libdm/libdm-file.c create mode 100644 libdm/libdm-report.c create mode 100644 libdm/libdm-string.c create mode 100644 libdm/misc/dm-ioctl.h create mode 100644 libdm/misc/dm-log-userspace.h create mode 100644 libdm/misc/dm-logging.h create mode 100644 libdm/misc/dmlib.h create mode 100644 libdm/misc/kdev_t.h create mode 100644 libdm/mm/dbg_malloc.c create mode 100644 libdm/mm/dbg_malloc.h create mode 100644 libdm/mm/pool-debug.c create mode 100644 libdm/mm/pool-fast.c create mode 100644 libdm/mm/pool.c create mode 100644 libdm/regex/matcher.c create mode 100644 libdm/regex/parse_rx.c create mode 100644 libdm/regex/parse_rx.h create mode 100644 libdm/regex/ttree.c create mode 100644 libdm/regex/ttree.h create mode 100644 liblvm/.exported_symbols create mode 100644 liblvm/Doxyfile create mode 100644 liblvm/Makefile.in create mode 100644 liblvm/liblvm2app.pc.in create mode 100644 liblvm/lvm2app.h create mode 100644 liblvm/lvm_base.c create mode 100644 liblvm/lvm_lv.c create mode 100644 liblvm/lvm_misc.c create mode 100644 liblvm/lvm_misc.h create mode 100644 liblvm/lvm_pv.c create mode 100644 liblvm/lvm_vg.c create mode 100644 make.tmpl.in create mode 100644 man/Makefile.in create mode 100644 man/clvmd.8.in create mode 100644 man/cmirrord.8.in create mode 100644 man/dmeventd.8.in create mode 100644 man/dmsetup.8.in create mode 100644 man/fsadm.8.in create mode 100644 man/lvchange.8.in create mode 100644 man/lvconvert.8.in create mode 100644 man/lvcreate.8.in create mode 100644 man/lvdisplay.8.in create mode 100644 man/lvextend.8.in create mode 100644 man/lvm.8.in create mode 100644 man/lvm.conf.5.in create mode 100644 man/lvmchange.8.in create mode 100644 man/lvmconf.8.in create mode 100644 man/lvmdiskscan.8.in create mode 100644 man/lvmdump.8.in create mode 100644 man/lvmsadc.8.in create mode 100644 man/lvmsar.8.in create mode 100644 man/lvreduce.8.in create mode 100644 man/lvremove.8.in create mode 100644 man/lvrename.8.in create mode 100644 man/lvresize.8.in create mode 100644 man/lvs.8.in create mode 100644 man/lvscan.8.in create mode 100644 man/pvchange.8.in create mode 100644 man/pvck.8.in create mode 100644 man/pvcreate.8.in create mode 100644 man/pvdisplay.8.in create mode 100644 man/pvmove.8.in create mode 100644 man/pvremove.8.in create mode 100644 man/pvresize.8.in create mode 100644 man/pvs.8.in create mode 100644 man/pvscan.8.in create mode 100644 man/vgcfgbackup.8.in create mode 100644 man/vgcfgrestore.8.in create mode 100644 man/vgchange.8.in create mode 100644 man/vgck.8.in create mode 100644 man/vgconvert.8.in create mode 100644 man/vgcreate.8.in create mode 100644 man/vgdisplay.8.in create mode 100644 man/vgexport.8.in create mode 100644 man/vgextend.8.in create mode 100644 man/vgimport.8.in create mode 100644 man/vgimportclone.8.in create mode 100644 man/vgmerge.8.in create mode 100644 man/vgmknodes.8.in create mode 100644 man/vgreduce.8.in create mode 100644 man/vgremove.8.in create mode 100644 man/vgrename.8.in create mode 100644 man/vgs.8.in create mode 100644 man/vgscan.8.in create mode 100644 man/vgsplit.8.in create mode 100644 packaging/device-mapper.changes create mode 100644 packaging/device-mapper.spec create mode 100644 po/Makefile.in create mode 100644 po/de.po create mode 100644 po/lvm2.po create mode 100644 po/pogen.h create mode 100644 report-generators/lib/log.rb create mode 100644 report-generators/lib/report_templates.rb create mode 100644 report-generators/lib/reports.rb create mode 100644 report-generators/lib/schedule_file.rb create mode 100644 report-generators/lib/string-store.rb create mode 100644 report-generators/memcheck.rb create mode 100644 report-generators/templates/boiler_plate.rhtml create mode 100644 report-generators/templates/index.rhtml create mode 100644 report-generators/templates/memcheck.rhtml create mode 100644 report-generators/templates/unit_detail.rhtml create mode 100644 report-generators/templates/unit_test.rhtml create mode 100644 report-generators/test/example.schedule create mode 100644 report-generators/test/strings/more_strings/test3.txt create mode 100644 report-generators/test/strings/test1.txt create mode 100644 report-generators/test/strings/test2 create mode 100644 report-generators/test/tc_log.rb create mode 100644 report-generators/test/tc_schedule_file.rb create mode 100644 report-generators/test/tc_string_store.rb create mode 100644 report-generators/test/ts.rb create mode 100644 report-generators/title_page.rb create mode 100644 report-generators/unit_test.rb create mode 100644 reports/stylesheet.css create mode 100644 scripts/Makefile.in create mode 100644 scripts/VolumeGroup.ocf create mode 100644 scripts/clvmd_fix_conf.sh create mode 100644 scripts/clvmd_init_red_hat.in create mode 100755 scripts/cmirrord_init_red_hat.in create mode 100644 scripts/fsadm.sh create mode 100755 scripts/last_cvs_update.sh create mode 100644 scripts/lvm2_monitoring_init_red_hat.in create mode 100644 scripts/lvm2_monitoring_init_rhel4 create mode 100644 scripts/lvm2create_initrd/Makefile create mode 100644 scripts/lvm2create_initrd/README create mode 100644 scripts/lvm2create_initrd/lvm2create_initrd create mode 100644 scripts/lvm2create_initrd/lvm2create_initrd.8 create mode 100644 scripts/lvm2create_initrd/lvm2create_initrd.pod create mode 100644 scripts/lvm2create_initrd/lvm2udev create mode 100644 scripts/lvmconf.sh create mode 100644 scripts/lvmconf_lockingtype2.sh create mode 100755 scripts/lvmdump.sh create mode 100755 scripts/relpath.awk create mode 100755 scripts/vg_convert create mode 100755 scripts/vgimportclone.sh create mode 100644 test/.gitignore create mode 100644 test/Makefile.in create mode 100644 test/api/Makefile.in create mode 100644 test/api/percent.c create mode 100644 test/api/percent.sh create mode 100644 test/api/test.c create mode 100644 test/api/vgtest.c create mode 100644 test/api/vgtest.sh create mode 100644 test/check.sh create mode 100644 test/harness.c create mode 100644 test/harness.sh create mode 100644 test/lvm-utils.sh create mode 100755 test/mkdtemp create mode 100644 test/not.c create mode 100755 test/t-000-basic.sh create mode 100644 test/t-activate-missing.sh create mode 100644 test/t-activate-partial.sh create mode 100755 test/t-covercmd.sh create mode 100644 test/t-dmeventd-restart.sh create mode 100644 test/t-fsadm.sh create mode 100644 test/t-inconsistent-metadata.sh create mode 100644 test/t-listings.sh create mode 100644 test/t-lock-blocking.sh create mode 100644 test/t-lvchange-mirror.sh create mode 100644 test/t-lvconvert-mirror-basic-0.sh create mode 100644 test/t-lvconvert-mirror-basic-1.sh create mode 100644 test/t-lvconvert-mirror-basic-2.sh create mode 100644 test/t-lvconvert-mirror-basic-3.sh create mode 100644 test/t-lvconvert-mirror-basic.sh create mode 100644 test/t-lvconvert-mirror.sh create mode 100644 test/t-lvconvert-repair-dmeventd.sh create mode 100644 test/t-lvconvert-repair-policy.sh create mode 100644 test/t-lvconvert-repair-replace.sh create mode 100644 test/t-lvconvert-repair-transient.sh create mode 100644 test/t-lvconvert-repair.sh create mode 100644 test/t-lvconvert-twostep.sh create mode 100644 test/t-lvcreate-mirror.sh create mode 100644 test/t-lvcreate-operation.sh create mode 100755 test/t-lvcreate-pvtags.sh create mode 100644 test/t-lvcreate-small-snap.sh create mode 100755 test/t-lvcreate-usage.sh create mode 100755 test/t-lvextend-percent-extents.sh create mode 100644 test/t-lvextend-snapshot-dmeventd.sh create mode 100644 test/t-lvextend-snapshot-policy.sh create mode 100644 test/t-lvm-init.sh create mode 100755 test/t-lvmcache-exercise.sh create mode 100644 test/t-lvresize-mirror.sh create mode 100755 test/t-lvresize-usage.sh create mode 100755 test/t-mdata-strings.sh create mode 100755 test/t-metadata-balance.sh create mode 100755 test/t-metadata.sh create mode 100644 test/t-mirror-names.sh create mode 100755 test/t-mirror-vgreduce-removemissing.sh create mode 100644 test/t-nomda-missing.sh create mode 100755 test/t-pool-labels.sh create mode 100755 test/t-pv-range-overflow.sh create mode 100755 test/t-pvchange-usage.sh create mode 100755 test/t-pvcreate-metadata0.sh create mode 100644 test/t-pvcreate-operation-md.sh create mode 100755 test/t-pvcreate-operation.sh create mode 100755 test/t-pvcreate-usage.sh create mode 100755 test/t-pvmove-basic.sh create mode 100755 test/t-pvremove-usage.sh create mode 100755 test/t-read-ahead.sh create mode 100644 test/t-snapshot-autoumount-dmeventd.sh create mode 100755 test/t-snapshot-merge.sh create mode 100644 test/t-snapshots-of-mirrors.sh create mode 100755 test/t-tags.sh create mode 100644 test/t-test-partition.sh create mode 100644 test/t-topology-support.sh create mode 100644 test/t-unknown-segment.sh create mode 100644 test/t-unlost-pv.sh create mode 100644 test/t-vgcfgbackup-usage.sh create mode 100644 test/t-vgchange-maxlv.sh create mode 100644 test/t-vgchange-usage.sh create mode 100755 test/t-vgcreate-usage.sh create mode 100644 test/t-vgextend-restoremissing.sh create mode 100644 test/t-vgextend-usage.sh create mode 100755 test/t-vgmerge-operation.sh create mode 100755 test/t-vgmerge-usage.sh create mode 100755 test/t-vgreduce-usage.sh create mode 100755 test/t-vgrename-usage.sh create mode 100755 test/t-vgsplit-operation.sh create mode 100644 test/t-vgsplit-stacked.sh create mode 100755 test/t-vgsplit-usage.sh create mode 100644 test/test-utils.sh create mode 100644 tools/.exported_symbols create mode 100644 tools/Makefile.in create mode 100644 tools/args.h create mode 100644 tools/cmdnames.h create mode 100644 tools/commands.h create mode 100644 tools/dmsetup.c create mode 100644 tools/dumpconfig.c create mode 100644 tools/formats.c create mode 100644 tools/lvchange.c create mode 100644 tools/lvconvert.c create mode 100644 tools/lvcreate.c create mode 100644 tools/lvdisplay.c create mode 100644 tools/lvextend.c create mode 100644 tools/lvm-static.c create mode 100644 tools/lvm.c create mode 100644 tools/lvm2cmd-static.c create mode 100644 tools/lvm2cmd.c create mode 100644 tools/lvm2cmd.h create mode 100644 tools/lvm2cmdline.h create mode 100644 tools/lvmchange.c create mode 100644 tools/lvmcmdlib.c create mode 100644 tools/lvmcmdline.c create mode 100644 tools/lvmdiskscan.c create mode 100644 tools/lvreduce.c create mode 100644 tools/lvremove.c create mode 100644 tools/lvrename.c create mode 100644 tools/lvresize.c create mode 100644 tools/lvscan.c create mode 100644 tools/polldaemon.c create mode 100644 tools/polldaemon.h create mode 100644 tools/pvchange.c create mode 100644 tools/pvck.c create mode 100644 tools/pvcreate.c create mode 100644 tools/pvdisplay.c create mode 100644 tools/pvmove.c create mode 100644 tools/pvremove.c create mode 100644 tools/pvresize.c create mode 100644 tools/pvscan.c create mode 100644 tools/reporter.c create mode 100644 tools/segtypes.c create mode 100644 tools/stub.h create mode 100644 tools/toollib.c create mode 100644 tools/toollib.h create mode 100644 tools/tools.h create mode 100644 tools/vgcfgbackup.c create mode 100644 tools/vgcfgrestore.c create mode 100644 tools/vgchange.c create mode 100644 tools/vgck.c create mode 100644 tools/vgconvert.c create mode 100644 tools/vgcreate.c create mode 100644 tools/vgdisplay.c create mode 100644 tools/vgexport.c create mode 100644 tools/vgextend.c create mode 100644 tools/vgimport.c create mode 100644 tools/vgmerge.c create mode 100644 tools/vgmknodes.c create mode 100644 tools/vgreduce.c create mode 100644 tools/vgremove.c create mode 100644 tools/vgrename.c create mode 100644 tools/vgscan.c create mode 100644 tools/vgsplit.c create mode 100644 udev/10-dm.rules.in create mode 100644 udev/11-dm-lvm.rules create mode 100644 udev/12-dm-permissions.rules create mode 100644 udev/13-dm-disk.rules create mode 100644 udev/95-dm-notify.rules create mode 100644 udev/Makefile.in create mode 100644 unit-tests/datastruct/Makefile.in create mode 100644 unit-tests/datastruct/TESTS create mode 100644 unit-tests/datastruct/bitset_t.c create mode 100644 unit-tests/mm/Makefile.in create mode 100644 unit-tests/mm/TESTS create mode 100755 unit-tests/mm/check_results create mode 100644 unit-tests/mm/pool_valgrind_t.c create mode 100644 unit-tests/regex/Makefile.in create mode 100644 unit-tests/regex/TESTS create mode 100644 unit-tests/regex/dev_patterns create mode 100644 unit-tests/regex/devices.list create mode 100644 unit-tests/regex/matcher_t.c create mode 100644 unit-tests/regex/matcher_t.expected create mode 100644 unit-tests/regex/matcher_t.expected2 create mode 100644 unit-tests/regex/matcher_t.expected3 create mode 100644 unit-tests/regex/nonprint_input create mode 100644 unit-tests/regex/nonprint_regexes create mode 100644 unit-tests/regex/parse_t.c create mode 100644 unit-tests/regex/random_regexes diff --git a/COPYING b/COPYING new file mode 100644 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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 index 0000000..5ab7695 --- /dev/null +++ b/COPYING.LIB @@ -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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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 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 index 0000000..f7e34f4 --- /dev/null +++ b/Makefile.in @@ -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 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 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 index 0000000..8593207 --- /dev/null +++ b/VERSION_DM @@ -0,0 +1 @@ +1.02.60 (2010-12-20) diff --git a/WHATS_NEW b/WHATS_NEW new file mode 100644 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 '. + 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
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 index 0000000..c6ce2e6 --- /dev/null +++ b/WHATS_NEW_DM @@ -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 filter to dmsetup table/status/ls. + Add --exec 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 index 0000000..f32079a --- /dev/null +++ b/autoconf/config.guess @@ -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 . +# Please send patches to . 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 ." + +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 /* 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 + + 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 + #include + + 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 + 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 + #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' /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 + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # 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 < +# include +#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 + 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 +# 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 < 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 index 0000000..6759825 --- /dev/null +++ b/autoconf/config.sub @@ -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 . 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 ." + +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 index 0000000..4fbbae7 --- /dev/null +++ b/autoconf/install-sh @@ -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 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 &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 +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#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 if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + 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 +#include +#include +#include +/* 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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 to if __STDC__ is defined, since + # 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 +#else +# include +#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 +_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 +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 +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 +#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 +#include +#include +#include + +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 + +_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 + +_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 +#include +#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 +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 +_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 +_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 +_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 +_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 +#include +#include +#include + +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 + +_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 + +_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 +#include +#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 +#include +#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 +#include +#include + +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 +#include + +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 + +_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 +#include + +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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 +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 +# define alloca _alloca +# else +# ifdef HAVE_ALLOCA_H +# include +# 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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 + +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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 +#ifdef HAVE_VFORK_H +# include +#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 , but some compilers + (e.g. gcc -O) don't grok . 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 +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 +#include + +#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 +# 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 +#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 declares pow. + For example, HP-UX 11i declares gettimeofday. */ +#define pow innocuous_pow + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char pow (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 . +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 . +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 . +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 . +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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#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 declares $ac_func. + For example, HP-UX 11i 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 to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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 ." + +_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=' ' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /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 +' >$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 = "" + +} +{ + 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 +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\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 index 0000000..6cb5e8d --- /dev/null +++ b/configure.in @@ -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 index 0000000..ce400d7 --- /dev/null +++ b/daemons/Makefile.in @@ -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 index 0000000..ee19e6c --- /dev/null +++ b/daemons/clvmd/Makefile.in @@ -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,, "@CLVMD@,")) + 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 index 0000000..c9ea10c --- /dev/null +++ b/daemons/clvmd/clvm.h @@ -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 index 0000000..52da2ac --- /dev/null +++ b/daemons/clvmd/clvmd-cman.c @@ -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 + +#include "clvmd-comms.h" +#include "clvm.h" +#include "clvmd.h" +#include "lvm-functions.h" + +#include + +#include + +#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= 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 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 index 0000000..49f1197 --- /dev/null +++ b/daemons/clvmd/clvmd-command.c @@ -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 + +#include "clvmd-comms.h" +#include "clvm.h" +#include "clvmd.h" +#include "lvm-functions.h" + +#include "locking.h" + +#include + +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 index 0000000..a94edd4 --- /dev/null +++ b/daemons/clvmd/clvmd-common.h @@ -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 +#include + +#endif diff --git a/daemons/clvmd/clvmd-comms.h b/daemons/clvmd/clvmd-comms.h new file mode 100644 index 0000000..fbcfe8b --- /dev/null +++ b/daemons/clvmd/clvmd-comms.h @@ -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 +# 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 +# include +# 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 +# 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 index 0000000..cfe7150 --- /dev/null +++ b/daemons/clvmd/clvmd-corosync.c @@ -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 + +#include "clvm.h" +#include "clvmd-comms.h" +#include "clvmd.h" +#include "lvm-functions.h" + +#include "locking.h" + +#include +#include +#include +#include + +#include + +/* 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; inodeid = joined_list[i].nodeid; + dm_hash_insert_binary(node_hash, + (char *)&ninfo->nodeid, + COROSYNC_CSID_LEN, ninfo); + } + } + ninfo->state = NODE_CLVMD; + } + + for (i=0; istate = NODE_DOWN; + } + + for (i=0; inodeid = 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 index 0000000..3561004 --- /dev/null +++ b/daemons/clvmd/clvmd-gulm.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..9416f5c --- /dev/null +++ b/daemons/clvmd/clvmd-gulm.h @@ -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 index 0000000..fc98b50 --- /dev/null +++ b/daemons/clvmd/clvmd-openais.c @@ -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 +#include +#include + +#include +#include + +#include +#include + +#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; inodeid = joined_list[i].nodeid; + dm_hash_insert_binary(node_hash, + (char *)&ninfo->nodeid, + OPENAIS_CSID_LEN, ninfo); + } + } + ninfo->state = NODE_CLVMD; + } + + for (i=0; istate = NODE_DOWN; + } + + for (i=0; inodeid = 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 index 0000000..335cb98 --- /dev/null +++ b/daemons/clvmd/clvmd-singlenode.c @@ -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 + +#include "locking.h" +#include "clvm.h" +#include "clvmd-comms.h" +#include "lvm-functions.h" +#include "clvmd.h" + +#include +#include +#include + +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 index 0000000..1a35c49 --- /dev/null +++ b/daemons/clvmd/clvmd.c @@ -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 + +#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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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 Command timeout (default 60 seconds)\n" + " -T Startup timeout (default none)\n" + " -I 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 index 0000000..ccc79cc --- /dev/null +++ b/daemons/clvmd/clvmd.h @@ -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 index 0000000..214f229 --- /dev/null +++ b/daemons/clvmd/lvm-functions.c @@ -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 + +#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 + +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 index 0000000..97153d4 --- /dev/null +++ b/daemons/clvmd/lvm-functions.h @@ -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 index 0000000..1e88b5c --- /dev/null +++ b/daemons/clvmd/refresh_clvmd.c @@ -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 +#include +#include + +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 index 0000000..e8d1698 --- /dev/null +++ b/daemons/clvmd/refresh_clvmd.h @@ -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 index 0000000..5f86556 --- /dev/null +++ b/daemons/clvmd/tcp-comms.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..9260e12 --- /dev/null +++ b/daemons/clvmd/tcp-comms.h @@ -0,0 +1,13 @@ +#include + +#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 index 0000000..0efc8d4 --- /dev/null +++ b/daemons/cmirrord/Makefile.in @@ -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 index 0000000..8d9a7b9 --- /dev/null +++ b/daemons/cmirrord/clogd.c @@ -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 +#include +#include +#include +#include +#include + +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 index 0000000..b6c925a --- /dev/null +++ b/daemons/cmirrord/cluster.c @@ -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 +#include +#include +#include +#include +#include + +/* 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 = §ion_id; + section_attr.expirationTime = SA_TIME_END; + +sync_create_retry: + rv = saCkptSectionCreate(h, §ion_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 = §ion_id; + section_attr.expirationTime = SA_TIME_END; + +clean_create_retry: + rv = saCkptSectionCreate(h, §ion_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 = §ion_id; + section_attr.expirationTime = SA_TIME_END; + +rr_create_retry: + rv = saCkptSectionCreate(h, §ion_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 -> %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 -> " + "(%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 index 0000000..50d87dd --- /dev/null +++ b/daemons/cmirrord/cluster.h @@ -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 index 0000000..c71c46b --- /dev/null +++ b/daemons/cmirrord/common.h @@ -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 index 0000000..3f7a9b5 --- /dev/null +++ b/daemons/cmirrord/compat.c @@ -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 + +/* + * 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 index 0000000..1e0edd4 --- /dev/null +++ b/daemons/cmirrord/compat.h @@ -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 index 0000000..de80793 --- /dev/null +++ b/daemons/cmirrord/functions.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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: + * [disk] [[no]sync] + * The kernel is responsible for adding the 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 index 0000000..20453f5 --- /dev/null +++ b/daemons/cmirrord/functions.h @@ -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 index 0000000..80a98d0 --- /dev/null +++ b/daemons/cmirrord/link_mon.c @@ -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 +#include +#include + +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 index 0000000..6600f95 --- /dev/null +++ b/daemons/cmirrord/link_mon.h @@ -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 index 0000000..9e076c4 --- /dev/null +++ b/daemons/cmirrord/local.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..91298be --- /dev/null +++ b/daemons/cmirrord/local.h @@ -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 index 0000000..fef114a --- /dev/null +++ b/daemons/cmirrord/logging.c @@ -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 index 0000000..40dd462 --- /dev/null +++ b/daemons/cmirrord/logging.h @@ -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 +#include +#include + +/* 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 index 0000000..25690c8 --- /dev/null +++ b/daemons/dmeventd/.exported_symbols @@ -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 index 0000000..e99e089 --- /dev/null +++ b/daemons/dmeventd/Makefile.in @@ -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)/$( +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for htonl, ntohl */ + +#ifdef linux +# include + +# 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 + +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 index 0000000..254758e --- /dev/null +++ b/daemons/dmeventd/dmeventd.h @@ -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 index 0000000..bc8ad99 --- /dev/null +++ b/daemons/dmeventd/libdevmapper-event.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* 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 index 0000000..0de20c1 --- /dev/null +++ b/daemons/dmeventd/libdevmapper-event.h @@ -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 + +/* + * 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 index 0000000..839433f --- /dev/null +++ b/daemons/dmeventd/libdevmapper-event.pc.in @@ -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 index 0000000..45176ad --- /dev/null +++ b/daemons/dmeventd/plugins/Makefile.in @@ -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 index 0000000..ebe3d05 --- /dev/null +++ b/daemons/dmeventd/plugins/lvm2/.exported_symbols @@ -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 index 0000000..46247aa --- /dev/null +++ b/daemons/dmeventd/plugins/lvm2/Makefile.in @@ -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 index 0000000..937d81d --- /dev/null +++ b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c @@ -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 +#include + +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 index 0000000..8efcb9b --- /dev/null +++ b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h @@ -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 index 0000000..b88c705 --- /dev/null +++ b/daemons/dmeventd/plugins/mirror/.exported_symbols @@ -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 index 0000000..7f80629 --- /dev/null +++ b/daemons/dmeventd/plugins/mirror/Makefile.in @@ -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 index 0000000..3e97d87 --- /dev/null +++ b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c @@ -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 /* 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, ¶ms); + + 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 index 0000000..b88c705 --- /dev/null +++ b/daemons/dmeventd/plugins/snapshot/.exported_symbols @@ -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 index 0000000..b8414b2 --- /dev/null +++ b/daemons/dmeventd/plugins/snapshot/Makefile.in @@ -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 index 0000000..6fc9f56 --- /dev/null +++ b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c @@ -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 +#include /* 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, ¶ms); + 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 index 0000000..c63e3ce --- /dev/null +++ b/doc/Makefile.in @@ -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 index 0000000..8504f5d --- /dev/null +++ b/doc/example.conf.in @@ -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 index 0000000..4d1a49d --- /dev/null +++ b/doc/example_cmdlib.c @@ -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 + +/* 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 index 0000000..fa30c0c --- /dev/null +++ b/doc/lvm_fault_handling.txt @@ -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 ': 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 ': 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 index 0000000..8746b8f --- /dev/null +++ b/doc/pvmove_outline.txt @@ -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 index 0000000..5a3f520 --- /dev/null +++ b/doc/tagging.txt @@ -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_.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 index 0000000..214435a --- /dev/null +++ b/doc/testing.txt @@ -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 index 0000000..c16e107 --- /dev/null +++ b/include/.symlinks.in @@ -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 index 0000000..3daaab1 --- /dev/null +++ b/include/Makefile.in @@ -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 index 0000000..010e888 --- /dev/null +++ b/lib/Makefile.in @@ -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 index 0000000..174f09e --- /dev/null +++ b/lib/activate/activate.c @@ -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 +#include +#include + +#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 index 0000000..df46ac2 --- /dev/null +++ b/lib/activate/activate.h @@ -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 index 0000000..a11d918 --- /dev/null +++ b/lib/activate/dev_manager.c @@ -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 +#include + +#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, ¶ms); + /* 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, ¶ms); + 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, ¶ms); + 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, + ¶ms); + 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, + ¶ms); + + 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/ 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 index 0000000..b0bb275 --- /dev/null +++ b/lib/activate/dev_manager.h @@ -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 index 0000000..1523115 --- /dev/null +++ b/lib/activate/fs.c @@ -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 +#include +#include +#include +#include + +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 index 0000000..28b2c73 --- /dev/null +++ b/lib/activate/fs.h @@ -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 index 0000000..ac68c5b --- /dev/null +++ b/lib/activate/targets.h @@ -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 index 0000000..d545563 --- /dev/null +++ b/lib/cache/lvmcache.c @@ -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 index 0000000..06838dc --- /dev/null +++ b/lib/cache/lvmcache.h @@ -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 index 0000000..a644fc7 --- /dev/null +++ b/lib/commands/errors.h @@ -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 index 0000000..c4da38a --- /dev/null +++ b/lib/commands/toolcontext.c @@ -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 +#include +#include +#include +#include + +#ifdef linux +# include +#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 index 0000000..4628c7c --- /dev/null +++ b/lib/commands/toolcontext.h @@ -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 +#include + +/* + * 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 index 0000000..72908f2 --- /dev/null +++ b/lib/config/config.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..f70deb0 --- /dev/null +++ b/lib/config/config.h @@ -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 index 0000000..a640112 --- /dev/null +++ b/lib/config/defaults.h @@ -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 index 0000000..bfcdca6 --- /dev/null +++ b/lib/datastruct/btree.c @@ -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 index 0000000..b64f3c7 --- /dev/null +++ b/lib/datastruct/btree.h @@ -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 index 0000000..5358850 --- /dev/null +++ b/lib/datastruct/lvm-types.h @@ -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 +#include + +/* 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 index 0000000..81575cb --- /dev/null +++ b/lib/datastruct/str_list.c @@ -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 index 0000000..f7180e2 --- /dev/null +++ b/lib/datastruct/str_list.h @@ -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 index 0000000..962aa1e --- /dev/null +++ b/lib/device/dev-cache.c @@ -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 +#include +#include + +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 index 0000000..c1c86d6 --- /dev/null +++ b/lib/device/dev-cache.h @@ -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 index 0000000..eb80a89 --- /dev/null +++ b/lib/device/dev-io.c @@ -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 +#include +#include +#include +#include + +#ifdef linux +# define u64 uint64_t /* Missing without __KERNEL__ */ +# undef WNOHANG /* Avoid redefinition */ +# undef WUNTRACED /* Avoid redefinition */ +# include /* 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 +# 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 index 0000000..6337992 --- /dev/null +++ b/lib/device/dev-luks.c @@ -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 index 0000000..89b9341 --- /dev/null +++ b/lib/device/dev-md.c @@ -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 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 index 0000000..b8ebcca --- /dev/null +++ b/lib/device/dev-swap.c @@ -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 index 0000000..80b4479 --- /dev/null +++ b/lib/device/device.c @@ -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 /* 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 index 0000000..694f503 --- /dev/null +++ b/lib/device/device.h @@ -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 + +#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 index 0000000..aae660a --- /dev/null +++ b/lib/display/display.c @@ -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 index 0000000..fb000a2 --- /dev/null +++ b/lib/display/display.h @@ -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 + +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 index 0000000..add1df8 --- /dev/null +++ b/lib/error/errseg.c @@ -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 index 0000000..130490a --- /dev/null +++ b/lib/filters/filter-composite.c @@ -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 + +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 index 0000000..75c0b1f --- /dev/null +++ b/lib/filters/filter-composite.h @@ -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 index 0000000..5cd8fb2 --- /dev/null +++ b/lib/filters/filter-md.c @@ -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 index 0000000..6a98f0b --- /dev/null +++ b/lib/filters/filter-md.h @@ -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 index 0000000..f3b1e05 --- /dev/null +++ b/lib/filters/filter-persistent.c @@ -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 +#include +#include + +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 index 0000000..a7f1245 --- /dev/null +++ b/lib/filters/filter-persistent.h @@ -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 index 0000000..e046c71 --- /dev/null +++ b/lib/filters/filter-regex.c @@ -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 index 0000000..a009c91 --- /dev/null +++ b/lib/filters/filter-regex.h @@ -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], 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 index 0000000..be8fbde --- /dev/null +++ b/lib/filters/filter-sysfs.c @@ -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 + +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 index 0000000..9e4f503 --- /dev/null +++ b/lib/filters/filter-sysfs.h @@ -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 index 0000000..c623d2a --- /dev/null +++ b/lib/filters/filter.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..07611f9 --- /dev/null +++ b/lib/filters/filter.h @@ -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 + +#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 index 0000000..e9fac2e --- /dev/null +++ b/lib/format1/.exported_symbols @@ -0,0 +1 @@ +init_format diff --git a/lib/format1/Makefile.in b/lib/format1/Makefile.in new file mode 100644 index 0000000..e102fe8 --- /dev/null +++ b/lib/format1/Makefile.in @@ -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 index 0000000..071b39d --- /dev/null +++ b/lib/format1/disk-rep.c @@ -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 + +#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 index 0000000..0d54438 --- /dev/null +++ b/lib/format1/disk-rep.h @@ -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 index 0000000..fc14444 --- /dev/null +++ b/lib/format1/format1.c @@ -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 index 0000000..c76ba62 --- /dev/null +++ b/lib/format1/format1.h @@ -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 index 0000000..d0b1b31 --- /dev/null +++ b/lib/format1/import-export.c @@ -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 + +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 index 0000000..99723ee --- /dev/null +++ b/lib/format1/import-extents.c @@ -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 index 0000000..de9b206 --- /dev/null +++ b/lib/format1/layout.c @@ -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 index 0000000..07596a5 --- /dev/null +++ b/lib/format1/lvm1-label.c @@ -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 +#include + +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 index 0000000..70d314d --- /dev/null +++ b/lib/format1/lvm1-label.h @@ -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 index 0000000..47570f5 --- /dev/null +++ b/lib/format1/vg_number.c @@ -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 index 0000000..e9fac2e --- /dev/null +++ b/lib/format_pool/.exported_symbols @@ -0,0 +1 @@ +init_format diff --git a/lib/format_pool/Makefile.in b/lib/format_pool/Makefile.in new file mode 100644 index 0000000..be5195c --- /dev/null +++ b/lib/format_pool/Makefile.in @@ -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 index 0000000..ca8bfd7 --- /dev/null +++ b/lib/format_pool/disk_rep.c @@ -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 + +/* 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 index 0000000..fff7343 --- /dev/null +++ b/lib/format_pool/disk_rep.h @@ -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 index 0000000..730da87 --- /dev/null +++ b/lib/format_pool/format_pool.c @@ -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 index 0000000..0d4b973 --- /dev/null +++ b/lib/format_pool/format_pool.h @@ -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 index 0000000..7884247 --- /dev/null +++ b/lib/format_pool/import_export.c @@ -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 index 0000000..2d34e60 --- /dev/null +++ b/lib/format_pool/pool_label.c @@ -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 +#include + +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 index 0000000..b7dc371 --- /dev/null +++ b/lib/format_pool/pool_label.h @@ -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 index 0000000..fbfe33d --- /dev/null +++ b/lib/format_pool/sptype_names.h @@ -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 index 0000000..43425dc --- /dev/null +++ b/lib/format_text/archive.c @@ -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 +#include +#include +#include +#include +#include + +#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 _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 ? : ""); + 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 index 0000000..ef85c6c --- /dev/null +++ b/lib/format_text/archiver.c @@ -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 + +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 index 0000000..7346f93 --- /dev/null +++ b/lib/format_text/archiver.h @@ -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 index 0000000..8ddfa85 --- /dev/null +++ b/lib/format_text/export.c @@ -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 +#include +#include + +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 index 0000000..1d2a611 --- /dev/null +++ b/lib/format_text/flags.c @@ -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 index 0000000..c186757 --- /dev/null +++ b/lib/format_text/format-text.c @@ -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 +#include +#include +#include +#include +#include + +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 index 0000000..79365ea --- /dev/null +++ b/lib/format_text/format-text.h @@ -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 index 0000000..3b56f08 --- /dev/null +++ b/lib/format_text/import-export.h @@ -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 + +/* + * 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 index 0000000..60f465f --- /dev/null +++ b/lib/format_text/import.c @@ -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 index 0000000..e5e83d4 --- /dev/null +++ b/lib/format_text/import_vsn1.c @@ -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 index 0000000..a3141e0 --- /dev/null +++ b/lib/format_text/layout.h @@ -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 index 0000000..76d42db --- /dev/null +++ b/lib/format_text/tags.c @@ -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 index 0000000..39a6916 --- /dev/null +++ b/lib/format_text/text_export.h @@ -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 index 0000000..99d222f --- /dev/null +++ b/lib/format_text/text_import.h @@ -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 index 0000000..e459cde --- /dev/null +++ b/lib/format_text/text_label.c @@ -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 +#include + +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 index 0000000..8e73be0 --- /dev/null +++ b/lib/freeseg/freeseg.c @@ -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 index 0000000..c622812 --- /dev/null +++ b/lib/label/label.c @@ -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 +#include +#include + +/* 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, §or, 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, §or, 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 index 0000000..3e07f06 --- /dev/null +++ b/lib/label/label.h @@ -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 index 0000000..0a2cae7 --- /dev/null +++ b/lib/locking/.exported_symbols @@ -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 index 0000000..1aae878 --- /dev/null +++ b/lib/locking/Makefile.in @@ -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 index 0000000..eea9974 --- /dev/null +++ b/lib/locking/cluster_locking.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..949d065 --- /dev/null +++ b/lib/locking/external_locking.c @@ -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 index 0000000..9137a30 --- /dev/null +++ b/lib/locking/file_locking.c @@ -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 +#include +#include +#include +#include +#include + +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 index 0000000..60eaab3 --- /dev/null +++ b/lib/locking/locking.c @@ -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 +#include +#include +#include +#include + +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 index 0000000..8893500 --- /dev/null +++ b/lib/locking/locking.h @@ -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 index 0000000..c3ca16b --- /dev/null +++ b/lib/locking/locking_types.h @@ -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 index 0000000..8c4110f --- /dev/null +++ b/lib/locking/no_locking.c @@ -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 + +/* + * 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 index 0000000..86b4988 --- /dev/null +++ b/lib/log/log.c @@ -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 +#include + +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("", 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 index 0000000..10f34be --- /dev/null +++ b/lib/log/log.h @@ -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 /* FILE */ +#include /* strerror() */ +#include + +#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 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 index 0000000..1c0a580 --- /dev/null +++ b/lib/log/lvm-logging.h @@ -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 index 0000000..4e7d9dc --- /dev/null +++ b/lib/metadata/lv.c @@ -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 index 0000000..bff36d2 --- /dev/null +++ b/lib/metadata/lv.h @@ -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 index 0000000..785a957 --- /dev/null +++ b/lib/metadata/lv_alloc.h @@ -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 index 0000000..4a984a5 --- /dev/null +++ b/lib/metadata/lv_manip.c @@ -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: + * also, more than 4k + * say, reiserfs puts it's superblock 32k in, IIRC + * 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 index 0000000..3b8895c --- /dev/null +++ b/lib/metadata/merge.c @@ -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(¤t->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 index 0000000..6994e0c --- /dev/null +++ b/lib/metadata/metadata-exported.h @@ -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 index 0000000..5f75a66 --- /dev/null +++ b/lib/metadata/metadata.c @@ -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 +#include + +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 index 0000000..08bff4a --- /dev/null +++ b/lib/metadata/metadata.h @@ -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 index 0000000..1be07cf --- /dev/null +++ b/lib/metadata/mirror.c @@ -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, 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 index 0000000..e5bb9f0 --- /dev/null +++ b/lib/metadata/pv.c @@ -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 index 0000000..a02f6f8 --- /dev/null +++ b/lib/metadata/pv.h @@ -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 index 0000000..d894f31 --- /dev/null +++ b/lib/metadata/pv_alloc.h @@ -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 index 0000000..e6fe6c2 --- /dev/null +++ b/lib/metadata/pv_manip.c @@ -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 index 0000000..8ddb698 --- /dev/null +++ b/lib/metadata/pv_map.c @@ -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 + +/* + * 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 index 0000000..4ebfd4a --- /dev/null +++ b/lib/metadata/pv_map.h @@ -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 index 0000000..79abca0 --- /dev/null +++ b/lib/metadata/replicator_manip.c @@ -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 : "", + 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 index 0000000..7218743 --- /dev/null +++ b/lib/metadata/segtype.c @@ -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 index 0000000..6d50746 --- /dev/null +++ b/lib/metadata/segtype.h @@ -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 index 0000000..cb5df6b --- /dev/null +++ b/lib/metadata/snapshot_manip.c @@ -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 index 0000000..9d0d5dd --- /dev/null +++ b/lib/metadata/vg.c @@ -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 index 0000000..448dcfe --- /dev/null +++ b/lib/metadata/vg.h @@ -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 index 0000000..1c92c6a --- /dev/null +++ b/lib/mirror/.exported_symbols @@ -0,0 +1 @@ +init_segtype diff --git a/lib/mirror/Makefile.in b/lib/mirror/Makefile.in new file mode 100644 index 0000000..5168eda --- /dev/null +++ b/lib/mirror/Makefile.in @@ -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 index 0000000..3d25d70 --- /dev/null +++ b/lib/mirror/mirrored.c @@ -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 + +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)+ / */ + 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 index 0000000..d8e2db8 --- /dev/null +++ b/lib/misc/configure.h.in @@ -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 and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ASM_BYTEORDER_H + +/* Define to 1 if you have the 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 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 header file. */ +#undef HAVE_COROSYNC_CONFDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_CTYPE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the 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 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 header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBCMAN_H + +/* Define to 1 if dynamic libraries are available. */ +#undef HAVE_LIBDL + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBDLM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBGEN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBGULM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBINTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_FS_H + +/* Define to 1 if you have the 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 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 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 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 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 header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_HISTORY_H + +/* Define to 1 if you have the 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 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 header file. */ +#undef HAVE_SELINUX_LABEL_H + +/* Define to 1 if you have the 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 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 header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_DISK_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SEM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STATVFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTMPX_H + +/* Define to 1 if you have the 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 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 . + */ +#undef MAJOR_IN_MKDEV + +/* Define to 1 if `major', `minor', and `makedev' are declared in + . */ +#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 and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your 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 , + , or 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 , + , or 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 , + , or 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 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 does not define. */ +#undef mode_t + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to rpl_realloc if the replacement function should be used. */ +#undef realloc + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef ssize_t + +/* Define to `int' if 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 index 0000000..9f95c37 --- /dev/null +++ b/lib/misc/crc.c @@ -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 index 0000000..2910bc5 --- /dev/null +++ b/lib/misc/crc.h @@ -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 index 0000000..5e83317 --- /dev/null +++ b/lib/misc/crc_gen.c @@ -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 index 0000000..fe08021 --- /dev/null +++ b/lib/misc/intl.h @@ -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 +# 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 index 0000000..e0d5940 --- /dev/null +++ b/lib/misc/last-path-component.h @@ -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 + +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 index 0000000..100827d --- /dev/null +++ b/lib/misc/lib.h @@ -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 +#include +#include +#include +#include +#include + +#endif diff --git a/lib/misc/lvm-exec.c b/lib/misc/lvm-exec.c new file mode 100644 index 0000000..5cad79e --- /dev/null +++ b/lib/misc/lvm-exec.c @@ -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 +#include + +/* + * 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 index 0000000..6d984f8 --- /dev/null +++ b/lib/misc/lvm-exec.h @@ -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 index 0000000..f577840 --- /dev/null +++ b/lib/misc/lvm-file.c @@ -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 +#include +#include +#include +#include + +/* + * 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 index 0000000..07327c3 --- /dev/null +++ b/lib/misc/lvm-file.h @@ -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 index 0000000..9da61fe --- /dev/null +++ b/lib/misc/lvm-globals.c @@ -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 + +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 index 0000000..2fabbc7 --- /dev/null +++ b/lib/misc/lvm-globals.h @@ -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 index 0000000..4b73db4 --- /dev/null +++ b/lib/misc/lvm-percent.c @@ -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 index 0000000..a925190 --- /dev/null +++ b/lib/misc/lvm-percent.h @@ -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 + +/* + * 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 index 0000000..8fd2c04 --- /dev/null +++ b/lib/misc/lvm-string.c @@ -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 + +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); +} + +/* + * -- or if !layer just -. + */ +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 --, 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 index 0000000..20f45c9 --- /dev/null +++ b/lib/misc/lvm-string.h @@ -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 +#include + +#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 index 0000000..2574890 --- /dev/null +++ b/lib/misc/lvm-version.h.in @@ -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 index 0000000..3ab8a71 --- /dev/null +++ b/lib/misc/lvm-wrappers.c @@ -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 +#include + +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 index 0000000..19a7f03 --- /dev/null +++ b/lib/misc/lvm-wrappers.h @@ -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 index 0000000..cab2909 --- /dev/null +++ b/lib/misc/sharedlib.c @@ -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 +#include +#include + +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 index 0000000..d996a8b --- /dev/null +++ b/lib/misc/sharedlib.h @@ -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 + +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 index 0000000..62e1be8 --- /dev/null +++ b/lib/misc/timestamp.c @@ -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 + +#include "timestamp.h" + +/* + * The realtime section uses clock_gettime with the CLOCK_MONOTONIC + * parameter to prevent issues with time warps + */ +#ifdef HAVE_REALTIME + +#include +#include + +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 + +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 index 0000000..50e2a85 --- /dev/null +++ b/lib/misc/timestamp.h @@ -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 index 0000000..75c7afa --- /dev/null +++ b/lib/misc/util.c @@ -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 index 0000000..0bcfd21 --- /dev/null +++ b/lib/misc/util.h @@ -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 index 0000000..062b765 --- /dev/null +++ b/lib/mm/memlock.c @@ -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 +#include +#include +#include +#include +#include + +#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 index 0000000..fd19317 --- /dev/null +++ b/lib/mm/memlock.h @@ -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 index 0000000..57d7241 --- /dev/null +++ b/lib/mm/xlate.h @@ -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 +# include +#else +# include +# 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 +# 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 index 0000000..1c92c6a --- /dev/null +++ b/lib/replicator/.exported_symbols @@ -0,0 +1 @@ +init_segtype diff --git a/lib/replicator/Makefile.in b/lib/replicator/Makefile.in new file mode 100644 index 0000000..ac42730 --- /dev/null +++ b/lib/replicator/Makefile.in @@ -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 index 0000000..26a5cf1 --- /dev/null +++ b/lib/replicator/replicator.c @@ -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 index 0000000..acc232f --- /dev/null +++ b/lib/report/columns.h @@ -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 index 0000000..0b4929a --- /dev/null +++ b/lib/report/properties.c @@ -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 + +#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 index 0000000..f363362 --- /dev/null +++ b/lib/report/properties.h @@ -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 index 0000000..136ad4c --- /dev/null +++ b/lib/report/report.c @@ -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 /* 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 index 0000000..26cc2f7 --- /dev/null +++ b/lib/report/report.h @@ -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 index 0000000..1c92c6a --- /dev/null +++ b/lib/snapshot/.exported_symbols @@ -0,0 +1 @@ +init_segtype diff --git a/lib/snapshot/Makefile.in b/lib/snapshot/Makefile.in new file mode 100644 index 0000000..72399f3 --- /dev/null +++ b/lib/snapshot/Makefile.in @@ -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 index 0000000..1a98d7e --- /dev/null +++ b/lib/snapshot/snapshot.c @@ -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: / + * >= 1.8.0: / + */ + r = sscanf(params, "%" PRIu64 "/%" PRIu64 " %" PRIu64, + §ors_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 index 0000000..51bf24a --- /dev/null +++ b/lib/striped/striped.c @@ -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 index 0000000..332ae99 --- /dev/null +++ b/lib/unknown/unknown.c @@ -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 index 0000000..e85e852 --- /dev/null +++ b/lib/uuid/uuid.c @@ -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 +#include +#include +#include +#include + +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 index 0000000..5c8382d --- /dev/null +++ b/lib/uuid/uuid.h @@ -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 index 0000000..16ce66c --- /dev/null +++ b/lib/zero/zero.c @@ -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 index 0000000..3a69860 --- /dev/null +++ b/libdm/.exported_symbols @@ -0,0 +1,2 @@ +dm_log +dm_log_with_errno diff --git a/libdm/Makefile.in b/libdm/Makefile.in new file mode 100644 index 0000000..70be309 --- /dev/null +++ b/libdm/Makefile.in @@ -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)/$(> 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 index 0000000..d4543df --- /dev/null +++ b/libdm/datastruct/hash.c @@ -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 index 0000000..c7a052f --- /dev/null +++ b/libdm/datastruct/list.c @@ -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 + +/* + * 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 index 0000000..5ba63ce --- /dev/null +++ b/libdm/ioctl/libdm-compat.h @@ -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 +#include + +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 index 0000000..4a8dfc6 --- /dev/null +++ b/libdm/ioctl/libdm-iface.c @@ -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 +#include +#include +#include +#include + +#ifdef linux +# include "kdev_t.h" +# include +#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 index 0000000..d8cee45 --- /dev/null +++ b/libdm/ioctl/libdm-targets.h @@ -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 +#include + +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 index 0000000..4aa9991 --- /dev/null +++ b/libdm/libdevmapper.h @@ -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 +#include +#include + +#ifdef linux +# include +#endif + +#include +#include +#include +#include + +#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/ + * 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 index 0000000..eb7071d --- /dev/null +++ b/libdm/libdevmapper.pc.in @@ -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 index 0000000..6d8bcbd --- /dev/null +++ b/libdm/libdm-common.c @@ -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 +#include +#include +#include +#include + +#ifdef UDEV_SYNC_SUPPORT +# include +# include +# include +# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE +# include +#endif + +#ifdef linux +# include +#endif + +#ifdef HAVE_SELINUX +# include +#endif +#ifdef HAVE_SELINUX_LABEL_H +# include +#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, ¤t_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 index 0000000..3267cfc --- /dev/null +++ b/libdm/libdm-common.h @@ -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 index 0000000..8d00514 --- /dev/null +++ b/libdm/libdm-deptree.c @@ -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 +#include +#include + +#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 index 0000000..82fae34 --- /dev/null +++ b/libdm/libdm-file.c @@ -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 +#include +#include + +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 index 0000000..9b8e3c1 --- /dev/null +++ b/libdm/libdm-report.c @@ -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 + +/* + * 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 index 0000000..365e7ec --- /dev/null +++ b/libdm/libdm-string.c @@ -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 + +/* + * 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 index 0000000..fb11b5c --- /dev/null +++ b/libdm/misc/dm-ioctl.h @@ -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 +#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 index 0000000..7cacb18 --- /dev/null +++ b/libdm/misc/dm-log-userspace.h @@ -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 index 0000000..13ab804 --- /dev/null +++ b/libdm/misc/dm-logging.h @@ -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 index 0000000..9293b7c --- /dev/null +++ b/libdm/misc/dmlib.h @@ -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 index 0000000..5fcda74 --- /dev/null +++ b/libdm/misc/kdev_t.h @@ -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 index 0000000..db7f5f3 --- /dev/null +++ b/libdm/mm/dbg_malloc.c @@ -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 +#include + +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 index 0000000..6de1020 --- /dev/null +++ b/libdm/mm/dbg_malloc.h @@ -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 +#include + +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 index 0000000..7fde3a6 --- /dev/null +++ b/libdm/mm/pool-debug.c @@ -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 + +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 index 0000000..ebd982e --- /dev/null +++ b/libdm/mm/pool-fast.c @@ -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 index 0000000..35bfffa --- /dev/null +++ b/libdm/mm/pool.c @@ -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 index 0000000..9590865 --- /dev/null +++ b/libdm/regex/matcher.c @@ -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 index 0000000..ea3922f --- /dev/null +++ b/libdm/regex/parse_rx.c @@ -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 + +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 a)) (... (cat b))) + * and want to turn it into (cat (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 index 0000000..37d7ced --- /dev/null +++ b/libdm/regex/parse_rx.h @@ -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 index 0000000..ec97c98 --- /dev/null +++ b/libdm/regex/ttree.c @@ -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 index 0000000..b4e3ba2 --- /dev/null +++ b/libdm/regex/ttree.h @@ -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 index 0000000..e69de29 diff --git a/liblvm/Doxyfile b/liblvm/Doxyfile new file mode 100644 index 0000000..3d2e8e4 --- /dev/null +++ b/liblvm/Doxyfile @@ -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 index 0000000..ec9522b --- /dev/null +++ b/liblvm/Makefile.in @@ -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)/$( $@ + +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 index 0000000..4c71c36 --- /dev/null +++ b/liblvm/liblvm2app.pc.in @@ -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 index 0000000..317911d --- /dev/null +++ b/liblvm/lvm2app.h @@ -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 + +#include + +#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 index 0000000..9e1a8ec --- /dev/null +++ b/liblvm/lvm_base.c @@ -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 index 0000000..b77f78c --- /dev/null +++ b/liblvm/lvm_lv.c @@ -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 index 0000000..62fef61 --- /dev/null +++ b/liblvm/lvm_misc.c @@ -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 index 0000000..a0324b8 --- /dev/null +++ b/liblvm/lvm_misc.h @@ -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 index 0000000..4fe2d3e --- /dev/null +++ b/liblvm/lvm_pv.c @@ -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 index 0000000..f087ba4 --- /dev/null +++ b/liblvm/lvm_vg.c @@ -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 index 0000000..806f77a --- /dev/null +++ b/make.tmpl.in @@ -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 $( $@ + +.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 index 0000000..2506fbd --- /dev/null +++ b/man/Makefile.in @@ -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 index 0000000..53a9113 --- /dev/null +++ b/man/clvmd.8.in @@ -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 []] [\-C] [\-h] +[\-R] +[\-S] +[\-t ] +[\-T ] +[\-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 [] +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 +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 +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 index 0000000..035fa43 --- /dev/null +++ b/man/cmirrord.8.in @@ -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 index 0000000..cc5a741 --- /dev/null +++ b/man/dmeventd.8.in @@ -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 index 0000000..68e9819 --- /dev/null +++ b/man/dmsetup.8.in @@ -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_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_file] +.br +.B dmsetup clear +.I device_name +.br +.B dmsetup reload +.I device_name [--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\ [+]|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\
+.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_file] +.br +Creates a device with the given name. +If table_file or
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/. +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_file] +.br +Loads
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_='1', DM_UDEV_FLAG='1' otherwise. +Subsystem udev flags don't have symbolic names assigned and these ones are +always reported as DM_SUBSYSTEM_UDEV_FLAG='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 index 0000000..eeec2d3 --- /dev/null +++ b/man/fsadm.8.in @@ -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 index 0000000..e269802 --- /dev/null +++ b/man/lvchange.8.in @@ -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 index 0000000..8e48793 --- /dev/null +++ b/man/lvconvert.8.in @@ -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 index 0000000..0d0976f --- /dev/null +++ b/man/lvcreate.8.in @@ -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 index 0000000..477e934 --- /dev/null +++ b/man/lvdisplay.8.in @@ -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 index 0000000..0b273ab --- /dev/null +++ b/man/lvextend.8.in @@ -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 index 0000000..37ebdab --- /dev/null +++ b/man/lvm.8.in @@ -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 index 0000000..ac16b1d --- /dev/null +++ b/man/lvm.conf.5.in @@ -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\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 index 0000000..7c7c85a --- /dev/null +++ b/man/lvmchange.8.in @@ -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 index 0000000..fb3d610 --- /dev/null +++ b/man/lvmconf.8.in @@ -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 ] +[\-\-lockinglib ] +[\-\-lockinglibdir ] + +.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\fR +Apply the changes to \fBconfigfile\fR instead of the default \fB#DEFAULT_SYS_DIR#/lvm.conf\fR. +.TP +.BR \-\-lockinglib " " \fI\fR +Set external \fBlocking_library\fR locking library to load if an external locking type is used. +.TP +.BR \-\-lockinglibdir " " \fI\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 index 0000000..da4188b --- /dev/null +++ b/man/lvmdiskscan.8.in @@ -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 index 0000000..9cce137 --- /dev/null +++ b/man/lvmdump.8.in @@ -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 index 0000000..b78ba6e --- /dev/null +++ b/man/lvmsadc.8.in @@ -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 index 0000000..0919545 --- /dev/null +++ b/man/lvmsar.8.in @@ -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 index 0000000..fc77223 --- /dev/null +++ b/man/lvreduce.8.in @@ -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 index 0000000..ac39cb4 --- /dev/null +++ b/man/lvremove.8.in @@ -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 index 0000000..8f90072 --- /dev/null +++ b/man/lvrename.8.in @@ -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 index 0000000..677b2db --- /dev/null +++ b/man/lvresize.8.in @@ -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 index 0000000..baa14ab --- /dev/null +++ b/man/lvs.8.in @@ -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 index 0000000..3dbe9d8 --- /dev/null +++ b/man/lvscan.8.in @@ -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 index 0000000..1bf6a4f --- /dev/null +++ b/man/pvchange.8.in @@ -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 index 0000000..de3dfac --- /dev/null +++ b/man/pvck.8.in @@ -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 index 0000000..cb10049 --- /dev/null +++ b/man/pvcreate.8.in @@ -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 index 0000000..efa3894 --- /dev/null +++ b/man/pvdisplay.8.in @@ -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 index 0000000..83db231 --- /dev/null +++ b/man/pvmove.8.in @@ -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 index 0000000..b435fc7 --- /dev/null +++ b/man/pvremove.8.in @@ -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 index 0000000..845f6d3 --- /dev/null +++ b/man/pvresize.8.in @@ -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 index 0000000..a5e1382 --- /dev/null +++ b/man/pvs.8.in @@ -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 index 0000000..4a6bcd7 --- /dev/null +++ b/man/pvscan.8.in @@ -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 index 0000000..1a72a01 --- /dev/null +++ b/man/vgcfgbackup.8.in @@ -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 index 0000000..c62c813 --- /dev/null +++ b/man/vgcfgrestore.8.in @@ -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 index 0000000..b4313d4 --- /dev/null +++ b/man/vgchange.8.in @@ -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 index 0000000..2e5d926 --- /dev/null +++ b/man/vgck.8.in @@ -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 index 0000000..14e95f4 --- /dev/null +++ b/man/vgconvert.8.in @@ -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 index 0000000..5ac5e2a --- /dev/null +++ b/man/vgcreate.8.in @@ -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 index 0000000..96dfe8c --- /dev/null +++ b/man/vgdisplay.8.in @@ -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 index 0000000..27ce577 --- /dev/null +++ b/man/vgexport.8.in @@ -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 index 0000000..4ae2f08 --- /dev/null +++ b/man/vgextend.8.in @@ -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 index 0000000..ede6394 --- /dev/null +++ b/man/vgimport.8.in @@ -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 index 0000000..7b84df4 --- /dev/null +++ b/man/vgimportclone.8.in @@ -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 index 0000000..bf1738b --- /dev/null +++ b/man/vgmerge.8.in @@ -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 index 0000000..49f6a9d --- /dev/null +++ b/man/vgmknodes.8.in @@ -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 index 0000000..f4f5d83 --- /dev/null +++ b/man/vgreduce.8.in @@ -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 index 0000000..0ec6510 --- /dev/null +++ b/man/vgremove.8.in @@ -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 index 0000000..3fabbbb --- /dev/null +++ b/man/vgrename.8.in @@ -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 index 0000000..a1a97df --- /dev/null +++ b/man/vgs.8.in @@ -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 index 0000000..cc46269 --- /dev/null +++ b/man/vgscan.8.in @@ -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 index 0000000..0c6ba22 --- /dev/null +++ b/man/vgsplit.8.in @@ -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 index 0000000..2ebfe64 --- /dev/null +++ b/packaging/device-mapper.changes @@ -0,0 +1,14 @@ +* Wed Jan 05 2011 Paolo Capriotti - 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 1.02.28 +- Fix Source: to be a resolvable URL + +* Tue Dec 09 2008 Yi Yang 1.02.28 +- Upgrade to 1.02.28 +- Fixed corrupt primary.xml.gz introduced by libdevmapper.pc + +* Sun Nov 02 2008 Anas Nashif 1.02.24 +- fixed file list diff --git a/packaging/device-mapper.spec b/packaging/device-mapper.spec new file mode 100644 index 0000000..46f95e9 --- /dev/null +++ b/packaging/device-mapper.spec @@ -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 index 0000000..a4f57a6 --- /dev/null +++ b/po/Makefile.in @@ -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 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 \n" +"Language-Team: LANGUAGE \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 index 0000000..2b236ae --- /dev/null +++ b/po/lvm2.po @@ -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 , 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 \n" +"Language-Team: LANGUAGE \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 "" +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 ' 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 index 0000000..66940b9 --- /dev/null +++ b/po/pogen.h @@ -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 index 0000000..c71d877 --- /dev/null +++ b/report-generators/lib/log.rb @@ -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 index 0000000..4c0bc78 --- /dev/null +++ b/report-generators/lib/report_templates.rb @@ -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 index 0000000..c61bb20 --- /dev/null +++ b/report-generators/lib/reports.rb @@ -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 index 0000000..6a339bc --- /dev/null +++ b/report-generators/lib/schedule_file.rb @@ -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 index 0000000..b3b8cc9 --- /dev/null +++ b/report-generators/lib/string-store.rb @@ -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 index 0000000..e616bc8 --- /dev/null +++ b/report-generators/memcheck.rb @@ -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 index 0000000..23f01cb --- /dev/null +++ b/report-generators/templates/boiler_plate.rhtml @@ -0,0 +1,25 @@ + + + +<%= title %> + + + + + +
+
+
+ + + +
Generation times
Unit tests
Memory tests
+ + +
+ <%= body %> +
+ + diff --git a/report-generators/templates/index.rhtml b/report-generators/templates/index.rhtml new file mode 100644 index 0000000..6d72081 --- /dev/null +++ b/report-generators/templates/index.rhtml @@ -0,0 +1,17 @@ + + +<% [:unit_test, :memcheck].each do |sym| %> +<% r = reports.get_report(sym) %> + + + + +<% end %> +
ReportGeneration time
+ <% if r.path.file? %> + <%= r.short_desc %> + <% else %> + <%= r.short_desc %> + <% end %> + <%= safe_mtime(r) %>
+ diff --git a/report-generators/templates/memcheck.rhtml b/report-generators/templates/memcheck.rhtml new file mode 100644 index 0000000..75872ed --- /dev/null +++ b/report-generators/templates/memcheck.rhtml @@ -0,0 +1,30 @@ + + + +
Tests passedTests failed
<%= total_passed %>><%= total_failed %>
+ +<% schedules.each do |s| %> +

<%= s.dir.sub('./unit-tests/', '') %>

+ + + +<% s.schedules.each do |t| %> + + + <% if t.status.success? %> + + <% else %> + + <% end %> + + <% stats = extract_stats(t) %> + + + + + +<% end %> +
TestResultDefinitely lostindirectly lostpossibly loststill reachable
+ <%= t.desc %> + passfail<%= stats.definitely_lost %><%= stats.indirectly_lost %><%= stats.possibly_lost %><%= stats.reachable %>
+<% end %> diff --git a/report-generators/templates/unit_detail.rhtml b/report-generators/templates/unit_detail.rhtml new file mode 100644 index 0000000..5324f07 --- /dev/null +++ b/report-generators/templates/unit_detail.rhtml @@ -0,0 +1,37 @@ + + + + + <% if t.status.success? %> + + <% else %> + + <% end %> + +
TestResult
+ <%= t.desc %> + passfail
+ + + + + + +
Command line
+
+<%= t.command_line %>
+  
+
+ + + + + + + +
Output
+
+<%= t.output %>
+  
+
+ diff --git a/report-generators/templates/unit_test.rhtml b/report-generators/templates/unit_test.rhtml new file mode 100644 index 0000000..3137abd --- /dev/null +++ b/report-generators/templates/unit_test.rhtml @@ -0,0 +1,23 @@ + + + +
Tests passedTests failed
<%= total_passed %>><%= total_failed %>
+ +<% schedules.each do |s| %> +

<%= s.dir.sub('./unit-tests/', '') %>

+ + +<% s.schedules.each do |t| %> + + + <% if t.status.success? %> + + <% else %> + + <% end %> + +<% end %> +
TestResult
+ <%= t.desc %> + passfail
+<% end %> diff --git a/report-generators/test/example.schedule b/report-generators/test/example.schedule new file mode 100644 index 0000000..f617187 --- /dev/null +++ b/report-generators/test/example.schedule @@ -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 index 0000000..3e9ffe0 --- /dev/null +++ b/report-generators/test/strings/more_strings/test3.txt @@ -0,0 +1 @@ +lorem diff --git a/report-generators/test/strings/test1.txt b/report-generators/test/strings/test1.txt new file mode 100644 index 0000000..af5626b --- /dev/null +++ b/report-generators/test/strings/test1.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/report-generators/test/strings/test2 b/report-generators/test/strings/test2 new file mode 100644 index 0000000..54d55bf --- /dev/null +++ b/report-generators/test/strings/test2 @@ -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 index 0000000..96ce7c0 --- /dev/null +++ b/report-generators/test/tc_log.rb @@ -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 index 0000000..70aefdd --- /dev/null +++ b/report-generators/test/tc_schedule_file.rb @@ -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 index 0000000..05c6719 --- /dev/null +++ b/report-generators/test/tc_string_store.rb @@ -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 index 0000000..4e99e68 --- /dev/null +++ b/report-generators/test/ts.rb @@ -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 index 0000000..1c1cd1d --- /dev/null +++ b/report-generators/title_page.rb @@ -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 index 0000000..1e590bc --- /dev/null +++ b/report-generators/unit_test.rb @@ -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 index 0000000..3d41926 --- /dev/null +++ b/reports/stylesheet.css @@ -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 index 0000000..5293cc3 --- /dev/null +++ b/scripts/Makefile.in @@ -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 $( + + +1.0 + + +Resource script for an LVM Volume Group. + +Controls the availability of an LVM Volume Group + + + + +The name of volume group. + +Volume group name + + + + +If set, the volume group will be activated exclusively. + +Exclusive activation + + + + + + + + + + + + + + +EOF +} + +# +# methods: What methods/operations do we support? +# +VolumeGroup_methods() { + cat <&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 index 0000000..cc2c50d --- /dev/null +++ b/scripts/clvmd_fix_conf.sh @@ -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 [] []" + echo "" + echo "|UNDO location of the cluster locking shared library. (no default)" + echo " UNDO will reset the locking back to local" + echo " name of the LVM config file (default: /etc/lvm/lvm.conf)" + echo " 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 - < $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 index 0000000..3fc90c5 --- /dev/null +++ b/scripts/clvmd_init_red_hat.in @@ -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 index 0000000..ab3b7fb --- /dev/null +++ b/scripts/cmirrord_init_red_hat.in @@ -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 index 0000000..c59dcb6 --- /dev/null +++ b/scripts/fsadm.sh @@ -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 +# +# 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
&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 index 0000000..97b77c1 --- /dev/null +++ b/scripts/last_cvs_update.sh @@ -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 +## +## 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 index 0000000..4812c75 --- /dev/null +++ b/scripts/lvm2_monitoring_init_red_hat.in @@ -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 index 0000000..0a42f55 --- /dev/null +++ b/scripts/lvm2_monitoring_init_rhel4 @@ -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 index 0000000..acb189d --- /dev/null +++ b/scripts/lvm2create_initrd/Makefile @@ -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 index 0000000..ff5e9de --- /dev/null +++ b/scripts/lvm2create_initrd/README @@ -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 diff --git a/scripts/lvm2create_initrd/lvm2create_initrd b/scripts/lvm2create_initrd/lvm2create_initrd new file mode 100644 index 0000000..9d012d5 --- /dev/null +++ b/scripts/lvm2create_initrd/lvm2create_initrd @@ -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 " + +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 + 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 index 0000000..c9dae9f --- /dev/null +++ b/scripts/lvm2create_initrd/lvm2create_initrd.8 @@ -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 index 0000000..577930f --- /dev/null +++ b/scripts/lvm2create_initrd/lvm2create_initrd.pod @@ -0,0 +1,187 @@ +=head1 NAME + +lvm2create_initrd - create initrd image for booting to root\-on\-LVM2 + +=head1 SYNOPSIS + +B [ B<-h|--help> ] [ B<-v|--verbose> ] [ B<-c|--lvmconf> I ] [ B<-m|--modules> "I" ] [ B<-e|--extra> "I" ] [ B<-r|--raid> "I" ] +[ B<-R|--raidconf> I ] [ B<-M|--makedev> I