mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			48 Commits
		
	
	
		
			dev-dct-pv
			...
			dev-dct-cm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 124b515d9f | ||
|  | e00ebdc934 | ||
|  | 60d26a4e42 | ||
|  | dce38761ac | ||
|  | 14cc164262 | ||
|  | 0d929d5a3d | ||
|  | c8945374d5 | ||
|  | 1f67df932d | ||
|  | 1bef87796f | ||
|  | cfbbb4bee9 | ||
|  | 058b53cdf4 | ||
|  | 2b8019fd43 | ||
|  | c213aa76c4 | ||
|  | 15e69fae19 | ||
|  | fe0ad1157d | ||
|  | 88a2b1dc37 | ||
|  | 03970369c5 | ||
|  | c2147bda74 | ||
|  | a61bc922a9 | ||
|  | d41a2e6f30 | ||
|  | 55d6e9ebb7 | ||
|  | 0a61ea395f | ||
|  | e61f84cd1f | ||
|  | ca8e344041 | ||
|  | 14838f584f | ||
|  | 6033e894e2 | ||
|  | 9b4c2027bf | ||
|  | 61e0925844 | ||
|  | 214f48aa96 | ||
|  | 29ee87987c | ||
|  | 2391324196 | ||
|  | e8230719ee | ||
|  | 2c5f6b8a02 | ||
|  | fe3b2f5ead | ||
|  | 91a1273d56 | ||
|  | 5507307b5f | ||
|  | 91b71a8b02 | ||
|  | 3057fe9fec | ||
|  | 258027d1b1 | ||
|  | 2da0f6110d | ||
|  | 09d6229350 | ||
|  | 6312e0872c | ||
|  | c752f4e1eb | ||
|  | cd36b1c698 | ||
|  | 310d9a09fd | ||
|  | 81e84556b5 | ||
|  | c903d0e4b3 | ||
|  | 2da7b10988 | 
							
								
								
									
										110
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										110
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| *.5 | ||||
| *.7 | ||||
| *.8 | ||||
| *.8_gen | ||||
| *.a | ||||
| *.d | ||||
| *.o | ||||
| @@ -25,118 +24,9 @@ make.tmpl | ||||
|  | ||||
| /autom4te.cache/ | ||||
| /autoscan.log | ||||
| /build/ | ||||
| /config.cache | ||||
| /config.log | ||||
| /config.status | ||||
| /configure.scan | ||||
| /cscope.out | ||||
| /html/ | ||||
| /reports/ | ||||
| /tags | ||||
| /tmp/ | ||||
|  | ||||
| coverity/coverity_model.xml | ||||
|  | ||||
| tools/man-generator | ||||
| tools/man-generator.c | ||||
|  | ||||
| test/.lib-dir-stamp | ||||
| test/.tests-stamp | ||||
| test/lib/dmsecuretest | ||||
| test/lib/lvchange | ||||
| test/lib/lvconvert | ||||
| test/lib/lvcreate | ||||
| test/lib/lvdisplay | ||||
| test/lib/lvextend | ||||
| test/lib/lvmconfig | ||||
| test/lib/lvmdiskscan | ||||
| test/lib/lvmsadc | ||||
| test/lib/lvmsar | ||||
| test/lib/lvreduce | ||||
| test/lib/lvremove | ||||
| test/lib/lvrename | ||||
| test/lib/lvresize | ||||
| test/lib/lvs | ||||
| test/lib/lvscan | ||||
| test/lib/pvchange | ||||
| test/lib/pvck | ||||
| test/lib/pvcreate | ||||
| test/lib/pvdisplay | ||||
| test/lib/pvmove | ||||
| test/lib/pvremove | ||||
| test/lib/pvresize | ||||
| test/lib/pvs | ||||
| test/lib/pvscan | ||||
| test/lib/securetest | ||||
| test/lib/vgcfgbackup | ||||
| test/lib/vgcfgrestore | ||||
| test/lib/vgchange | ||||
| test/lib/vgck | ||||
| test/lib/vgconvert | ||||
| test/lib/vgcreate | ||||
| test/lib/vgdisplay | ||||
| test/lib/vgexport | ||||
| test/lib/vgextend | ||||
| test/lib/vgimport | ||||
| test/lib/vgimportclone | ||||
| test/lib/vgmerge | ||||
| test/lib/vgmknodes | ||||
| test/lib/vgreduce | ||||
| test/lib/vgremove | ||||
| test/lib/vgrename | ||||
| test/lib/vgs | ||||
| test/lib/vgscan | ||||
| test/lib/vgsplit | ||||
| test/api/lvtest.t | ||||
| test/api/pe_start.t | ||||
| test/api/percent.t | ||||
| test/api/python_lvm_unit.py | ||||
| test/api/test | ||||
| test/api/thin_percent.t | ||||
| test/api/vglist.t | ||||
| test/api/vgtest.t | ||||
| test/lib/aux | ||||
| test/lib/check | ||||
| test/lib/clvmd | ||||
| test/lib/dm-version-expected | ||||
| test/lib/dmeventd | ||||
| test/lib/dmsetup | ||||
| test/lib/dmstats | ||||
| test/lib/fail | ||||
| test/lib/flavour-ndev-cluster | ||||
| test/lib/flavour-ndev-cluster-lvmpolld | ||||
| test/lib/flavour-ndev-lvmetad | ||||
| test/lib/flavour-ndev-lvmetad-lvmpolld | ||||
| test/lib/flavour-ndev-lvmpolld | ||||
| test/lib/flavour-ndev-vanilla | ||||
| test/lib/flavour-udev-cluster | ||||
| test/lib/flavour-udev-cluster-lvmpolld | ||||
| test/lib/flavour-udev-lvmetad | ||||
| test/lib/flavour-udev-lvmetad-lvmpolld | ||||
| test/lib/flavour-udev-lvmlockd-dlm | ||||
| test/lib/flavour-udev-lvmlockd-sanlock | ||||
| test/lib/flavour-udev-lvmlockd-test | ||||
| test/lib/flavour-udev-lvmpolld | ||||
| test/lib/flavour-udev-vanilla | ||||
| test/lib/fsadm | ||||
| test/lib/get | ||||
| test/lib/inittest | ||||
| test/lib/invalid | ||||
| test/lib/lvm | ||||
| test/lib/lvm-wrapper | ||||
| test/lib/lvmchange | ||||
| test/lib/lvmdbusd.profile | ||||
| test/lib/lvmetad | ||||
| test/lib/lvmpolld | ||||
| test/lib/not | ||||
| test/lib/paths | ||||
| test/lib/paths-common | ||||
| test/lib/runner | ||||
| test/lib/should | ||||
| test/lib/test | ||||
| test/lib/thin-performance.profile | ||||
| test/lib/utils | ||||
| test/lib/version-expected | ||||
| test/unit/dmraid_t.c | ||||
| test/unit/unit-test | ||||
|   | ||||
							
								
								
									
										25
									
								
								COPYING.BSD
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								COPYING.BSD
									
									
									
									
									
								
							| @@ -1,25 +0,0 @@ | ||||
| BSD 2-Clause License | ||||
|  | ||||
| Copyright (c) 2014, Red Hat, Inc. | ||||
| All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|  | ||||
| 1. Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
|  | ||||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|    this list of conditions and the following disclaimer in the documentation | ||||
|    and/or other materials provided with the distribution. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										111
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								Makefile.in
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # | ||||
| # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| @@ -28,6 +28,14 @@ ifeq ("@INTL@", "yes") | ||||
|   SUBDIRS += po | ||||
| endif | ||||
|  | ||||
| ifeq ("@APPLIB@", "yes") | ||||
|   SUBDIRS += liblvm | ||||
| endif | ||||
|  | ||||
| ifeq ("@PYTHON_BINDINGS@", "yes") | ||||
|   SUBDIRS += python | ||||
| endif | ||||
|  | ||||
| ifeq ($(MAKECMDGOALS),clean) | ||||
|   SUBDIRS += test | ||||
| endif | ||||
| @@ -35,7 +43,8 @@ endif | ||||
| ifeq ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = conf include man test scripts \ | ||||
|     libdaemon lib tools daemons libdm \ | ||||
|     udev po | ||||
|     udev po liblvm python \ | ||||
|     unit-tests/datastruct unit-tests/mm unit-tests/regex | ||||
| tools.distclean: test.distclean | ||||
| endif | ||||
| DISTCLEAN_DIRS += lcov_reports* | ||||
| @@ -43,25 +52,18 @@ DISTCLEAN_TARGETS += config.cache config.log config.status make.tmpl | ||||
|  | ||||
| include make.tmpl | ||||
|  | ||||
| include $(top_srcdir)/base/Makefile | ||||
| include $(top_srcdir)/device_mapper/Makefile | ||||
| include $(top_srcdir)/test/unit/Makefile | ||||
|  | ||||
| libdm: include | ||||
| libdaemon: include | ||||
| lib: libdaemon $(BASE_TARGET) $(DEVICE_MAPPER_TARGET) | ||||
| lib: libdm libdaemon | ||||
| liblvm: lib | ||||
| daemons: lib libdaemon tools | ||||
| scripts: lib | ||||
| tools: lib libdaemon | ||||
| tools: lib libdaemon device-mapper | ||||
| po: tools daemons | ||||
| man: tools | ||||
| all_man: tools | ||||
| scripts: libdm | ||||
| test: tools daemons | ||||
| unit-test  run-unit-test: test | ||||
| scripts: liblvm libdm | ||||
|  | ||||
| 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 | ||||
| scripts.device-mapper: include.device-mapper | ||||
| @@ -75,6 +77,10 @@ po.pofile: tools.pofile daemons.pofile | ||||
| pofile: po.pofile | ||||
| endif | ||||
|  | ||||
| ifeq ("@PYTHON_BINDINGS@", "yes") | ||||
| python: liblvm | ||||
| endif | ||||
|  | ||||
| ifneq ("$(CFLOW_CMD)", "") | ||||
| tools.cflow: libdm.cflow lib.cflow | ||||
| daemons.cflow: tools.cflow | ||||
| @@ -89,10 +95,10 @@ endif | ||||
| DISTCLEAN_TARGETS += cscope.out | ||||
| CLEAN_DIRS += autom4te.cache | ||||
|  | ||||
| check check_system check_cluster check_local check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock: test | ||||
| check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all | ||||
| 	$(MAKE) -C test $(@) | ||||
|  | ||||
| conf.generate man.generate: tools | ||||
| conf.generate: tools | ||||
|  | ||||
| # how to use parenthesis in makefiles | ||||
| leftparen:=( | ||||
| @@ -122,9 +128,8 @@ rpm: dist | ||||
| 	    $(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc | ||||
| 	rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec | ||||
|  | ||||
| generate: conf.generate man.generate | ||||
| generate: conf.generate | ||||
| 	$(MAKE) -C conf generate | ||||
| 	$(MAKE) -C man generate | ||||
|  | ||||
| all_man: | ||||
| 	$(MAKE) -C man all_man | ||||
| @@ -138,7 +143,7 @@ install_system_dirs: | ||||
| 	$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR) | ||||
| 	$(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache | ||||
|  | ||||
| install_initscripts: | ||||
| install_initscripts:  | ||||
| 	$(MAKE) -C scripts install_initscripts | ||||
|  | ||||
| install_systemd_generators: | ||||
| @@ -151,11 +156,26 @@ install_systemd_units: | ||||
| install_all_man: | ||||
| 	$(MAKE) -C man install_all_man | ||||
|  | ||||
| ifeq ("@PYTHON_BINDINGS@", "yes") | ||||
| install_python_bindings: | ||||
| 	$(MAKE) -C liblvm/python install_python_bindings | ||||
| endif | ||||
|  | ||||
| install_tmpfiles_configuration: | ||||
| 	$(MAKE) -C scripts install_tmpfiles_configuration | ||||
|  | ||||
| LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \ | ||||
| 	libdaemon/client.info libdaemon/server.info \ | ||||
| 	daemons/clvmd.info \ | ||||
| 	daemons/dmeventd.info \ | ||||
| 	daemons/lvmetad.info \ | ||||
| 	daemons/lvmlockd.info \ | ||||
| 	daemons/lvmpolld.info | ||||
|  | ||||
| CLEAN_TARGETS += $(LCOV_TRACES) | ||||
|  | ||||
| ifneq ("$(LCOV)", "") | ||||
| .PHONY: lcov-reset lcov lcov-dated | ||||
| .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) | ||||
| @@ -165,22 +185,55 @@ LCOV_REPORTS_DIR := lcov_reports | ||||
| endif | ||||
|  | ||||
| lcov-reset: | ||||
| 	$(LCOV) --zerocounters --directory $(top_builddir) | ||||
| 	$(LCOV) --zerocounters $(addprefix -d , $(basename $(LCOV_TRACES))) | ||||
|  | ||||
| # maybe use subdirs processing to create tracefiles... | ||||
| $(LCOV_TRACES): | ||||
| 	$(LCOV) -b $(basename $@) -d $(basename $@) \ | ||||
| 		--ignore-errors source -c -o - | $(SED) \ | ||||
| 		-e "s/\(dmeventd_lvm.[ch]\)/plugins\/lvm2\/\1/" \ | ||||
| 		-e "s/dmeventd_\(mirror\|snapshot\|thin\|raid\)\.c/plugins\/\1\/dmeventd_\1\.c/" \ | ||||
| 		>$@ | ||||
|  | ||||
| ifneq ("$(GENHTML)", "") | ||||
| lcov: | ||||
| 	$(RM) -rf $(LCOV_REPORTS_DIR) | ||||
| lcov: $(LCOV_TRACES) | ||||
| 	$(RM) -r $(LCOV_REPORTS_DIR) | ||||
| 	$(MKDIR_P) $(LCOV_REPORTS_DIR) | ||||
| 	$(LCOV) --capture --directory $(top_builddir) --ignore-errors source \ | ||||
| 		--output-file $(LCOV_REPORTS_DIR)/out.info | ||||
| 	-test ! -s $(LCOV_REPORTS_DIR)/out.info || \ | ||||
| 		$(GENHTML) -o $(LCOV_REPORTS_DIR) --ignore-errors source \ | ||||
| 		$(LCOV_REPORTS_DIR)/out.info | ||||
| 	for i in $(LCOV_TRACES); do \ | ||||
| 		test -s $$i -a $$(wc -w <$$i) -ge 100 && lc="$$lc $$i"; \ | ||||
| 	done; \ | ||||
| 	test -z "$$lc" || $(GENHTML) -p @abs_top_builddir@ \ | ||||
| 		-o $(LCOV_REPORTS_DIR) $$lc | ||||
| endif | ||||
|  | ||||
| endif | ||||
|  | ||||
| ifneq ($(shell which ctags 2>/dev/null),) | ||||
| ifeq ("$(TESTING)", "yes") | ||||
| # testing and report generation | ||||
| RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test | ||||
|  | ||||
| .PHONY: unit-test ruby-test test-programs | ||||
|  | ||||
| # FIXME: put dependencies on libdm and liblvm | ||||
| # FIXME: Should be handled by Makefiles in subdirs, not here at top level. | ||||
| 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 | ||||
|  | ||||
| ifneq ($(shell which ctags),) | ||||
| .PHONY: tags | ||||
| tags: | ||||
| 	test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags 2>/dev/null | head -1)" || $(RM) tags | ||||
|   | ||||
							
								
								
									
										21
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README
									
									
									
									
									
								
							| @@ -1,25 +1,16 @@ | ||||
| This tree contains the LVM2 and device-mapper tools and libraries. | ||||
|  | ||||
| This is development branch, for stable 2.02 release see 2018-06-01-stable | ||||
| branch. | ||||
|  | ||||
| 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://sourceware.org/pub/lvm2/ | ||||
|   ftp://sources.redhat.com/pub/lvm2/ | ||||
|   https://github.com/lvmteam/lvm2/releases | ||||
|  | ||||
| The source code is stored in git: | ||||
|   https://sourceware.org/git/?p=lvm2.git | ||||
|   git clone git://sourceware.org/git/lvm2.git | ||||
| mirrored to: | ||||
|   https://github.com/lvmteam/lvm2 | ||||
|   git clone https://github.com/lvmteam/lvm2.git | ||||
|   git clone git@github.com:lvmteam/lvm2.git | ||||
|   http://git.fedorahosted.org/git/lvm2.git | ||||
|   git clone git://git.fedorahosted.org/git/lvm2.git | ||||
|  | ||||
| Mailing list for general discussion related to LVM2: | ||||
|   linux-lvm@redhat.com | ||||
| @@ -37,14 +28,6 @@ and multipath-tools: | ||||
|   dm-devel@redhat.com | ||||
|   Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel | ||||
|  | ||||
| Website: | ||||
|   https://sourceware.org/lvm2/ | ||||
|  | ||||
| Report upstream bugs at: | ||||
|   https://bugzilla.redhat.com/enter_bug.cgi?product=LVM%20and%20device-mapper | ||||
| or open issues at: | ||||
|   https://github.com/lvmteam/lvm2/issues | ||||
|  | ||||
| The source code repository used until 7th June 2012 is accessible here: | ||||
|   http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2. | ||||
|  | ||||
|   | ||||
							
								
								
									
										62
									
								
								TESTING
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								TESTING
									
									
									
									
									
								
							| @@ -1,62 +0,0 @@ | ||||
| LVM2 Test Suite | ||||
| =============== | ||||
|  | ||||
| The codebase contains many tests in the test subdirectory. | ||||
|  | ||||
| Before running tests | ||||
| -------------------- | ||||
|  | ||||
| Keep in mind the testsuite MUST run under root user. | ||||
|  | ||||
| It is recommended not to use LVM on the test machine, especially when running | ||||
| tests with udev (`make check_system`.) | ||||
|  | ||||
| You MUST disable (or mask) any LVM daemons: | ||||
|  | ||||
| - lvmetad | ||||
| - dmeventd | ||||
| - lvmpolld | ||||
| - lvmdbusd | ||||
| - lvmlockd | ||||
| - clvmd | ||||
| - cmirrord | ||||
|  | ||||
| For running cluster tests, we are using singlenode locking. Pass | ||||
| `--with-clvmd=singlenode` to configure. | ||||
|  | ||||
| NOTE: This is useful only for testing, and should not be used in produciton | ||||
| code. | ||||
|  | ||||
| To run D-Bus daemon tests, existing D-Bus session is required. | ||||
|  | ||||
| Running tests | ||||
| ------------- | ||||
|  | ||||
| As root run: | ||||
|  | ||||
|     make check | ||||
|  | ||||
| To run only tests matching a string: | ||||
|  | ||||
|     make check T=test | ||||
|  | ||||
| To skip tests matching a string: | ||||
|  | ||||
|     make check S=test | ||||
|  | ||||
| There are other targets and many environment variables can be used to tweak the | ||||
| testsuite - for full list and description run `make -C test help`. | ||||
|  | ||||
| Installing testsuite | ||||
| -------------------- | ||||
|  | ||||
| It is possible to install and run a testsuite against installed LVM. Run the | ||||
| following: | ||||
|  | ||||
|     make -C test install | ||||
|  | ||||
| Then lvm2-testsuite binary can be executed to test installed binaries. | ||||
|  | ||||
| See `lvm2-testsuite --help` for options. The same environment variables can be | ||||
| used as with `make check`. | ||||
|  | ||||
| @@ -1 +1 @@ | ||||
| 1.02.165-git (2019-06-15) | ||||
| 1.02.138-git (2016-11-30) | ||||
|   | ||||
							
								
								
									
										391
									
								
								WHATS_NEW
									
									
									
									
									
								
							
							
						
						
									
										391
									
								
								WHATS_NEW
									
									
									
									
									
								
							| @@ -1,393 +1,6 @@ | ||||
| Version 2.03.06 -  | ||||
| ================================ | ||||
|   Improve internal removal of cached devices. | ||||
|   Synchronize with udev when dropping snapshot. | ||||
|   Add missing device synchronization point before removing pvmove node. | ||||
|   Correctly set read_ahead for LVs when pvmove is finished. | ||||
|   Remove unsupported OPTIONS+="event_timeout" udev rule from 11-dm-lvm.rules. | ||||
|   Prevent creating VGs with PVs with different logical block sizes. | ||||
|   Fix metadata writes from corrupting with large physical block size. | ||||
|  | ||||
| Version 2.03.05 - 15th June 2019 | ||||
| ================================ | ||||
|   Fix command definition for pvchange -a. | ||||
|   Add vgck --updatemetadata command that will repair metadata problems. | ||||
|   Improve VG reading to work if one good copy of metadata is found. | ||||
|   Report/display/scan commands that read VGs will no longer write/repair. | ||||
|   Move metadata repairs from VG reading to VG writing. | ||||
|   Add config setting md_component_checks to control MD component checks. | ||||
|   Add end of device MD component checks when dev has no udev info. | ||||
|  | ||||
| Version 2.03.04 - 10th June 2019 | ||||
| ================================ | ||||
|   Remove unused_duplicate_devs from cmd causing segfault in dmeventd. | ||||
|  | ||||
| Version 2.03.03 - 07th June 2019 | ||||
| ================================ | ||||
|   Report no_discard_passdown for cache LVs with lvs -o+kernel_discards. | ||||
|   Add pvck --dump option to extract metadata. | ||||
|   Fix signal delivery checking race in libdaemon (lvmetad). | ||||
|   Add missing Before=shutdown.target to LVM2 services to fix shutdown ordering. | ||||
|   Skip autoactivation for a PV when PV size does not match device size. | ||||
|   Remove first-pvscan-initialization which should no longer be needed. | ||||
|   Add remote refresh through lvmlockd/dlm for shared LVs after lvextend. | ||||
|   Ignore foreign and shared PVs for pvscan online files. | ||||
|   Add config setting to control fields in debug file and verbose output. | ||||
|   Add command[pid] and timestamp to debug file and verbose output. | ||||
|   Fix missing growth of _pmsmare volume when extending _tmeta volume. | ||||
|   Automatically grow thin metadata, when thin data gets too big. | ||||
|   Add synchronization with udev before removing cached devices. | ||||
|   Add support for caching VDO LVs and VDOPOOL LVs. | ||||
|   Add support for vgsplit with cached devices. | ||||
|   Query mpath device only once per command for its state. | ||||
|   Use device INFO instead of STATUS when checking for mpath device uuid. | ||||
|   Change default io_memory_size from 4 to 8 MiB. | ||||
|   Add config setting io_memory_size to set bcache size. | ||||
|   Fix pvscan autoactivation for concurrent pvscans. | ||||
|   Change scan_lvs default to 0 so LVs are not scanned for PVs. | ||||
|   Thin-pool selects power-of-2 chunk size by default. | ||||
|   Cache selects power-of-2 chunk size by default. | ||||
|   Support reszing for VDOPoolLV and VDOLV. | ||||
|   Improve -lXXX%VG modifier which improves cache segment estimation. | ||||
|   Ensure migration_threshold for cache is at least 8 chunks. | ||||
|   Restore missing man info lvcreate --zero for thin-pools. | ||||
|   Drop misleadning comment for metadata minimum_io_size for VDO segment. | ||||
|   Add device hints to reduce scanning. | ||||
|   Introduce LVM_SUPPRESS_SYSLOG to suppress syslog usage by generator. | ||||
|   Fix generator quering lvmconfig unpresent config option. | ||||
|   Fix memleak on bcache error path code. | ||||
|   Fix missing unlock on lvm2 dmeventd plugin error path initialization. | ||||
|   Improve Makefile dependency tracking. | ||||
|   Move VDO support towards V2 target (6.2) support. | ||||
|  | ||||
| Version 2.03.02 - 18th December 2018 | ||||
| ==================================== | ||||
|   Fix missing proper initialization of pv_list struct when adding pv. | ||||
|   Fix (de)activation of RaidLVs with visible SubLVs. | ||||
|   Prohibit mirrored 'mirror' log via lvcreate and lvconvert. | ||||
|   Use sync io if async io_setup fails, or use_aio=0 is set in config. | ||||
|   Fix more issues reported by coverity scan. | ||||
|  | ||||
| Version 2.03.01 - 31st October 2018 | ||||
| =================================== | ||||
|  | ||||
| Version 2.03.00 - 10th October 2018 | ||||
| =================================== | ||||
|   Add hot fix to avoiding locking collision when monitoring thin-pools. | ||||
|   Allow raid4 -> linear conversion request. | ||||
|   Fix lvconvert striped/raid0/raid0_meta -> raid6 regression. | ||||
|   Add 'lvm2-activation-generator:' prefix for kmsg messages logged by generator. | ||||
|   Add After=rbdmap.service to {lvm2-activation-net,blk-availability}.service. | ||||
|   Reduce max concurrent aios to avoid EMFILE with many devices. | ||||
|   Fix lvconvert conversion attempts to linear. | ||||
|   Fix lvconvert raid0/raid0_meta -> striped regression. | ||||
|   Fix lvconvert --splitmirror for mirror type (2.02.178). | ||||
|   Do not pair cache policy and cache metadata format. | ||||
|   lvconvert: reject conversions on raid1 LVs with split tracked SubLVs | ||||
|   lvconvert: reject conversions on raid1 split tracked SubLVs | ||||
|   Add basic creation support for VDO target. | ||||
|   Never send any discard ioctl with test mode. | ||||
|   Fix thin-pool alloc which needs same PV for data and metadata. | ||||
|   Extend list of non-memlocked areas with newly linked libs. | ||||
|   Enhance vgcfgrestore to check for active LVs in restored VG. | ||||
|   Configure supports --disable-silent-rules for verbose builds. | ||||
|   Fix unmonitoring of merging snapshots. | ||||
|   Cache can uses metadata format 2 with cleaner policy. | ||||
|   Fix check if resized PV can also fit metadata area. | ||||
|   Avoid showing internal error in lvs output or pvmoved LVs. | ||||
|   Remove clvmd | ||||
|   Remove lvmlib (api) | ||||
|   Remove lvmetad | ||||
|   lvconvert: provide possible layouts between linear and striped/raid | ||||
|   Use versionsort to fix archive file expiry beyond 100000 files. | ||||
|  | ||||
| Version 2.02.178-rc1 - 24th May 2018 | ||||
| ==================================== | ||||
|   Add libaio dependency for build. | ||||
|   Remove lvm1 and pool format handling and add filter to ignore them. | ||||
|   Move some filter checks to after disks are read. | ||||
|   Rework disk scanning and when it is used. | ||||
|   Add new io layer and shift code to using it. | ||||
|   Fix lvconvert's return code on degraded -m raid1 conversion. | ||||
|   --enable-testing switch for ./configure has been removed. | ||||
|   --with-snapshots switch for ./configure has been removed. | ||||
|   --with-mirrors switch for ./configure has been removed. | ||||
|   --with-raid switch for ./configure has been removed. | ||||
|   --with-thin switch for ./configure has been removed. | ||||
|   --with-cache switch for ./configure has been removed. | ||||
|   Include new unit-test framework and unit tests. | ||||
|   Extend validation of region_size for mirror segment. | ||||
|   Reload whole device stack when reinitilizing mirror log. | ||||
|   Mirrors without monitoring are WARNING and not blocking on error. | ||||
|   Detect too big region_size with clustered mirrors. | ||||
|   Fix evaluation of maximal region size for mirror log. | ||||
|   Enhance mirror log size estimation and use smaller size when possible. | ||||
|   Fix incorrect mirror log size calculation on 32bit arch. | ||||
|   Enhance preloading tree creating. | ||||
|   Fix regression on acceptance of any LV on lvconvert. | ||||
|   Restore usability of thin LV to be again external origin for another thin. | ||||
|   Keep systemd vars on change event in 69-dm-lvm-metad.rules for systemd reload. | ||||
|   Write systemd and non-systemd rule in 69-dm-lvm-metad.rules, GOTO active one. | ||||
|   Add test for activation/volume_list (Sub)LV remnants. | ||||
|   Disallow usage of cache format 2 with mq cache policy. | ||||
|   Again accept striped LV as COW LV with lvconvert -s (2.02.169). | ||||
|   Fix raid target version testing for supported features. | ||||
|   Allow activation of pools when thin/cache_check tool is missing. | ||||
|   Remove RaidLV on creation failure when rmeta devices can't be activated. | ||||
|   Add prioritized_section() to restore cookie boundaries (2.02.177). | ||||
|   Enhance error messages when read error happens. | ||||
|   Enhance mirror log initialization for old mirror target. | ||||
|   Skip private crypto and stratis devices. | ||||
|   Skip frozen raid devices from scanning. | ||||
|   Activate RAID SubLVs on read_only_volume_list readwrite. | ||||
|   Offer convenience type raid5_n converting to raid10. | ||||
|   Automatically avoid reading invalid snapshots during device scan. | ||||
|   Ensure COW device is writable even for read-only thick snapshots. | ||||
|   Support activation of component LVs in read-only mode. | ||||
|   Extend internal library to recognize and work with component LV. | ||||
|   Skip duplicate check for active LV when prompting for its removal. | ||||
|   Activate correct lock holding LV when it is cached. | ||||
|   Do not modify archived metadata when removing striped raid. | ||||
|   Fix memleak on error path when obtaining lv_raid_data_offset. | ||||
|   Fix compatibility size test of extended external origin. | ||||
|   Add external_origin visiting in for_each_sub_lv(). | ||||
|   Ensure cluster commands drop their device cache before locking VG. | ||||
|   Do not report LV as remotely active when it's locally exclusive in cluster. | ||||
|   Add deprecate messages for usage of mirrors with mirrorlog. | ||||
|   Separate reporting of monitoring status and error status. | ||||
|   Improve validation of created strings in vgimportclone. | ||||
|   Add missing initialisation of mem pool in systemd generator. | ||||
|   Do not reopen output streams for multithreaded users of liblvm. | ||||
|   Configure ensures /usr/bin dir is checked for dmpd tools. | ||||
|   Restore pvmove support for wide-clustered active volumes (2.02.177). | ||||
|   Avoid non-exclusive activation of exclusive segment types. | ||||
|   Fix trimming sibling PVs when doing a pvmove of raid subLVs. | ||||
|   Preserve exclusive activation during thin snaphost merge. | ||||
|   Avoid exceeding array bounds in allocation tag processing. | ||||
|   Add --lockopt to common options and add option to skip selected locks. | ||||
|  | ||||
| Version 2.02.177 - 18th December 2017 | ||||
| Version 2.02.169 -  | ||||
| ===================================== | ||||
|   When writing text metadata content, use complete 4096 byte blocks. | ||||
|   Change text format metadata alignment from 512 to 4096 bytes. | ||||
|   When writing metadata, consistently skip mdas marked as failed. | ||||
|   Refactor and adjust text format metadata alignment calculation. | ||||
|   Fix python3 path in lvmdbusd to use value detected by configure. | ||||
|   Reduce checks for active LVs in vgchange before background polling. | ||||
|   Ensure _node_send_message always uses clean status of thin pool. | ||||
|   Fix lvmlockd to use pool lock when accessing _tmeta volume. | ||||
|   Report expected sanlock_convert errors only when retries fail. | ||||
|   Avoid blocking in sanlock_convert on SH to EX lock conversion. | ||||
|   Deactivate missing raid LV legs (_rimage_X-missing_Y_Z) on decativation. | ||||
|   Skip read-modify-write when entire block is replaced. | ||||
|   Categorise I/O with reason annotations in debug messages. | ||||
|   Allow extending of raid LVs created with --nosync after a failed repair. | ||||
|   Command will lock memory only when suspending volumes. | ||||
|   Merge segments when pvmove is finished. | ||||
|   Remove label_verify that has never been used. | ||||
|   Ensure very large numbers used as arguments are not casted to lower values.  | ||||
|   Enhance reading and validation of options stripes and stripes_size. | ||||
|   Fix printing of default stripe size when user is not using stripes. | ||||
|   Activation code for pvmove automatically discovers holding LVs for resume. | ||||
|   Make a pvmove LV locking holder. | ||||
|   Do not change critical section counter on resume path without real resume. | ||||
|   Enhance activation code to automatically suspend pvmove participants. | ||||
|   Prevent conversion of thin volumes to snapshot origin when lvmlockd is used. | ||||
|   Correct the steps to change lock type in lvmlockd man page. | ||||
|   Retry lock acquisition on recognized sanlock errors. | ||||
|   Fix lock manager error codes in lvmlockd. | ||||
|   Remove unnecessary single read from lvmdiskscan. | ||||
|   Check raid reshape flags in vg_validate(). | ||||
|   Add support for pvmove of cache and snapshot origins. | ||||
|   Avoid using precommitted metadata for suspending pvmove tree. | ||||
|   Ehnance pvmove locking. | ||||
|   Deactivate activated LVs on error path when pvmove activation fails. | ||||
|   Add "io" to log/debug_classes for logging low-level I/O. | ||||
|   Eliminate redundant nested VG metadata in VG struct. | ||||
|   Avoid importing persistent filter in vgscan/pvscan/vgrename. | ||||
|   Fix memleak of string buffer when vgcfgbackup runs in secure mode. | ||||
|   Do not print error when clvmd cannot find running clvmd. | ||||
|   Prevent start of new merge of snapshot if origin is already being merged. | ||||
|   Fix offered type for raid6_n_6 to raid5 conversion (raid5_n). | ||||
|   Deactivate sub LVs when removing unused cache-pool. | ||||
|   Do not take backup with suspended devices. | ||||
|   Avoid RAID4 activation on incompatible kernels under all circumstances. | ||||
|   Reject conversion request to striped/raid0 on 2-legged raid4/5. | ||||
|  | ||||
| Version 2.02.176 - 3rd November 2017 | ||||
| ==================================== | ||||
|   Keep Install section only in lvm2-{lvmetad,lvmpolld}.socket systemd unit. | ||||
|   Fix segfault in lvm_pv_remove in liblvm. (2.02.173) | ||||
|   Do not allow storing VG metadata with LV without any segment. | ||||
|   Fix printed message when thin snapshot was already merged. | ||||
|   Remove created spare LV when creation of thin-pool failed. | ||||
|   Avoid reading ignored metadata when mda gets used again. | ||||
|   Fix detection of moved PVs in vgsplit. (2.02.175) | ||||
|   Ignore --stripes/--stripesize on RAID takeover | ||||
|   Improve used paths for generated systemd units and init shells. | ||||
|   Disallow creation of snapshot of mirror/raid subLV (was never supported). | ||||
|   Fix regression in more advanced vgname extraction in lvconvert (2.02.169). | ||||
|   Allow lvcreate to be used for caching of _tdata LV. | ||||
|   Avoid internal error when resizing cache type _tdata LV (not yet supported). | ||||
|   Show original converted names when lvconverting LV to pool volume. | ||||
|   Move lib code used only by liblvm into metadata-liblvm.c. | ||||
|   Distinguish between device not found and excluded by filter. | ||||
|   Monitor external origin LVs. | ||||
|   Remove the replicator code, including configure --with-replicators. | ||||
|   Allow lvcreate --type mirror to work with 100%FREE. | ||||
|   Improve selection of resource name for complex volume activation lock. | ||||
|   Avoid cutting first character of resource name for activation lock. | ||||
|   Support for encrypted devices in fsadm. | ||||
|   Improve thin pool overprovisioning and repair warning messages. | ||||
|   Fix incorrect adjustment of region size on striped RaidLVs. | ||||
|  | ||||
| Version 2.02.175 - 6th October 2017 | ||||
| =================================== | ||||
|   Use --help with blockdev when checking for --getsize64 support in fsadm. | ||||
|   Dump lvmdbusd debug information with SIGUSR1. | ||||
|   Fix metadata corruption in vgsplit and vgmerge intermediate states. | ||||
|   Add PV_MOVED_VG PV status flag to mark PVs moving between VGs. | ||||
|   Fix lvmdbus hang and recognise unknown VG correctly. | ||||
|   Improve error messages when command rules fail. | ||||
|   Require LV name with pvmove in a shared VG. | ||||
|   Allow shared active mirror LVs with lvmlockd, dlm, and cmirrord. | ||||
|   Support lvconvert --repair with cache and cachepool volumes. | ||||
|   lvconvert --repair respects --poolmetadataspare option. | ||||
|   Mark that we don't plan to develop liblvm2app and python bindings any further. | ||||
|   Fix thin pool creation in shared VG. (2.02.173) | ||||
|  | ||||
| Version 2.02.174 - 13th September 2017 | ||||
| ====================================== | ||||
|   Prevent raid1 split with trackchanges in a shared VG. | ||||
|   Avoid double unlocking of client & lockspace mutexes in lvmlockd. | ||||
|   Fix leaking of file descriptor for non-blocking filebased locking. | ||||
|   Fix check for 2nd mda at end of disk fits if using pvcreate --restorefile. | ||||
|   Use maximum metadataarea size that fits with pvcreate --restorefile. | ||||
|   Always clear cached bootloaderarea when wiping label e.g. in pvcreate. | ||||
|   Disallow --bootloaderareasize with pvcreate --restorefile. | ||||
|   Fix lvmlockd check for running lock managers during lock adoption. | ||||
|   Add --withgeneralpreamble and --withlocalpreamble to lvmconfig. | ||||
|   Improve makefiles' linking. | ||||
|   Fix some paths in generated makefiles to respected configured settings. | ||||
|   Add warning when creating thin-pool with zeroing and chunk size >= 512KiB. | ||||
|   Introduce exit code 4 EINIT_FAILED to replace -1 when initialisation fails. | ||||
|   Add synchronization points with udev during reshape of raid LVs. | ||||
|  | ||||
| Version 2.02.173 - 20th July 2017 | ||||
| ================================= | ||||
|   Add synchronization points with udev during conversion of raid LVs. | ||||
|   Improve --size args validation and report more detailed error message. | ||||
|   Initialize debugging mutex before any debug message in clvmd. | ||||
|   Log error instead of warn when noticing connection problem with lvmetad. | ||||
|   Fix memory leak in lvmetad when working with duplicates. | ||||
|   Remove restrictions on reshaping open and clustered raid devices. | ||||
|   Add incompatible data_offset to raid metadata to fix reshape activation. | ||||
|   Accept 'lvm -h' and 'lvm --help' as well as 'lvm help' for help. | ||||
|   Suppress error message from accept() on clean lvmetad shutdown. | ||||
|   Tidy clvmd client list processing and fix segfaults. | ||||
|   Protect clvmd debug log messages with mutex and add client id. | ||||
|   Fix shellcheck reported issues for script files. | ||||
|  | ||||
| Version 2.02.172 - 28th June 2017 | ||||
| ================================= | ||||
|   Add missing NULL to argv array when spliting cmdline arguments. | ||||
|   Add display_percent helper function for printing percent values. | ||||
|   lvconvert --repair handles failing raid legs (present but marked 'D'ead). | ||||
|   Do not lvdisplay --maps unset settings of cache pool. | ||||
|   Fix lvdisplay --maps for cache pool without policy settings. | ||||
|   Support aborting of flushing cache LV. | ||||
|   Reenable conversion of data and metadata thin-pool volumes to raid. | ||||
|   Improve raid status reporting with lvs. | ||||
|   No longer necessary to '--force' a repair for RAID1. | ||||
|   Linear to RAID1 upconverts now use "recover" sync action, not "resync". | ||||
|   Improve lvcreate --cachepool arg validation. | ||||
|   Limit maximum size of thin-pool for specific chunk size. | ||||
|   Print a warning about in-use PVs with no VG using them. | ||||
|   Disable automatic clearing of PVs that look like in-use orphans. | ||||
|   Cache format2 flag is now using segment name type field. | ||||
|   Support storing status flags via segtype name field. | ||||
|   Stop using '--yes' mode when fsadm runs without terminal. | ||||
|   Extend validation of filesystems resized by fsadm. | ||||
|   Enhance lvconvert automatic settings of possible (raid) LV types. | ||||
|   Allow lvchange to change properties on a thin pool data sub LV. | ||||
|   Fix lvcreate extent percentage calculation for mirrors. | ||||
|   Don't reinstate still-missing devices when correcting inconsistent metadata. | ||||
|   Properly handle subshell return codes in fsadm. | ||||
|   Disallow cachepool creation with policy cleaner and mode writeback. | ||||
|  | ||||
| Version 2.02.171 - 3rd May 2017 | ||||
| =============================== | ||||
|   Fix memory warnings by using mempools for command definition processing. | ||||
|   Fix running commands from a script file. | ||||
|   Add pvcreate prompt when device size doesn't match setphysicalvolumesize. | ||||
|   lvconvert - preserve region size on raid1 image count changes | ||||
|   Adjust pvresize/pvcreate messages and prompt if underlying dev size differs. | ||||
|   raid - sanely handle insufficient space on takeover. | ||||
|   Fix configure --enable-notify-dbus status message. | ||||
|   Change configure option name prefix from --enable-lockd to --enable-lvmlockd. | ||||
|   lvcreate - raise mirror/raid default regionsize to 2MiB | ||||
|   Add missing configurable prefix to configuration file installation directory. | ||||
|  | ||||
| Version 2.02.170 - 13th April 2017 | ||||
| ================================== | ||||
|   Introduce global/fsadm_executable to make fsadm path configurable. | ||||
|   Look for limited thin pool metadata size when using 16G metadata. | ||||
|   Add lvconvert pool creation rule disallowing options with poolmetadata. | ||||
|   Fix lvconvert when the same LV is incorrectly reused in options. | ||||
|   Fix lvconvert VG name validation in option values. | ||||
|   Fix missing lvmlockd LV locks in lvchange and lvconvert. | ||||
|   Fix dmeventd setup for lvchange --poll. | ||||
|   Fix use of --poll and --monitor with lvchange and vgchange. | ||||
|   Disallow lvconvert of hidden LV to a pool. | ||||
|   Ignore --partial option when not used for activation. | ||||
|   Allow --activationmode option with lvchange --refresh. | ||||
|   Better message on lvconvert --regionsize | ||||
|   Allow valid lvconvert --regionsize change | ||||
|   Add raid10 alias raid10_near | ||||
|   Handle insufficient PVs on lvconvert takeover | ||||
|   Fix SIGINT blocking to prevent corrupted metadata | ||||
|   Fix systemd unit existence check for lvmconf --services --startstopservices. | ||||
|   Check and use PATH_MAX buffers when creating vgrename device paths. | ||||
|  | ||||
| Version 2.02.169 - 28th March 2017 | ||||
| ================================== | ||||
|   Automatically decide whether '-' in a man page is a hyphen or a minus sign. | ||||
|   Add build-time configuration command line to 'lvm version' output. | ||||
|   Handle known table line parameter order change in specific raid target vsns. | ||||
|   Conditionally reject raid convert to striped/raid0* after reshape. | ||||
|   Ensure raid6 upconversion restrictions. | ||||
|   Adjust mirror & raid dmeventd plugins for new lvconvert --repair behaviour. | ||||
|   Disable lvmetad when lvconvert --repair is run. | ||||
|   Remove obsolete lvmchange binary - convert to built-in command. | ||||
|   Show more information for cached volumes in lvdisplay [-m]. | ||||
|   Add option for lvcreate/lvconvert --cachemetadataformat auto|1|2. | ||||
|   Support cache segment with configurable metadata format. | ||||
|   Add allocation/cache_metadata_format profilable settings. | ||||
|   Use function cache_set_params() for both lvcreate and lvconvert. | ||||
|   Skip rounding on cache chunk size boudary when create cache LV. | ||||
|   Improve cache_set_params support for chunk_size selection. | ||||
|   Fix metadata profile allocation/cache_[mode|policy] setting. | ||||
|   Fix missing support for using allocation/cache_pool_chunk_size setting. | ||||
|   Upstream git moved to https://sourceware.org/git/?p=lvm2 | ||||
|   Support conversion of raid type, stripesize and number of disks | ||||
|   Reject writemostly/writebehind in lvchange during resynchronization. | ||||
|   Deactivate active origin first before removal for improved workflow. | ||||
|   Fix regression of accepting both --type and -m with lvresize. (2.02.158) | ||||
|   Add lvconvert --swapmetadata, new specific way to swap pool metadata LVs. | ||||
|   Add lvconvert --startpoll, new specific way to start polling conversions. | ||||
|   Add lvconvert --mergethin, new specific way to merge thin snapshots. | ||||
|   Add lvconvert --mergemirrors, new specific way to merge split mirrors. | ||||
|   Add lvconvert --mergesnapshot, new specific way to combine cow LVs. | ||||
|   Split up lvconvert code based on command definitions. | ||||
|   Split up lvchange code based on command definitions. | ||||
|   Generate help output and man pages from command definitions. | ||||
|   Verify all command line items against command definition. | ||||
|   Match every command run to one command definition. | ||||
|   Specify every allowed command definition/syntax in command-lines.in. | ||||
|   Add extra memory page when limiting pthread stack size in clvmd. | ||||
|   Support striped/raid0* <-> raid10_near conversions. | ||||
|   Support shrinking of RaidLVs. | ||||
|   Support region size changes on existing RaidLVs. | ||||
|   Support region size changes on existing RaidLVs | ||||
|   Avoid parallel usage of cpg_mcast_joined() in clvmd with corosync. | ||||
|   Support raid6_{ls,rs,la,ra}_6 segment types and conversions from/to it. | ||||
|   Support raid6_n_6 segment type and conversions from/to it. | ||||
|   | ||||
							
								
								
									
										134
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							| @@ -1,138 +1,16 @@ | ||||
| Version 1.02.165 -  | ||||
| ================================= | ||||
|   Add debug of dmsetup udevcomplete with hexa print DM_COOKIE_COMPLETED. | ||||
|   Fix versioning of dm_stats_create_region and dm_stats_create_region. | ||||
|  | ||||
| Version 1.02.163 - 15th June 2019 | ||||
| ================================= | ||||
|  | ||||
| Version 1.02.161 - 10th June 2019 | ||||
| ================================= | ||||
|  | ||||
| Version 1.02.159 - 07th June 2019 | ||||
| ================================= | ||||
|   Parsing of cache status understand no_discard_passdown. | ||||
|   Ensure migration_threshold for cache is at least 8 chunks. | ||||
|  | ||||
| Version 1.02.155 - 18th December 2018 | ||||
| Version 1.02.138 -  | ||||
| ===================================== | ||||
|   Include correct internal header inside libdm list.c. | ||||
|   Enhance ioctl flattening and add parameters only when needed. | ||||
|   Add DM_DEVICE_ARM_POLL for API completness matching kernel. | ||||
|   Do not add parameters for RESUME with DM_DEVICE_CREATE dm task. | ||||
|   Fix dmstats report printing no output. | ||||
|  | ||||
| Version 1.02.153 - 31st October 2018 | ||||
| ==================================== | ||||
|  | ||||
| Version 1.02.151 - 10th October 2018 | ||||
| ==================================== | ||||
|   Add hot fix to avoiding locking collision when monitoring thin-pools. | ||||
|  | ||||
| Version 1.02.150 - 01 August 2018 | ||||
| ================================= | ||||
|   Add vdo plugin for monitoring VDO devices. | ||||
|  | ||||
| Version 1.02.149 - 19th July 2018 | ||||
| ================================= | ||||
|  | ||||
| Version 1.02.148 - 18th June 2018 | ||||
| ================================= | ||||
|  | ||||
| Version 1.02.147 - 13th June 2018 | ||||
| ================================= | ||||
|  | ||||
| Version 1.02.147-rc1 - 24th May 2018 | ||||
| ==================================== | ||||
|   Reuse uname() result for mirror target. | ||||
|   Recognize also mounted btrfs through dm_device_has_mounted_fs(). | ||||
|   Add missing log_error() into dm_stats_populate() returning 0. | ||||
|   Avoid calling dm_stats_populat() for DM devices without any stats regions. | ||||
|   Support DM_DEBUG_WITH_LINE_NUMBERS envvar for debug msg with source:line. | ||||
|   Configured command for thin pool threshold handling gets whole environment. | ||||
|   Fix tests for failing dm_snprintf() in stats code. | ||||
|   Parsing mirror status accepts 'userspace' keyword in status. | ||||
|   Introduce dm_malloc_aligned for page alignment of buffers. | ||||
|  | ||||
| Version 1.02.146 - 18th December 2017 | ||||
| ===================================== | ||||
|   Activation tree of thin pool skips duplicated check of pool status. | ||||
|   Remove code supporting replicator target. | ||||
|   Do not ignore failure of _info_by_dev(). | ||||
|   Propagate delayed resume for pvmove subvolumes. | ||||
|   Suppress integrity encryption keys in 'table' output unless --showkeys supplied. | ||||
|  | ||||
| Version 1.02.145 - 3rd November 2017 | ||||
| ==================================== | ||||
|   Keep Install section only in dm-event.socket systemd unit. | ||||
|   Issue a specific error with dmsetup status if device is unknown. | ||||
|   Fix RT_LIBS reference in generated libdevmapper.pc for pkg-config | ||||
|  | ||||
| Version 1.02.144 - 6th October 2017 | ||||
| =================================== | ||||
|   Schedule exit when received SIGTERM in dmeventd. | ||||
|   Also try to unmount /boot on blkdeactivate -u if on top of supported device. | ||||
|   Use blkdeactivate -r wait in blk-availability systemd service/initscript. | ||||
|   Add blkdeactivate -r wait option to wait for MD resync/recovery/reshape. | ||||
|   Fix blkdeactivate regression with failing DM/MD devs deactivation (1.02.142). | ||||
|   Fix typo in blkdeactivate's '--{dm,lvm,mpath}options' option name. | ||||
|   Correct return value testing when get reserved values for reporting. | ||||
|   Take -S with dmsetup suspend/resume/clear/wipe_table/remove/deps/status/table. | ||||
|  | ||||
| Version 1.02.143 - 13th September 2017 | ||||
| ====================================== | ||||
|   Restore umask when creation of node fails. | ||||
|   Add --concise to dmsetup create for many devices with tables in one command. | ||||
|   Accept minor number without major in library when it knows dm major number. | ||||
|   Introduce single-line concise table output format: dmsetup table --concise | ||||
|  | ||||
| Version 1.02.142 - 20th July 2017 | ||||
| ================================= | ||||
|   Create /dev/disk/by-part{uuid,label} and gpt-auto-root symlinks with udev. | ||||
|  | ||||
| Version 1.02.141 - 28th June 2017 | ||||
| ================================= | ||||
|   Fix reusing of dm_task structure for status reading (used by dmeventd). | ||||
|   Add dm_percent_to_round_float for adjusted percentage rounding. | ||||
|   Reset array with dead rimage devices once raid gets in sync. | ||||
|   Drop unneeded --config option from raid dmeventd plugin. | ||||
|   dm_get_status_raid() handle better some incosistent md statuses. | ||||
|   Accept truncated files in calls to dm_stats_update_regions_from_fd(). | ||||
|   Restore Warning by 5% increment when thin-pool is over 80% (1.02.138). | ||||
|  | ||||
| Version 1.02.140 - 3rd May 2017 | ||||
| =============================== | ||||
|   Add missing configure --enable-dmfilemapd status message and fix --disable. | ||||
|  | ||||
| Version 1.02.139 - 13th April 2017 | ||||
| ================================== | ||||
|   Fix assignment in _target_version() when dm task can't run. | ||||
|   Flush stdout on each iteration when using --count or --interval. | ||||
|   Show detailed error message when execvp fails while starting dmfilemapd. | ||||
|   Fix segmentation fault when dmfilemapd is run with no arguments. | ||||
|   Numerous minor dmfilemapd fixes from coverity. | ||||
|  | ||||
| Version 1.02.138 - 28th March 2017 | ||||
| ================================== | ||||
|   Support additional raid5/6 configurations. | ||||
|   Provide dm_tree_node_add_cache_target@base compatible symbol. | ||||
|   Support DM_CACHE_FEATURE_METADATA2, new cache metadata format 2. | ||||
|   Improve code to handle mode mask for cache nodes. | ||||
|   Cache status check for passthrough also require trailing space. | ||||
|   Add extra memory page when limiting pthread stack size in dmeventd. | ||||
|   Avoids immediate resume when preloaded device is smaller. | ||||
|   Do not suppress kernel key description in dmsetup table output for dm-crypt. | ||||
|   Support configurable command executed from dmeventd thin plugin. | ||||
|   Support new R|r human readable units output format. | ||||
|   Thin dmeventd plugin reacts faster on lvextend failure path with umount. | ||||
|   Add dm_stats_bind_from_fd() to bind a stats handle from a file descriptor. | ||||
|   Do not try call callback when reverting activation on error path. | ||||
|   Fix file mapping for extents with physically adjacent extents in dmstats. | ||||
|   Fix file mapping for extents with physically adjacent extents. | ||||
|   Validation vsnprintf result in runtime translate of dm_log (1.02.136). | ||||
|   Separate filemap extent allocation from region table in dmstats. | ||||
|   Fix segmentation fault when filemap region creation fails in dmstats. | ||||
|   Fix performance of region cleanup for failed filemap creation in dmstats. | ||||
|   Fix very slow region deletion with many regions in dmstats. | ||||
|   Separate filemap extent allocation from region table. | ||||
|   Fix segmentation fault when filemap region creation fails. | ||||
|   Fix performance of region cleanup for failed filemap creation. | ||||
|   Fix very slow region deletion with many regions. | ||||
|  | ||||
| Version 1.02.137 - 30th November 2016 | ||||
| ===================================== | ||||
|   | ||||
| @@ -155,7 +155,7 @@ AC_DEFUN([AC_TRY_LDFLAGS], | ||||
| #   and this notice are preserved.  This file is offered as-is, without any | ||||
| #   warranty. | ||||
|  | ||||
| serial 3 | ||||
| #serial 3 | ||||
|  | ||||
| AC_DEFUN([AX_GCC_BUILTIN], [ | ||||
|     AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1]) | ||||
|   | ||||
							
								
								
									
										293
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										293
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # generated automatically by aclocal 1.15.1 -*- Autoconf -*- | ||||
| # generated automatically by aclocal 1.15 -*- Autoconf -*- | ||||
|  | ||||
| # Copyright (C) 1996-2017 Free Software Foundation, Inc. | ||||
| # Copyright (C) 1996-2014 Free Software Foundation, Inc. | ||||
|  | ||||
| # This file is free software; the Free Software Foundation | ||||
| # gives unlimited permission to copy and/or distribute it, | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
| m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) | ||||
| # =========================================================================== | ||||
| #     https://www.gnu.org/software/autoconf-archive/ax_python_module.html | ||||
| #     http://www.gnu.org/software/autoconf-archive/ax_python_module.html | ||||
| # =========================================================================== | ||||
| # | ||||
| # SYNOPSIS | ||||
| @@ -37,7 +37,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun | ||||
| #   and this notice are preserved. This file is offered as-is, without any | ||||
| #   warranty. | ||||
|  | ||||
| #serial 9 | ||||
| #serial 8 | ||||
|  | ||||
| AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE]) | ||||
| AC_DEFUN([AX_PYTHON_MODULE],[ | ||||
| @@ -69,63 +69,32 @@ AC_DEFUN([AX_PYTHON_MODULE],[ | ||||
|     fi | ||||
| ]) | ||||
|  | ||||
| # pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*- | ||||
| # serial 11 (pkg-config-0.29.1) | ||||
| # pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*- | ||||
| # serial 1 (pkg-config-0.24) | ||||
| #  | ||||
| # Copyright © 2004 Scott James Remnant <scott@netsplit.com>. | ||||
| # | ||||
| # 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. | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>. | ||||
| dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com> | ||||
| dnl | ||||
| dnl This program is free software; you can redistribute it and/or modify | ||||
| dnl it under the terms of the GNU General Public License as published by | ||||
| dnl the Free Software Foundation; either version 2 of the License, or | ||||
| dnl (at your option) any later version. | ||||
| dnl | ||||
| dnl This program is distributed in the hope that it will be useful, but | ||||
| dnl WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
| dnl General Public License for more details. | ||||
| dnl | ||||
| dnl You should have received a copy of the GNU General Public License | ||||
| dnl along with this program; if not, write to the Free Software | ||||
| dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
| dnl 02111-1307, USA. | ||||
| dnl | ||||
| dnl As a special exception to the GNU General Public License, if you | ||||
| dnl distribute this file as part of a program that contains a | ||||
| dnl configuration script generated by Autoconf, you may include it under | ||||
| dnl the same distribution terms that you use for the rest of that | ||||
| dnl program. | ||||
|  | ||||
| dnl PKG_PREREQ(MIN-VERSION) | ||||
| dnl ----------------------- | ||||
| dnl Since: 0.29 | ||||
| dnl | ||||
| dnl Verify that the version of the pkg-config macros are at least | ||||
| dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's | ||||
| dnl installed version of pkg-config, this checks the developer's version | ||||
| dnl of pkg.m4 when generating configure. | ||||
| dnl | ||||
| dnl To ensure that this macro is defined, also add: | ||||
| dnl m4_ifndef([PKG_PREREQ], | ||||
| dnl     [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) | ||||
| dnl | ||||
| dnl See the "Since" comment for each macro you use to see what version | ||||
| dnl of the macros you require. | ||||
| m4_defun([PKG_PREREQ], | ||||
| [m4_define([PKG_MACROS_VERSION], [0.29.1]) | ||||
| m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, | ||||
|     [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) | ||||
| ])dnl PKG_PREREQ | ||||
|  | ||||
| dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) | ||||
| dnl ---------------------------------- | ||||
| dnl Since: 0.16 | ||||
| dnl | ||||
| dnl Search for the pkg-config tool and set the PKG_CONFIG variable to | ||||
| dnl first found in the path. Checks that the version of pkg-config found | ||||
| dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is | ||||
| dnl used since that's the first version where most current features of | ||||
| dnl pkg-config existed. | ||||
| # PKG_PROG_PKG_CONFIG([MIN-VERSION]) | ||||
| # ---------------------------------- | ||||
| AC_DEFUN([PKG_PROG_PKG_CONFIG], | ||||
| [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) | ||||
| m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) | ||||
| @@ -147,19 +116,18 @@ if test -n "$PKG_CONFIG"; then | ||||
| 		PKG_CONFIG="" | ||||
| 	fi | ||||
| fi[]dnl | ||||
| ])dnl PKG_PROG_PKG_CONFIG | ||||
| ])# PKG_PROG_PKG_CONFIG | ||||
|  | ||||
| dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) | ||||
| dnl ------------------------------------------------------------------- | ||||
| dnl Since: 0.18 | ||||
| dnl | ||||
| dnl Check to see whether a particular set of modules exists. Similar to | ||||
| dnl PKG_CHECK_MODULES(), but does not set variables or print errors. | ||||
| dnl | ||||
| dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) | ||||
| dnl only at the first occurence in configure.ac, so if the first place | ||||
| dnl it's called might be skipped (such as if it is within an "if", you | ||||
| dnl have to call PKG_CHECK_EXISTS manually | ||||
| # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) | ||||
| # | ||||
| # Check to see whether a particular set of modules exists.  Similar | ||||
| # to PKG_CHECK_MODULES(), but does not set variables or print errors. | ||||
| # | ||||
| # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) | ||||
| # only at the first occurence in configure.ac, so if the first place | ||||
| # it's called might be skipped (such as if it is within an "if", you | ||||
| # have to call PKG_CHECK_EXISTS manually | ||||
| # -------------------------------------------------------------- | ||||
| AC_DEFUN([PKG_CHECK_EXISTS], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl | ||||
| if test -n "$PKG_CONFIG" && \ | ||||
| @@ -169,10 +137,8 @@ m4_ifvaln([$3], [else | ||||
|   $3])dnl | ||||
| fi]) | ||||
|  | ||||
| dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) | ||||
| dnl --------------------------------------------- | ||||
| dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting | ||||
| dnl pkg_failed based on the result. | ||||
| # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) | ||||
| # --------------------------------------------- | ||||
| m4_define([_PKG_CONFIG], | ||||
| [if test -n "$$1"; then | ||||
|     pkg_cv_[]$1="$$1" | ||||
| @@ -184,11 +150,10 @@ m4_define([_PKG_CONFIG], | ||||
|  else | ||||
|     pkg_failed=untried | ||||
| fi[]dnl | ||||
| ])dnl _PKG_CONFIG | ||||
| ])# _PKG_CONFIG | ||||
|  | ||||
| dnl _PKG_SHORT_ERRORS_SUPPORTED | ||||
| dnl --------------------------- | ||||
| dnl Internal check to see if pkg-config supports short errors. | ||||
| # _PKG_SHORT_ERRORS_SUPPORTED | ||||
| # ----------------------------- | ||||
| AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) | ||||
| if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then | ||||
| @@ -196,17 +161,19 @@ if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then | ||||
| else | ||||
|         _pkg_short_errors_supported=no | ||||
| fi[]dnl | ||||
| ])dnl _PKG_SHORT_ERRORS_SUPPORTED | ||||
| ])# _PKG_SHORT_ERRORS_SUPPORTED | ||||
|  | ||||
|  | ||||
| dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], | ||||
| dnl   [ACTION-IF-NOT-FOUND]) | ||||
| dnl -------------------------------------------------------------- | ||||
| dnl Since: 0.4.0 | ||||
| dnl | ||||
| dnl Note that if there is a possibility the first call to | ||||
| dnl PKG_CHECK_MODULES might not happen, you should be sure to include an | ||||
| dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac | ||||
| # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], | ||||
| # [ACTION-IF-NOT-FOUND]) | ||||
| # | ||||
| # | ||||
| # Note that if there is a possibility the first call to | ||||
| # PKG_CHECK_MODULES might not happen, you should be sure to include an | ||||
| # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac | ||||
| # | ||||
| # | ||||
| # -------------------------------------------------------------- | ||||
| AC_DEFUN([PKG_CHECK_MODULES], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl | ||||
| AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl | ||||
| @@ -260,40 +227,16 @@ else | ||||
|         AC_MSG_RESULT([yes]) | ||||
| 	$3 | ||||
| fi[]dnl | ||||
| ])dnl PKG_CHECK_MODULES | ||||
| ])# PKG_CHECK_MODULES | ||||
|  | ||||
|  | ||||
| dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], | ||||
| dnl   [ACTION-IF-NOT-FOUND]) | ||||
| dnl --------------------------------------------------------------------- | ||||
| dnl Since: 0.29 | ||||
| dnl | ||||
| dnl Checks for existence of MODULES and gathers its build flags with | ||||
| dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags | ||||
| dnl and VARIABLE-PREFIX_LIBS from --libs. | ||||
| dnl | ||||
| dnl Note that if there is a possibility the first call to | ||||
| dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to | ||||
| dnl include an explicit call to PKG_PROG_PKG_CONFIG in your | ||||
| dnl configure.ac. | ||||
| AC_DEFUN([PKG_CHECK_MODULES_STATIC], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl | ||||
| _save_PKG_CONFIG=$PKG_CONFIG | ||||
| PKG_CONFIG="$PKG_CONFIG --static" | ||||
| PKG_CHECK_MODULES($@) | ||||
| PKG_CONFIG=$_save_PKG_CONFIG[]dnl | ||||
| ])dnl PKG_CHECK_MODULES_STATIC | ||||
|  | ||||
|  | ||||
| dnl PKG_INSTALLDIR([DIRECTORY]) | ||||
| dnl ------------------------- | ||||
| dnl Since: 0.27 | ||||
| dnl | ||||
| dnl Substitutes the variable pkgconfigdir as the location where a module | ||||
| dnl should install pkg-config .pc files. By default the directory is | ||||
| dnl $libdir/pkgconfig, but the default can be changed by passing | ||||
| dnl DIRECTORY. The user can override through the --with-pkgconfigdir | ||||
| dnl parameter. | ||||
| # PKG_INSTALLDIR(DIRECTORY) | ||||
| # ------------------------- | ||||
| # Substitutes the variable pkgconfigdir as the location where a module | ||||
| # should install pkg-config .pc files. By default the directory is | ||||
| # $libdir/pkgconfig, but the default can be changed by passing | ||||
| # DIRECTORY. The user can override through the --with-pkgconfigdir | ||||
| # parameter. | ||||
| AC_DEFUN([PKG_INSTALLDIR], | ||||
| [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) | ||||
| m4_pushdef([pkg_description], | ||||
| @@ -304,18 +247,16 @@ AC_ARG_WITH([pkgconfigdir], | ||||
| AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) | ||||
| m4_popdef([pkg_default]) | ||||
| m4_popdef([pkg_description]) | ||||
| ])dnl PKG_INSTALLDIR | ||||
| ]) dnl PKG_INSTALLDIR | ||||
|  | ||||
|  | ||||
| dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) | ||||
| dnl -------------------------------- | ||||
| dnl Since: 0.27 | ||||
| dnl | ||||
| dnl Substitutes the variable noarch_pkgconfigdir as the location where a | ||||
| dnl module should install arch-independent pkg-config .pc files. By | ||||
| dnl default the directory is $datadir/pkgconfig, but the default can be | ||||
| dnl changed by passing DIRECTORY. The user can override through the | ||||
| dnl --with-noarch-pkgconfigdir parameter. | ||||
| # PKG_NOARCH_INSTALLDIR(DIRECTORY) | ||||
| # ------------------------- | ||||
| # Substitutes the variable noarch_pkgconfigdir as the location where a | ||||
| # module should install arch-independent pkg-config .pc files. By | ||||
| # default the directory is $datadir/pkgconfig, but the default can be | ||||
| # changed by passing DIRECTORY. The user can override through the | ||||
| # --with-noarch-pkgconfigdir parameter. | ||||
| AC_DEFUN([PKG_NOARCH_INSTALLDIR], | ||||
| [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) | ||||
| m4_pushdef([pkg_description], | ||||
| @@ -326,15 +267,13 @@ AC_ARG_WITH([noarch-pkgconfigdir], | ||||
| AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) | ||||
| m4_popdef([pkg_default]) | ||||
| m4_popdef([pkg_description]) | ||||
| ])dnl PKG_NOARCH_INSTALLDIR | ||||
| ]) dnl PKG_NOARCH_INSTALLDIR | ||||
|  | ||||
|  | ||||
| dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, | ||||
| dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) | ||||
| dnl ------------------------------------------- | ||||
| dnl Since: 0.28 | ||||
| dnl | ||||
| dnl Retrieves the value of the pkg-config variable for the given module. | ||||
| # PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, | ||||
| # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) | ||||
| # ------------------------------------------- | ||||
| # Retrieves the value of the pkg-config variable for the given module. | ||||
| AC_DEFUN([PKG_CHECK_VAR], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl | ||||
| AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl | ||||
| @@ -343,77 +282,9 @@ _PKG_CONFIG([$1], [variable="][$3]["], [$2]) | ||||
| AS_VAR_COPY([$1], [pkg_cv_][$1]) | ||||
|  | ||||
| AS_VAR_IF([$1], [""], [$5], [$4])dnl | ||||
| ])dnl PKG_CHECK_VAR | ||||
| ])# PKG_CHECK_VAR | ||||
|  | ||||
| dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, | ||||
| dnl   [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], | ||||
| dnl   [DESCRIPTION], [DEFAULT]) | ||||
| dnl ------------------------------------------ | ||||
| dnl | ||||
| dnl Prepare a "--with-" configure option using the lowercase | ||||
| dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and | ||||
| dnl PKG_CHECK_MODULES in a single macro. | ||||
| AC_DEFUN([PKG_WITH_MODULES], | ||||
| [ | ||||
| m4_pushdef([with_arg], m4_tolower([$1])) | ||||
|  | ||||
| m4_pushdef([description], | ||||
|            [m4_default([$5], [build with ]with_arg[ support])]) | ||||
|  | ||||
| m4_pushdef([def_arg], [m4_default([$6], [auto])]) | ||||
| m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) | ||||
| m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) | ||||
|  | ||||
| m4_case(def_arg, | ||||
|             [yes],[m4_pushdef([with_without], [--without-]with_arg)], | ||||
|             [m4_pushdef([with_without],[--with-]with_arg)]) | ||||
|  | ||||
| AC_ARG_WITH(with_arg, | ||||
|      AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, | ||||
|     [AS_TR_SH([with_]with_arg)=def_arg]) | ||||
|  | ||||
| AS_CASE([$AS_TR_SH([with_]with_arg)], | ||||
|             [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], | ||||
|             [auto],[PKG_CHECK_MODULES([$1],[$2], | ||||
|                                         [m4_n([def_action_if_found]) $3], | ||||
|                                         [m4_n([def_action_if_not_found]) $4])]) | ||||
|  | ||||
| m4_popdef([with_arg]) | ||||
| m4_popdef([description]) | ||||
| m4_popdef([def_arg]) | ||||
|  | ||||
| ])dnl PKG_WITH_MODULES | ||||
|  | ||||
| dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, | ||||
| dnl   [DESCRIPTION], [DEFAULT]) | ||||
| dnl ----------------------------------------------- | ||||
| dnl | ||||
| dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES | ||||
| dnl check._[VARIABLE-PREFIX] is exported as make variable. | ||||
| AC_DEFUN([PKG_HAVE_WITH_MODULES], | ||||
| [ | ||||
| PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) | ||||
|  | ||||
| AM_CONDITIONAL([HAVE_][$1], | ||||
|                [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) | ||||
| ])dnl PKG_HAVE_WITH_MODULES | ||||
|  | ||||
| dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, | ||||
| dnl   [DESCRIPTION], [DEFAULT]) | ||||
| dnl ------------------------------------------------------ | ||||
| dnl | ||||
| dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after | ||||
| dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make | ||||
| dnl and preprocessor variable. | ||||
| AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], | ||||
| [ | ||||
| PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) | ||||
|  | ||||
| AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], | ||||
|         [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) | ||||
| ])dnl PKG_HAVE_DEFINE_WITH_MODULES | ||||
|  | ||||
| # Copyright (C) 1999-2017 Free Software Foundation, Inc. | ||||
| # Copyright (C) 1999-2014 Free Software Foundation, Inc. | ||||
| # | ||||
| # This file is free software; the Free Software Foundation | ||||
| # gives unlimited permission to copy and/or distribute it, | ||||
| @@ -446,9 +317,8 @@ AC_DEFUN([AM_PATH_PYTHON], | ||||
|  [ | ||||
|   dnl Find a Python interpreter.  Python versions prior to 2.0 are not | ||||
|   dnl supported. (2.0 was released on October 16, 2000). | ||||
|   dnl FIXME: Remove the need to hard-code Python versions here. | ||||
|   m4_define_default([_AM_PYTHON_INTERPRETER_LIST], | ||||
| [python python2 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 dnl | ||||
| [python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl | ||||
|  python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) | ||||
|  | ||||
|   AC_ARG_VAR([PYTHON], [the Python interpreter]) | ||||
| @@ -649,7 +519,7 @@ for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] | ||||
| sys.exit(sys.hexversion < minverhex)" | ||||
|   AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) | ||||
|  | ||||
| # Copyright (C) 2001-2017 Free Software Foundation, Inc. | ||||
| # Copyright (C) 2001-2014 Free Software Foundation, Inc. | ||||
| # | ||||
| # This file is free software; the Free Software Foundation | ||||
| # gives unlimited permission to copy and/or distribute it, | ||||
| @@ -666,4 +536,5 @@ AC_DEFUN([AM_RUN_LOG], | ||||
|    echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD | ||||
|    (exit $ac_status); }]) | ||||
|  | ||||
|  | ||||
| m4_include([acinclude.m4]) | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| # Copyright (C) 2018 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  | ||||
| # Uncomment this to build the simple radix tree.  You'll need to make clean too. | ||||
| # Comment to build the advanced radix tree. | ||||
| #base/data-struct/radix-tree.o: CFLAGS += -DSIMPLE_RADIX_TREE | ||||
|  | ||||
| # NOTE: this Makefile only works as 'include' for toplevel Makefile | ||||
| #       which defined all top_* variables | ||||
|  | ||||
| BASE_SOURCE=\ | ||||
| 	base/data-struct/hash.c \ | ||||
| 	base/data-struct/list.c \ | ||||
| 	base/data-struct/radix-tree.c | ||||
|  | ||||
| BASE_TARGET = base/libbase.a | ||||
| BASE_DEPENDS = $(BASE_SOURCE:%.c=%.d) | ||||
| BASE_OBJECTS = $(BASE_SOURCE:%.c=%.o) | ||||
| CLEAN_TARGETS += $(BASE_DEPENDS) $(BASE_OBJECTS) \ | ||||
| 	$(BASE_SOURCE:%.c=%.gcda) \ | ||||
| 	$(BASE_SOURCE:%.c=%.gcno) \ | ||||
| 	$(BASE_TARGET) | ||||
|  | ||||
| $(BASE_TARGET): $(BASE_OBJECTS) | ||||
| 	@echo "    [AR] $@" | ||||
| 	$(Q) $(RM) $@ | ||||
| 	$(Q) $(AR) rsv $@ $(BASE_OBJECTS) > /dev/null | ||||
|  | ||||
| ifeq ("$(DEPENDS)","yes") | ||||
| -include $(BASE_DEPENDS) | ||||
| endif | ||||
| @@ -1,394 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "device_mapper/misc/dmlib.h" | ||||
| #include "base/memory/zalloc.h" | ||||
| #include "hash.h" | ||||
|  | ||||
| struct dm_hash_node { | ||||
| 	struct dm_hash_node *next; | ||||
| 	void *data; | ||||
| 	unsigned data_len; | ||||
| 	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 = 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 = 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 = zalloc(len))) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	return hc; | ||||
|  | ||||
|       bad: | ||||
| 	free(hc->slots); | ||||
| 	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; | ||||
| 			free(c); | ||||
| 		} | ||||
| } | ||||
|  | ||||
| void dm_hash_destroy(struct dm_hash_table *t) | ||||
| { | ||||
| 	_free_nodes(t); | ||||
| 	free(t->slots); | ||||
| 	free(t); | ||||
| } | ||||
|  | ||||
| static struct dm_hash_node **_find(struct dm_hash_table *t, const void *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 void *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 void *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 void *key, | ||||
| 			uint32_t len) | ||||
| { | ||||
| 	struct dm_hash_node **c = _find(t, key, len); | ||||
|  | ||||
| 	if (*c) { | ||||
| 		struct dm_hash_node *old = *c; | ||||
| 		*c = (*c)->next; | ||||
| 		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); | ||||
| } | ||||
|  | ||||
| static struct dm_hash_node **_find_str_with_val(struct dm_hash_table *t, | ||||
| 					        const void *key, const void *val, | ||||
| 					        uint32_t len, uint32_t val_len) | ||||
| { | ||||
| 	struct dm_hash_node **c; | ||||
| 	unsigned h; | ||||
|         | ||||
| 	h = _hash(key, len) & (t->num_slots - 1); | ||||
|  | ||||
| 	for (c = &t->slots[h]; *c; c = &((*c)->next)) { | ||||
| 		if ((*c)->keylen != len) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!memcmp(key, (*c)->key, len) && (*c)->data) { | ||||
| 			if (((*c)->data_len == val_len) && | ||||
| 			    !memcmp(val, (*c)->data, val_len)) | ||||
| 				return c; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key, | ||||
| 				  const void *val, uint32_t val_len) | ||||
| { | ||||
| 	struct dm_hash_node *n; | ||||
| 	struct dm_hash_node *first; | ||||
| 	int len = strlen(key) + 1; | ||||
| 	unsigned h; | ||||
|  | ||||
| 	n = _create_node(key, len); | ||||
| 	if (!n) | ||||
| 		return 0; | ||||
|  | ||||
| 	n->data = (void *)val; | ||||
| 	n->data_len = val_len; | ||||
|  | ||||
| 	h = _hash(key, len) & (t->num_slots - 1); | ||||
|  | ||||
| 	first = t->slots[h]; | ||||
|  | ||||
| 	if (first) | ||||
| 		n->next = first; | ||||
| 	else | ||||
| 		n->next = 0; | ||||
| 	t->slots[h] = n; | ||||
|  | ||||
| 	t->num_nodes++; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Look through multiple entries with the same key for one that has a | ||||
|  * matching val and return that.  If none have maching val, return NULL. | ||||
|  */ | ||||
| void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key, | ||||
| 			      const void *val, uint32_t val_len) | ||||
| { | ||||
| 	struct dm_hash_node **c; | ||||
|  | ||||
| 	c = _find_str_with_val(t, key, val, strlen(key) + 1, val_len); | ||||
|  | ||||
| 	return (c && *c) ? (*c)->data : 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Look through multiple entries with the same key for one that has a | ||||
|  * matching val and remove that. | ||||
|  */ | ||||
| void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key, | ||||
| 			     const void *val, uint32_t val_len) | ||||
| { | ||||
| 	struct dm_hash_node **c; | ||||
|  | ||||
| 	c = _find_str_with_val(t, key, val, strlen(key) + 1, val_len); | ||||
|  | ||||
| 	if (c && *c) { | ||||
| 		struct dm_hash_node *old = *c; | ||||
| 		*c = (*c)->next; | ||||
| 		free(old); | ||||
| 		t->num_nodes--; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Look up the value for a key and count how many | ||||
|  * entries have the same key. | ||||
|  * | ||||
|  * If no entries have key, return NULL and set count to 0. | ||||
|  * | ||||
|  * If one entry has the key, the function returns the val, | ||||
|  * and sets count to 1. | ||||
|  * | ||||
|  * If N entries have the key, the function returns the val | ||||
|  * from the first entry, and sets count to N. | ||||
|  */ | ||||
| void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count) | ||||
| { | ||||
| 	struct dm_hash_node **c; | ||||
| 	struct dm_hash_node **c1 = NULL; | ||||
| 	uint32_t len = strlen(key) + 1; | ||||
| 	unsigned h; | ||||
|  | ||||
| 	*count = 0; | ||||
|  | ||||
| 	h = _hash(key, len) & (t->num_slots - 1); | ||||
|  | ||||
| 	for (c = &t->slots[h]; *c; c = &((*c)->next)) { | ||||
| 		if ((*c)->keylen != len) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!memcmp(key, (*c)->key, len)) { | ||||
| 			(*count)++; | ||||
| 			if (!c1) | ||||
| 				c1 = c; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!c1) | ||||
| 		return NULL; | ||||
| 	else | ||||
| 		return *c1 ? (*c1)->data : 0; | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
| @@ -1,94 +0,0 @@ | ||||
| #ifndef BASE_DATA_STRUCT_HASH_H | ||||
| #define BASE_DATA_STRUCT_HASH_H | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| 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) | ||||
| 	__attribute__((__warn_unused_result__)); | ||||
| 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 void *key, uint32_t len); | ||||
| int dm_hash_insert_binary(struct dm_hash_table *t, const void *key, uint32_t len, | ||||
| 			  void *data); | ||||
| void dm_hash_remove_binary(struct dm_hash_table *t, const void *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); | ||||
|  | ||||
| /* | ||||
|  * dm_hash_insert() replaces the value of an existing | ||||
|  * entry with a matching key if one exists.  Otherwise | ||||
|  * it adds a new entry. | ||||
|  * | ||||
|  * dm_hash_insert_with_val() inserts a new entry if | ||||
|  * another entry with the same key already exists. | ||||
|  * val_len is the size of the data being inserted. | ||||
|  * | ||||
|  * If two entries with the same key exist, | ||||
|  * (added using dm_hash_insert_allow_multiple), then: | ||||
|  * . dm_hash_lookup() returns the first one it finds, and | ||||
|  *   dm_hash_lookup_with_val() returns the one with a matching | ||||
|  *   val_len/val. | ||||
|  * . dm_hash_remove() removes the first one it finds, and | ||||
|  *   dm_hash_remove_with_val() removes the one with a matching | ||||
|  *   val_len/val. | ||||
|  * | ||||
|  * If a single entry with a given key exists, and it has | ||||
|  * zero val_len, then: | ||||
|  * . dm_hash_lookup() returns it | ||||
|  * . dm_hash_lookup_with_val(val_len=0) returns it | ||||
|  * . dm_hash_remove() removes it | ||||
|  * . dm_hash_remove_with_val(val_len=0) removes it | ||||
|  * | ||||
|  * dm_hash_lookup_with_count() is a single call that will | ||||
|  * both lookup a key's value and check if there is more | ||||
|  * than one entry with the given key. | ||||
|  * | ||||
|  * (It is not meant to retrieve all the entries with the | ||||
|  * given key.  In the common case where a single entry exists | ||||
|  * for the key, it is useful to have a single call that will | ||||
|  * both look up the value and indicate if multiple values | ||||
|  * exist for the key.) | ||||
|  * | ||||
|  * dm_hash_lookup_with_count: | ||||
|  * . If no entries exist, the function returns NULL, and | ||||
|  *   the count is set to 0. | ||||
|  * . If only one entry exists, the value of that entry is | ||||
|  *   returned and count is set to 1. | ||||
|  * . If N entries exists, the value of the first entry is | ||||
|  *   returned and count is set to N. | ||||
|  */ | ||||
|  | ||||
| void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key, | ||||
|                               const void *val, uint32_t val_len); | ||||
| void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key, | ||||
|                              const void *val, uint32_t val_len); | ||||
| int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key, | ||||
|                                   const void *val, uint32_t val_len); | ||||
| void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count); | ||||
|  | ||||
|  | ||||
| #define dm_hash_iterate(v, h) \ | ||||
| 	for (v = dm_hash_get_first((h)); v; \ | ||||
| 	     v = dm_hash_get_next((h), v)) | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
| @@ -1,170 +0,0 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "list.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| /* | ||||
|  * Initialise a list before use. | ||||
|  * The list head's next and previous pointers point back to itself. | ||||
|  */ | ||||
| void dm_list_init(struct dm_list *head) | ||||
| { | ||||
| 	head->n = head->p = head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Insert an element before 'head'. | ||||
|  * If 'head' is the list head, this adds an element to the end of the list. | ||||
|  */ | ||||
| void dm_list_add(struct dm_list *head, struct dm_list *elem) | ||||
| { | ||||
| 	assert(head->n); | ||||
|  | ||||
| 	elem->n = head; | ||||
| 	elem->p = head->p; | ||||
|  | ||||
| 	head->p->n = elem; | ||||
| 	head->p = elem; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Insert an element after 'head'. | ||||
|  * If 'head' is the list head, this adds an element to the front of the list. | ||||
|  */ | ||||
| void dm_list_add_h(struct dm_list *head, struct dm_list *elem) | ||||
| { | ||||
| 	assert(head->n); | ||||
|  | ||||
| 	elem->n = head->n; | ||||
| 	elem->p = head; | ||||
|  | ||||
| 	head->n->p = elem; | ||||
| 	head->n = elem; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Delete an element from its list. | ||||
|  * Note that this doesn't change the element itself - it may still be safe | ||||
|  * to follow its pointers. | ||||
|  */ | ||||
| void dm_list_del(struct dm_list *elem) | ||||
| { | ||||
| 	elem->n->p = elem->p; | ||||
| 	elem->p->n = elem->n; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Remove an element from existing list and insert before 'head'. | ||||
|  */ | ||||
| void dm_list_move(struct dm_list *head, struct dm_list *elem) | ||||
| { | ||||
|         dm_list_del(elem); | ||||
|         dm_list_add(head, elem); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Is the list empty? | ||||
|  */ | ||||
| int dm_list_empty(const struct dm_list *head) | ||||
| { | ||||
| 	return head->n == head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Is this the first element of the list? | ||||
|  */ | ||||
| int dm_list_start(const struct dm_list *head, const struct dm_list *elem) | ||||
| { | ||||
| 	return elem->p == head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Is this the last element of the list? | ||||
|  */ | ||||
| int dm_list_end(const struct dm_list *head, const struct dm_list *elem) | ||||
| { | ||||
| 	return elem->n == head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return first element of the list or NULL if empty | ||||
|  */ | ||||
| struct dm_list *dm_list_first(const struct dm_list *head) | ||||
| { | ||||
| 	return (dm_list_empty(head) ? NULL : head->n); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return last element of the list or NULL if empty | ||||
|  */ | ||||
| struct dm_list *dm_list_last(const struct dm_list *head) | ||||
| { | ||||
| 	return (dm_list_empty(head) ? NULL : head->p); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return the previous element of the list, or NULL if we've reached the start. | ||||
|  */ | ||||
| struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem) | ||||
| { | ||||
| 	return (dm_list_start(head, elem) ? NULL : elem->p); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return the next element of the list, or NULL if we've reached the end. | ||||
|  */ | ||||
| struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem) | ||||
| { | ||||
| 	return (dm_list_end(head, elem) ? NULL : elem->n); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return the number of elements in a list by walking it. | ||||
|  */ | ||||
| unsigned int dm_list_size(const struct dm_list *head) | ||||
| { | ||||
| 	unsigned int s = 0; | ||||
| 	const struct dm_list *v; | ||||
|  | ||||
| 	dm_list_iterate(v, head) | ||||
| 	    s++; | ||||
|  | ||||
| 	return s; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Join two lists together. | ||||
|  * This moves all the elements of the list 'head1' to the end of the list | ||||
|  * 'head', leaving 'head1' empty. | ||||
|  */ | ||||
| void dm_list_splice(struct dm_list *head, struct dm_list *head1) | ||||
| { | ||||
| 	assert(head->n); | ||||
| 	assert(head1->n); | ||||
|  | ||||
| 	if (dm_list_empty(head1)) | ||||
| 	    return; | ||||
|  | ||||
| 	head1->p->n = head; | ||||
| 	head1->n->p = head->p; | ||||
|  | ||||
| 	head->p->n = head1->n; | ||||
| 	head->p = head1->p; | ||||
|  | ||||
| 	dm_list_init(head1); | ||||
| } | ||||
| @@ -1,209 +0,0 @@ | ||||
| #ifndef BASE_DATA_STRUCT_LIST_H | ||||
| #define BASE_DATA_STRUCT_LIST_H | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|  * 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; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * String list. | ||||
|  */ | ||||
| struct dm_str_list { | ||||
| 	struct dm_list list; | ||||
| 	const char *str; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Initialise a list before use. | ||||
|  * The list head's next and previous pointers point back to itself. | ||||
|  */ | ||||
| #define DM_LIST_HEAD_INIT(name)	 { &(name), &(name) } | ||||
| #define DM_LIST_INIT(name)	struct dm_list name = DM_LIST_HEAD_INIT(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 end 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); | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,256 +0,0 @@ | ||||
| // Copyright (C) 2018 Red Hat, Inc. All rights reserved. | ||||
| //  | ||||
| // This file is part of LVM2. | ||||
| // | ||||
| // This copyrighted material is made available to anyone wishing to use, | ||||
| // modify, copy, or redistribute it subject to the terms and conditions | ||||
| // of the GNU Lesser General Public License v.2.1. | ||||
| // | ||||
| // You should have received a copy of the GNU Lesser 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 | ||||
|  | ||||
| #include "radix-tree.h" | ||||
|  | ||||
| #include "base/memory/container_of.h" | ||||
| #include "base/memory/zalloc.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
| // This implementation is based around nested binary trees.  Very | ||||
| // simple (and hopefully correct). | ||||
|  | ||||
| struct node { | ||||
| 	struct node *left; | ||||
| 	struct node *right; | ||||
|  | ||||
| 	uint8_t key; | ||||
| 	struct node *center; | ||||
|  | ||||
| 	bool has_value; | ||||
| 	union radix_value value; | ||||
| }; | ||||
|  | ||||
| struct radix_tree { | ||||
| 	radix_value_dtr dtr; | ||||
| 	void *dtr_context; | ||||
|  | ||||
| 	struct node *root; | ||||
| }; | ||||
|  | ||||
| struct radix_tree * | ||||
| radix_tree_create(radix_value_dtr dtr, void *dtr_context) | ||||
| { | ||||
| 	struct radix_tree *rt = zalloc(sizeof(*rt)); | ||||
|  | ||||
| 	if (rt) { | ||||
| 		rt->dtr = dtr; | ||||
| 		rt->dtr_context = dtr_context; | ||||
| 	} | ||||
|  | ||||
| 	return rt; | ||||
| } | ||||
|  | ||||
| // Returns the number of entries in the tree | ||||
| static unsigned _destroy_tree(struct node *n, radix_value_dtr dtr, void *context) | ||||
| { | ||||
| 	unsigned r; | ||||
|  | ||||
| 	if (!n) | ||||
| 		return 0; | ||||
|  | ||||
| 	r = _destroy_tree(n->left, dtr, context); | ||||
| 	r += _destroy_tree(n->right, dtr, context); | ||||
| 	r += _destroy_tree(n->center, dtr, context); | ||||
|  | ||||
| 	if (n->has_value) { | ||||
| 		if (dtr) | ||||
| 			dtr(context, n->value); | ||||
| 		r++; | ||||
| 	} | ||||
|  | ||||
| 	free(n); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| void radix_tree_destroy(struct radix_tree *rt) | ||||
| { | ||||
| 	_destroy_tree(rt->root, rt->dtr, rt->dtr_context); | ||||
| 	free(rt); | ||||
| } | ||||
|  | ||||
| static unsigned _count(struct node *n) | ||||
| { | ||||
| 	unsigned r; | ||||
|  | ||||
| 	if (!n) | ||||
| 		return 0; | ||||
|  | ||||
| 	r = _count(n->left); | ||||
| 	r += _count(n->right); | ||||
| 	r += _count(n->center); | ||||
|  | ||||
| 	if (n->has_value) | ||||
| 		r++; | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| unsigned radix_tree_size(struct radix_tree *rt) | ||||
| { | ||||
| 	return _count(rt->root); | ||||
| } | ||||
|  | ||||
| static struct node **_lookup(struct node **pn, uint8_t *kb, uint8_t *ke) | ||||
| { | ||||
| 	struct node *n = *pn; | ||||
|  | ||||
| 	if (!n || (kb == ke)) | ||||
| 		return pn; | ||||
|  | ||||
| 	if (*kb < n->key) | ||||
| 		return _lookup(&n->left, kb, ke); | ||||
|  | ||||
| 	else if (*kb > n->key) | ||||
| 		return _lookup(&n->right, kb, ke); | ||||
|  | ||||
| 	else | ||||
| 		return _lookup(&n->center, kb + 1, ke); | ||||
| } | ||||
|  | ||||
| static bool _insert(struct node **pn, uint8_t *kb, uint8_t *ke, union radix_value v) | ||||
| { | ||||
| 	struct node *n = *pn; | ||||
|  | ||||
| 	if (!n) { | ||||
| 		n = zalloc(sizeof(*n)); | ||||
| 		if (!n) | ||||
| 			return false; | ||||
|  | ||||
| 		n->key = *kb; | ||||
| 		*pn = n; | ||||
| 	} | ||||
|  | ||||
| 	if (kb == ke) { | ||||
| 		n->has_value = true; | ||||
| 		n->value = v; | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	if (*kb < n->key) | ||||
| 		return _insert(&n->left, kb, ke, v); | ||||
|  | ||||
| 	else if (*kb > n->key) | ||||
| 		return _insert(&n->right, kb, ke, v); | ||||
|  | ||||
| 	else | ||||
| 		return _insert(&n->center, kb + 1, ke, v); | ||||
| } | ||||
|  | ||||
| bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value v) | ||||
| { | ||||
| 	return _insert(&rt->root, kb, ke, v); | ||||
| } | ||||
|  | ||||
| bool radix_tree_remove(struct radix_tree *rt, uint8_t *kb, uint8_t *ke) | ||||
| { | ||||
| 	struct node **pn = _lookup(&rt->root, kb, ke); | ||||
| 	struct node *n = *pn; | ||||
|  | ||||
| 	if (!n || !n->has_value) | ||||
| 		return false; | ||||
|  | ||||
| 	else { | ||||
| 		if (rt->dtr) | ||||
| 			rt->dtr(rt->dtr_context, n->value); | ||||
|  | ||||
| 		if (n->left || n->center || n->right) { | ||||
| 			n->has_value = false; | ||||
| 			return true; | ||||
|  | ||||
| 		} else { | ||||
| 			// FIXME: delete parent if this was the last entry | ||||
| 			free(n); | ||||
| 			*pn = NULL; | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| unsigned radix_tree_remove_prefix(struct radix_tree *rt, uint8_t *kb, uint8_t *ke) | ||||
| { | ||||
| 	struct node **pn; | ||||
| 	unsigned count; | ||||
|  | ||||
| 	pn = _lookup(&rt->root, kb, ke); | ||||
|  | ||||
| 	if (*pn) { | ||||
| 		count = _destroy_tree(*pn, rt->dtr, rt->dtr_context); | ||||
| 		*pn = NULL; | ||||
| 	} | ||||
|  | ||||
| 	return count; | ||||
| } | ||||
|  | ||||
| bool | ||||
| radix_tree_lookup(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value *result) | ||||
| { | ||||
| 	struct node **pn = _lookup(&rt->root, kb, ke); | ||||
| 	struct node *n = *pn; | ||||
|  | ||||
| 	if (n && n->has_value) { | ||||
| 		*result = n->value; | ||||
| 		return true; | ||||
| 	} else | ||||
| 		return false; | ||||
| } | ||||
|  | ||||
| static void _iterate(struct node *n, struct radix_tree_iterator *it) | ||||
| { | ||||
| 	if (!n) | ||||
| 		return; | ||||
|  | ||||
| 	_iterate(n->left, it); | ||||
|  | ||||
| 	if (n->has_value) | ||||
| 		// FIXME: fill out the key | ||||
| 		it->visit(it, NULL, NULL, n->value); | ||||
|  | ||||
| 	_iterate(n->center, it); | ||||
| 	_iterate(n->right, it); | ||||
| } | ||||
|  | ||||
| void radix_tree_iterate(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, | ||||
|                         struct radix_tree_iterator *it) | ||||
| { | ||||
| 	if (kb == ke) | ||||
| 		_iterate(rt->root, it); | ||||
|  | ||||
| 	else { | ||||
| 		struct node **pn = _lookup(&rt->root, kb, ke); | ||||
| 		struct node *n = *pn; | ||||
|  | ||||
| 		if (n) { | ||||
| 			if (n->has_value) | ||||
| 				it->visit(it, NULL, NULL, n->value); | ||||
| 			_iterate(n->center, it); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool radix_tree_is_well_formed(struct radix_tree *rt) | ||||
| { | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void radix_tree_dump(struct radix_tree *rt, FILE *out) | ||||
| { | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| @@ -1,21 +0,0 @@ | ||||
| // Copyright (C) 2018 Red Hat, Inc. All rights reserved. | ||||
| //  | ||||
| // This file is part of LVM2. | ||||
| // | ||||
| // This copyrighted material is made available to anyone wishing to use, | ||||
| // modify, copy, or redistribute it subject to the terms and conditions | ||||
| // of the GNU Lesser General Public License v.2.1. | ||||
| // | ||||
| // You should have received a copy of the GNU Lesser 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 | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #ifdef SIMPLE_RADIX_TREE | ||||
| #include "base/data-struct/radix-tree-simple.c" | ||||
| #else | ||||
| #include "base/data-struct/radix-tree-adaptive.c" | ||||
| #endif | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
| @@ -1,64 +0,0 @@ | ||||
| // Copyright (C) 2018 Red Hat, Inc. All rights reserved. | ||||
| //  | ||||
| // This file is part of LVM2. | ||||
| // | ||||
| // This copyrighted material is made available to anyone wishing to use, | ||||
| // modify, copy, or redistribute it subject to the terms and conditions | ||||
| // of the GNU Lesser General Public License v.2.1. | ||||
| // | ||||
| // You should have received a copy of the GNU Lesser 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 | ||||
|   | ||||
| #ifndef BASE_DATA_STRUCT_RADIX_TREE_H | ||||
| #define BASE_DATA_STRUCT_RADIX_TREE_H | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| struct radix_tree; | ||||
|  | ||||
| union radix_value { | ||||
| 	void *ptr; | ||||
| 	uint64_t n; | ||||
| }; | ||||
|  | ||||
| typedef void (*radix_value_dtr)(void *context, union radix_value v); | ||||
|  | ||||
| // dtr will be called on any deleted entries.  dtr may be NULL. | ||||
| struct radix_tree *radix_tree_create(radix_value_dtr dtr, void *dtr_context); | ||||
| void radix_tree_destroy(struct radix_tree *rt); | ||||
|  | ||||
| unsigned radix_tree_size(struct radix_tree *rt); | ||||
| bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value v); | ||||
| bool radix_tree_remove(struct radix_tree *rt, uint8_t *kb, uint8_t *ke); | ||||
|  | ||||
| // Returns the number of values removed | ||||
| unsigned radix_tree_remove_prefix(struct radix_tree *rt, uint8_t *prefix_b, uint8_t *prefix_e); | ||||
|  | ||||
| bool radix_tree_lookup(struct radix_tree *rt, | ||||
| 		       uint8_t *kb, uint8_t *ke, union radix_value *result); | ||||
|  | ||||
| // The radix tree stores entries in lexicographical order.  Which means | ||||
| // we can iterate entries, in order.  Or iterate entries with a particular | ||||
| // prefix. | ||||
| struct radix_tree_iterator { | ||||
|         // Returns false if the iteration should end. | ||||
| 	bool (*visit)(struct radix_tree_iterator *it, | ||||
|                       uint8_t *kb, uint8_t *ke, union radix_value v); | ||||
| }; | ||||
|  | ||||
| void radix_tree_iterate(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, | ||||
|                         struct radix_tree_iterator *it); | ||||
|  | ||||
| // Checks that some constraints on the shape of the tree are | ||||
| // being held.  For debug only. | ||||
| bool radix_tree_is_well_formed(struct radix_tree *rt); | ||||
| void radix_tree_dump(struct radix_tree *rt, FILE *out); | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
| @@ -1,23 +0,0 @@ | ||||
| // Copyright (C) 2018 Red Hat, Inc. All rights reserved. | ||||
| //  | ||||
| // This file is part of LVM2. | ||||
| // | ||||
| // This copyrighted material is made available to anyone wishing to use, | ||||
| // modify, copy, or redistribute it subject to the terms and conditions | ||||
| // of the GNU Lesser General Public License v.2.1. | ||||
| // | ||||
| // You should have received a copy of the GNU Lesser 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 | ||||
|  | ||||
| #ifndef BASE_MEMORY_CONTAINER_OF_H | ||||
| #define BASE_MEMORY_CONTAINER_OF_H | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #define container_of(v, t, head) \ | ||||
|     ((t *)((const char *)(v) - (const char *)&((t *) 0)->head)) | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
| @@ -1,27 +0,0 @@ | ||||
| // Copyright (C) 2018 Red Hat, Inc. All rights reserved. | ||||
| //  | ||||
| // This file is part of LVM2. | ||||
| // | ||||
| // This copyrighted material is made available to anyone wishing to use, | ||||
| // modify, copy, or redistribute it subject to the terms and conditions | ||||
| // of the GNU Lesser General Public License v.2.1. | ||||
| // | ||||
| // You should have received a copy of the GNU Lesser 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 | ||||
|  | ||||
| #ifndef BASE_MEMORY_ZALLOC_H | ||||
| #define BASE_MEMORY_ZALLOC_H | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| static inline void *zalloc(size_t len) | ||||
| { | ||||
| 	return calloc(1, len); | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
| @@ -1,5 +1,5 @@ | ||||
| # | ||||
| # Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| @@ -25,7 +25,6 @@ PROFILES=$(PROFILE_TEMPLATES) \ | ||||
| 	$(srcdir)/cache-smq.profile \ | ||||
| 	$(srcdir)/thin-generic.profile \ | ||||
| 	$(srcdir)/thin-performance.profile \ | ||||
| 	$(srcdir)/vdo-small.profile \ | ||||
| 	$(srcdir)/lvmdbusd.profile | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
| @@ -33,8 +32,8 @@ include $(top_builddir)/make.tmpl | ||||
| .PHONY: install_conf install_localconf install_profiles | ||||
|  | ||||
| generate: | ||||
| 	$(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withgeneralpreamble --withcomments --ignorelocal --withspaces > example.conf.in | ||||
| 	$(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withlocalpreamble --withcomments --withspaces local > lvmlocal.conf.in | ||||
| 	(cat $(top_srcdir)/conf/example.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --ignorelocal --withspaces) > example.conf.in | ||||
| 	(cat $(top_srcdir)/conf/lvmlocal.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --withspaces local) > lvmlocal.conf.in | ||||
|  | ||||
| install_conf: $(CONFSRC) | ||||
| 	@if [ ! -e $(confdir)/$(CONFDEST) ]; then \ | ||||
| @@ -49,9 +48,8 @@ install_localconf: $(CONFLOCAL) | ||||
| 	fi | ||||
|  | ||||
| install_profiles: $(PROFILES) | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_DIR) $(profiledir) | ||||
| 	$(Q) $(INSTALL_DATA) $(PROFILES) $(profiledir)/ | ||||
| 	$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_PROFILE_DIR) | ||||
| 	$(INSTALL_DATA) $(PROFILES) $(DESTDIR)$(DEFAULT_PROFILE_DIR)/ | ||||
|  | ||||
| install_lvm2: install_conf install_localconf install_profiles | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,6 @@ allocation { | ||||
| 	cache_mode = "writethrough" | ||||
| 	cache_policy = "smq" | ||||
| 	cache_settings { | ||||
| 	        # currently no settings for "smq" policy | ||||
| 	        # currently no settins for "smq" policy | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										23
									
								
								conf/example.conf.base
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								conf/example.conf.base
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # 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. | ||||
| # | ||||
| # Refer to 'man lvm.conf' for information about how settings configured in | ||||
| # this file are combined with built-in values and command line options to | ||||
| # arrive at the final values used by LVM. | ||||
| # | ||||
| # Refer to 'man lvmconfig' for information about displaying the built-in | ||||
| # and configured values used by LVM. | ||||
| # | ||||
| # If a default value is set in this file (not commented out), then a | ||||
| # new version of LVM using this file will continue using that value, | ||||
| # even if the new version of LVM changes the built-in default value. | ||||
| # | ||||
| # To put this file in a different directory and override @DEFAULT_SYS_DIR@ set | ||||
| # the environment variable LVM_SYSTEM_DIR before running the tools. | ||||
| # | ||||
| # N.B. Take care that each setting only appears once if uncommenting | ||||
| # example settings in this file. | ||||
|  | ||||
| @@ -88,22 +88,6 @@ devices { | ||||
| 	#  | ||||
| 	external_device_info_source = "none" | ||||
|  | ||||
| 	# Configuration option devices/hints. | ||||
| 	# Use a local file to remember which devices have PVs on them. | ||||
| 	# Some commands will use this as an optimization to reduce device | ||||
| 	# scanning, and will only scan the listed PVs. Removing the hint file | ||||
| 	# will cause lvm to generate a new one. Disable hints if PVs will | ||||
| 	# be copied onto devices using non-lvm commands, like dd. | ||||
| 	#  | ||||
| 	# Accepted values: | ||||
| 	#   all | ||||
| 	#     Use all hints. | ||||
| 	#   none | ||||
| 	#     Use no hints. | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# hints = "all" | ||||
|  | ||||
| 	# Configuration option devices/preferred_names. | ||||
| 	# Select which path name to display for a block device. | ||||
| 	# If multiple path names exist for a block device, and LVM needs to | ||||
| @@ -139,10 +123,11 @@ devices { | ||||
| 	# then the device is accepted. Be careful mixing 'a' and 'r' patterns, | ||||
| 	# as the combination might produce unexpected results (test changes.) | ||||
| 	# Run vgscan after changing the filter to regenerate the cache. | ||||
| 	# See the use_lvmetad comment for a special case regarding filters. | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# Accept every block device: | ||||
| 	# filter = [ "a|.*|" ] | ||||
| 	# filter = [ "a|.*/|" ] | ||||
| 	# Reject the cdrom drive: | ||||
| 	# filter = [ "r|/dev/cdrom|" ] | ||||
| 	# Work with just loopback devices, e.g. for testing: | ||||
| @@ -150,20 +135,38 @@ devices { | ||||
| 	# Accept all loop devices and ide drives except hdc: | ||||
| 	# filter = [ "a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|" ] | ||||
| 	# Use anchors to be very specific: | ||||
| 	# filter = [ "a|^/dev/hda8$|", "r|.*|" ] | ||||
| 	# filter = [ "a|^/dev/hda8$|", "r|.*/|" ] | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# filter = [ "a|.*|" ] | ||||
| 	# filter = [ "a|.*/|" ] | ||||
|  | ||||
| 	# Configuration option devices/global_filter. | ||||
| 	# Limit the block devices that are used by LVM system components. | ||||
| 	# Because devices/filter may be overridden from the command line, it is | ||||
| 	# not suitable for system-wide device filtering, e.g. udev. | ||||
| 	# not suitable for system-wide device filtering, e.g. udev and lvmetad. | ||||
| 	# Use global_filter to hide devices from these LVM system components. | ||||
| 	# The syntax is the same as devices/filter. Devices rejected by | ||||
| 	# global_filter are not opened by LVM. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# global_filter = [ "a|.*|" ] | ||||
| 	# global_filter = [ "a|.*/|" ] | ||||
|  | ||||
| 	# Configuration option devices/cache_dir. | ||||
| 	# Directory in which to store the device cache file. | ||||
| 	# The results of filtering are cached on disk to avoid rescanning dud | ||||
| 	# devices (which can take a very long time). By default this cache is | ||||
| 	# stored in a file named .cache. It is safe to delete this file; the | ||||
| 	# tools regenerate it. If obtain_device_list_from_udev is enabled, the | ||||
| 	# list of devices is obtained from udev and any existing .cache file | ||||
| 	# is removed. | ||||
| 	cache_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@" | ||||
|  | ||||
| 	# Configuration option devices/cache_file_prefix. | ||||
| 	# A prefix used before the .cache file name. See devices/cache_dir. | ||||
| 	cache_file_prefix = "" | ||||
|  | ||||
| 	# Configuration option devices/write_cache_state. | ||||
| 	# Enable/disable writing the cache file. See devices/cache_dir. | ||||
| 	write_cache_state = 1 | ||||
|  | ||||
| 	# Configuration option devices/types. | ||||
| 	# List of additional acceptable block device types. | ||||
| @@ -182,53 +185,14 @@ devices { | ||||
| 	# present on the system. sysfs must be part of the kernel and mounted.) | ||||
| 	sysfs_scan = 1 | ||||
|  | ||||
| 	# Configuration option devices/scan_lvs. | ||||
| 	# Scan LVM LVs for layered PVs, allowing LVs to be used as PVs. | ||||
| 	# When 1, LVM will detect PVs layered on LVs, and caution must be | ||||
| 	# taken to avoid a host accessing a layered VG that may not belong | ||||
| 	# to it, e.g. from a guest image. This generally requires excluding | ||||
| 	# the LVs with device filters. Also, when this setting is enabled, | ||||
| 	# every LVM command will scan every active LV on the system (unless | ||||
| 	# filtered), which can cause performance problems on systems with | ||||
| 	# many active LVs. When this setting is 0, LVM will not detect or | ||||
| 	# use PVs that exist on LVs, and will not allow a PV to be created on | ||||
| 	# an LV. The LVs are ignored using a built in device filter that | ||||
| 	# identifies and excludes LVs. | ||||
| 	scan_lvs = 0 | ||||
|  | ||||
| 	# Configuration option devices/multipath_component_detection. | ||||
| 	# Ignore devices that are components of DM multipath devices. | ||||
| 	multipath_component_detection = 1 | ||||
|  | ||||
| 	# Configuration option devices/md_component_detection. | ||||
| 	# Enable detection and exclusion of MD component devices. | ||||
| 	# An MD component device is a block device that MD uses as part | ||||
| 	# of a software RAID virtual device. When an LVM PV is created | ||||
| 	# on an MD device, LVM must only use the top level MD device as | ||||
| 	# the PV, and should ignore the underlying component devices. | ||||
| 	# In cases where the MD superblock is located at the end of the | ||||
| 	# component devices, it is more difficult for LVM to consistently | ||||
| 	# identify an MD component, see the md_component_checks setting. | ||||
| 	# Ignore devices that are components of software RAID (md) devices. | ||||
| 	md_component_detection = 1 | ||||
|  | ||||
| 	# Configuration option devices/md_component_checks. | ||||
| 	# The checks LVM should use to detect MD component devices. | ||||
| 	# MD component devices are block devices used by MD software RAID. | ||||
| 	#  | ||||
| 	# Accepted values: | ||||
| 	#   auto | ||||
| 	#     LVM will skip scanning the end of devices when it has other | ||||
| 	#     indications that the device is not an MD component. | ||||
| 	#   start | ||||
| 	#     LVM will only scan the start of devices for MD superblocks. | ||||
| 	#     This does not incur extra I/O by LVM. | ||||
| 	#   full | ||||
| 	#     LVM will scan the start and end of devices for MD superblocks. | ||||
| 	#     This requires an extra read at the end of devices. | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# md_component_checks = "auto" | ||||
|  | ||||
| 	# Configuration option devices/fw_raid_component_detection. | ||||
| 	# Ignore devices that are components of firmware RAID devices. | ||||
| 	# LVM must use an external_device_info_source other than none for this | ||||
| @@ -236,24 +200,19 @@ devices { | ||||
| 	fw_raid_component_detection = 0 | ||||
|  | ||||
| 	# Configuration option devices/md_chunk_alignment. | ||||
| 	# Align the start of a PV data area with md device's stripe-width. | ||||
| 	# Align PV data blocks with md device's stripe-width. | ||||
| 	# This applies if a PV is placed directly on an md device. | ||||
| 	# default_data_alignment will be overriden if it is not aligned | ||||
| 	# with the value detected for this setting. | ||||
| 	# This setting is overriden by data_alignment_detection, | ||||
| 	# data_alignment, and the --dataalignment option. | ||||
| 	md_chunk_alignment = 1 | ||||
|  | ||||
| 	# Configuration option devices/default_data_alignment. | ||||
| 	# Align the start of a PV data area with this number of MiB. | ||||
| 	# Set to 1 for 1MiB, 2 for 2MiB, etc. Set to 0 to disable. | ||||
| 	# This setting is overriden by data_alignment and the --dataalignment | ||||
| 	# option. | ||||
| 	# Default alignment of the start of a PV data area in MB. | ||||
| 	# If set to 0, a value of 64KiB will be used. | ||||
| 	# Set to 1 for 1MiB, 2 for 2MiB, etc. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# default_data_alignment = 1 | ||||
|  | ||||
| 	# Configuration option devices/data_alignment_detection. | ||||
| 	# Align the start of a PV data area with sysfs io properties. | ||||
| 	# Detect PV data alignment based on sysfs device information. | ||||
| 	# The start of a PV data area will be a multiple of 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 | ||||
| @@ -261,29 +220,27 @@ devices { | ||||
| 	# preferred unit of receiving I/O, e.g. MD 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. | ||||
| 	# default_data_alignment and md_chunk_alignment will be overriden | ||||
| 	# if they are not aligned with the value detected for this setting. | ||||
| 	# This setting is overriden by data_alignment and the --dataalignment | ||||
| 	# option. | ||||
| 	# This setting takes precedence over md_chunk_alignment. | ||||
| 	data_alignment_detection = 1 | ||||
|  | ||||
| 	# Configuration option devices/data_alignment. | ||||
| 	# Align the start of a PV data area with this number of KiB. | ||||
| 	# When non-zero, this setting overrides default_data_alignment. | ||||
| 	# Set to 0 to disable, in which case default_data_alignment | ||||
| 	# is used to align the first PE in units of MiB. | ||||
| 	# This setting is overriden by the --dataalignment option. | ||||
| 	# Alignment of the start of a PV data area in KiB. | ||||
| 	# If a PV is placed directly on an md device and md_chunk_alignment or | ||||
| 	# data_alignment_detection are enabled, then this setting is ignored. | ||||
| 	# Otherwise, md_chunk_alignment and data_alignment_detection are | ||||
| 	# disabled if this is set. Set to 0 to use the default alignment or the | ||||
| 	# page size, if larger. | ||||
| 	data_alignment = 0 | ||||
|  | ||||
| 	# Configuration option devices/data_alignment_offset_detection. | ||||
| 	# Shift the start of an aligned PV data area based on sysfs information. | ||||
| 	# After a PV data area is aligned, it will be shifted by the | ||||
| 	# Detect PV data alignment offset based on sysfs device information. | ||||
| 	# The start of a PV aligned data area will be shifted by the | ||||
| 	# alignment_offset exposed in sysfs. This offset is often 0, but may | ||||
| 	# be non-zero. Certain 4KiB sector drives that compensate for windows | ||||
| 	# partitioning will have an alignment_offset of 3584 bytes (sector 7 | ||||
| 	# is the lowest aligned logical block, the 4KiB sectors start at | ||||
| 	# LBA -1, and consequently sector 63 is aligned on a 4KiB boundary). | ||||
| 	# This setting is overriden by the --dataalignmentoffset option. | ||||
| 	# pvcreate --dataalignmentoffset will skip this detection. | ||||
| 	data_alignment_offset_detection = 1 | ||||
|  | ||||
| 	# Configuration option devices/ignore_suspended_devices. | ||||
| @@ -311,6 +268,14 @@ devices { | ||||
| 	# different way, making them a better choice for VG stacking. | ||||
| 	ignore_lvm_mirrors = 1 | ||||
|  | ||||
| 	# Configuration option devices/disable_after_error_count. | ||||
| 	# Number of I/O errors after which a device is skipped. | ||||
| 	# During each LVM operation, errors received from each device are | ||||
| 	# counted. If the counter of a device exceeds the limit set here, | ||||
| 	# no further I/O is sent to that device for the remainder of the | ||||
| 	# operation. Setting this to 0 disables the counters altogether. | ||||
| 	disable_after_error_count = 0 | ||||
|  | ||||
| 	# Configuration option devices/require_restorefile_with_uuid. | ||||
| 	# Allow use of pvcreate --uuid without requiring --restorefile. | ||||
| 	require_restorefile_with_uuid = 1 | ||||
| @@ -347,12 +312,6 @@ devices { | ||||
| 	# Enabling this setting allows the VG to be used as usual even with | ||||
| 	# uncertain devices. | ||||
| 	allow_changes_with_duplicate_pvs = 0 | ||||
|  | ||||
| 	# Configuration option devices/allow_mixed_block_sizes. | ||||
| 	# Allow PVs in the same VG with different logical block sizes. | ||||
| 	# When allowed, the user is responsible to ensure that an LV is | ||||
| 	# using PVs with matching block sizes when necessary. | ||||
| 	allow_mixed_block_sizes = 0 | ||||
| } | ||||
|  | ||||
| # Configuration section allocation. | ||||
| @@ -387,7 +346,7 @@ allocation { | ||||
| 	maximise_cling = 1 | ||||
|  | ||||
| 	# Configuration option allocation/use_blkid_wiping. | ||||
| 	# Use blkid to detect and erase existing signatures on new PVs and LVs. | ||||
| 	# Use blkid to detect existing signatures on new PVs and LVs. | ||||
| 	# The blkid library can detect more signatures than the native LVM | ||||
| 	# detection code, but may take longer. LVM needs to be compiled with | ||||
| 	# blkid wiping support for this setting to apply. LVM native detection | ||||
| @@ -420,9 +379,8 @@ allocation { | ||||
|  | ||||
| 	# Configuration option allocation/raid_stripe_all_devices. | ||||
| 	# Stripe across all PVs when RAID stripes are not specified. | ||||
| 	# If enabled, all PVs in the VG or on the command line are used for | ||||
| 	# raid0/4/5/6/10 when the command does not specify the number of | ||||
| 	# stripes to use. | ||||
| 	# If enabled, all PVs in the VG or on the command line are used for raid0/4/5/6/10 | ||||
| 	# when the command does not specify the number of stripes to use. | ||||
| 	# This was the default behaviour until release 2.02.162. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# raid_stripe_all_devices = 0 | ||||
| @@ -431,17 +389,6 @@ allocation { | ||||
| 	# Cache pool metadata and data will always use different PVs. | ||||
| 	cache_pool_metadata_require_separate_pvs = 0 | ||||
|  | ||||
| 	# Configuration option allocation/cache_metadata_format. | ||||
| 	# Sets default metadata format for new cache. | ||||
| 	#  | ||||
| 	# Accepted values: | ||||
| 	#   0  Automatically detected best available format | ||||
| 	#   1  Original format | ||||
| 	#   2  Improved 2nd. generation format | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# cache_metadata_format = 0 | ||||
|  | ||||
| 	# Configuration option allocation/cache_mode. | ||||
| 	# The default cache mode used for new cache. | ||||
| 	#  | ||||
| @@ -458,7 +405,7 @@ allocation { | ||||
|  | ||||
| 	# Configuration option allocation/cache_policy. | ||||
| 	# The default cache policy used for new cache volume. | ||||
| 	# Since kernel 4.2 the default policy is smq (Stochastic multiqueue), | ||||
| 	# Since kernel 4.2 the default policy is smq (Stochastic multique), | ||||
| 	# otherwise the older mq (Multiqueue) policy is selected. | ||||
| 	# This configuration option does not have a default value defined. | ||||
|  | ||||
| @@ -539,154 +486,6 @@ allocation { | ||||
| 	# Default physical extent size in KiB to use for new VGs. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# physical_extent_size = 4096 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_use_compression. | ||||
| 	# Enables or disables compression when creating a VDO volume. | ||||
| 	# Compression may be disabled if necessary to maximize performance | ||||
| 	# or to speed processing of data that is unlikely to compress. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_use_compression = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_use_deduplication. | ||||
| 	# Enables or disables deduplication when creating a VDO volume. | ||||
| 	# Deduplication may be disabled in instances where data is not expected | ||||
| 	# to have good deduplication rates but compression is still desired. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_use_deduplication = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_use_metadata_hints. | ||||
| 	# Enables or disables whether VDO volume should tag its latency-critical | ||||
| 	# writes with the REQ_SYNC flag. Some device mapper targets such as dm-raid5 | ||||
| 	# process writes with this flag at a higher priority. | ||||
| 	# Default is enabled. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_use_metadata_hints = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_minimum_io_size. | ||||
| 	# The minimum IO size for VDO volume to accept, in bytes. | ||||
| 	# Valid values are 512 or 4096. The recommended and default value is 4096. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_minimum_io_size = 4096 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_block_map_cache_size_mb. | ||||
| 	# Specifies the amount of memory in MiB allocated for caching block map | ||||
| 	# pages for VDO volume. The value must be a multiple of 4096 and must be | ||||
| 	# at least 128MiB and less than 16TiB. The cache must be at least 16MiB | ||||
| 	# per logical thread. Note that there is a memory overhead of 15%. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_block_map_cache_size_mb = 128 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_block_map_period. | ||||
| 	# The speed with which the block map cache writes out modified block map pages. | ||||
| 	# A smaller era length is likely to reduce the amount time spent rebuilding, | ||||
| 	# at the cost of increased block map writes during normal operation. | ||||
| 	# The maximum and recommended value is 16380; the minimum value is 1. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_block_map_period = 16380 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_check_point_frequency. | ||||
| 	# The default check point frequency for VDO volume. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_check_point_frequency = 0 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_use_sparse_index. | ||||
| 	# Enables sparse indexing for VDO volume. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_use_sparse_index = 0 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_index_memory_size_mb. | ||||
| 	# Specifies the amount of index memory in MiB for VDO volume. | ||||
| 	# The value must be at least 256MiB and at most 1TiB. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_index_memory_size_mb = 256 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_slab_size_mb. | ||||
| 	# Specifies the size in MiB of the increment by which a VDO is grown. | ||||
| 	# Using a smaller size constrains the total maximum physical size | ||||
| 	# that can be accommodated. Must be a power of two between 128MiB and 32GiB. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_slab_size_mb = 2048 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_ack_threads. | ||||
| 	# Specifies the number of threads to use for acknowledging | ||||
| 	# completion of requested VDO I/O operations. | ||||
| 	# The value must be at in range [0..100]. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_ack_threads = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_bio_threads. | ||||
| 	# Specifies the number of threads to use for submitting I/O | ||||
| 	# operations to the storage device of VDO volume. | ||||
| 	# The value must be in range [1..100] | ||||
| 	# Each additional thread after the first will use an additional 18MiB of RAM, | ||||
| 	# plus 1.12 MiB of RAM per megabyte of configured read cache size. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_bio_threads = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_bio_rotation. | ||||
| 	# Specifies the number of I/O operations to enqueue for each bio-submission | ||||
| 	# thread before directing work to the next. The value must be in range [1..1024]. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_bio_rotation = 64 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_cpu_threads. | ||||
| 	# Specifies the number of threads to use for CPU-intensive work such as | ||||
| 	# hashing or compression for VDO volume. The value must be in range [1..100] | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_cpu_threads = 2 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_hash_zone_threads. | ||||
| 	# Specifies the number of threads across which to subdivide parts of the VDO | ||||
| 	# processing based on the hash value computed from the block data. | ||||
| 	# The value must be at in range [0..100]. | ||||
| 	# vdo_hash_zone_threads, vdo_logical_threads and vdo_physical_threads must be | ||||
| 	# either all zero or all non-zero. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_hash_zone_threads = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_logical_threads. | ||||
| 	# Specifies the number of threads across which to subdivide parts of the VDO | ||||
| 	# processing based on the hash value computed from the block data. | ||||
| 	# A logical thread count of 9 or more will require explicitly specifying | ||||
| 	# a sufficiently large block map cache size, as well. | ||||
| 	# The value must be in range [0..100]. | ||||
| 	# vdo_hash_zone_threads, vdo_logical_threads and vdo_physical_threads must be | ||||
| 	# either all zero or all non-zero. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_logical_threads = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_physical_threads. | ||||
| 	# Specifies the number of threads across which to subdivide parts of the VDO | ||||
| 	# processing based on physical block addresses. | ||||
| 	# Each additional thread after the first will use an additional 10MiB of RAM. | ||||
| 	# The value must be in range [0..16]. | ||||
| 	# vdo_hash_zone_threads, vdo_logical_threads and vdo_physical_threads must be | ||||
| 	# either all zero or all non-zero. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_physical_threads = 1 | ||||
|  | ||||
| 	# Configuration option allocation/vdo_write_policy. | ||||
| 	# Specifies the write policy: | ||||
| 	# auto  - VDO will check the storage device and determine whether it supports flushes. | ||||
| 	#         If it does, VDO will run in async mode, otherwise it will run in sync mode. | ||||
| 	# sync  - Writes are acknowledged only after data is stably written. | ||||
| 	#         This policy is not supported if the underlying storage is not also synchronous. | ||||
| 	# async - Writes are acknowledged after data has been cached for writing to stable storage. | ||||
| 	#         Data which has not been flushed is not guaranteed to persist in this mode. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_write_policy = "auto" | ||||
|  | ||||
| 	# Configuration option allocation/vdo_max_discard. | ||||
| 	# Specified te maximum size of discard bio accepted, in 4096 byte blocks. | ||||
| 	# I/O requests to a VDO volume are normally split into 4096-byte blocks, | ||||
| 	# and processed up to 2048 at a time. However, discard requests to a VDO volume | ||||
| 	# can be automatically split to a larger size, up to <max discard> 4096-byte blocks | ||||
| 	# in a single bio, and are limited to 1500 at a time. | ||||
| 	# Increasing this value may provide better overall performance, at the cost of | ||||
| 	# increased latency for the individual discard requests. | ||||
| 	# The default and minimum is 1. The maximum is UINT_MAX / 4096. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_max_discard = 1 | ||||
| } | ||||
|  | ||||
| # Configuration section log. | ||||
| @@ -777,8 +576,7 @@ log { | ||||
|  | ||||
| 	# Configuration option log/indent. | ||||
| 	# Indent messages according to their severity. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# indent = 0 | ||||
| 	indent = 1 | ||||
|  | ||||
| 	# Configuration option log/command_names. | ||||
| 	# Display the command name on each line of output. | ||||
| @@ -801,23 +599,9 @@ log { | ||||
| 	# Select log messages by class. | ||||
| 	# Some debugging messages are assigned to a class and only appear in | ||||
| 	# debug output if the class is listed here. Classes currently | ||||
| 	# available: memory, devices, io, activation, allocation, | ||||
| 	# available: memory, devices, activation, allocation, lvmetad, | ||||
| 	# metadata, cache, locking, lvmpolld. Use "all" to see everything. | ||||
| 	debug_classes = [ "memory", "devices", "io", "activation", "allocation", "metadata", "cache", "locking", "lvmpolld", "dbus" ] | ||||
|  | ||||
| 	# Configuration option log/debug_file_fields. | ||||
| 	# The fields included in debug output written to log file. | ||||
| 	# Use "all" to include everything (the default). | ||||
| 	# This configuration option is advanced. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# debug_file_fields = [ "time", "command", "fileline", "message" ] | ||||
|  | ||||
| 	# Configuration option log/debug_output_fields. | ||||
| 	# The fields included in debug output written to stderr. | ||||
| 	# Use "all" to include everything (the default). | ||||
| 	# This configuration option is advanced. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# debug_output_fields = [ "time", "command", "fileline", "message" ] | ||||
| 	debug_classes = [ "memory", "devices", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld", "dbus" ] | ||||
| } | ||||
|  | ||||
| # Configuration section backup. | ||||
| @@ -905,6 +689,35 @@ global { | ||||
| 	# the error messages. | ||||
| 	activation = 1 | ||||
|  | ||||
| 	# Configuration option global/fallback_to_lvm1. | ||||
| 	# Try running LVM1 tools if LVM cannot communicate with DM. | ||||
| 	# This option only applies to 2.4 kernels and is provided to help | ||||
| 	# switch between device-mapper kernels and LVM1 kernels. The LVM1 | ||||
| 	# tools need to be installed with .lvm1 suffices, e.g. vgscan.lvm1. | ||||
| 	# They will stop working once the lvm2 on-disk metadata format is used. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# fallback_to_lvm1 = @DEFAULT_FALLBACK_TO_LVM1@ | ||||
|  | ||||
| 	# Configuration option global/format. | ||||
| 	# The default metadata format that commands should use. | ||||
| 	# The -M 1|2 option overrides this setting. | ||||
| 	#  | ||||
| 	# Accepted values: | ||||
| 	#   lvm1 | ||||
| 	#   lvm2 | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# format = "lvm2" | ||||
|  | ||||
| 	# Configuration option global/format_libraries. | ||||
| 	# Shared libraries that process different metadata formats. | ||||
| 	# If support for LVM1 metadata was compiled as a shared library use | ||||
| 	# format_libraries = "liblvm2format1.so" | ||||
| 	# This configuration option does not have a default value defined. | ||||
|  | ||||
| 	# Configuration option global/segment_libraries. | ||||
| 	# This configuration option does not have a default value defined. | ||||
|  | ||||
| 	# Configuration option global/proc. | ||||
| 	# Location of proc filesystem. | ||||
| 	# This configuration option is advanced. | ||||
| @@ -914,10 +727,57 @@ global { | ||||
| 	# Location of /etc system configuration directory. | ||||
| 	etc = "@CONFDIR@" | ||||
|  | ||||
| 	# Configuration option global/locking_type. | ||||
| 	# Type of locking to use. | ||||
| 	#  | ||||
| 	# Accepted values: | ||||
| 	#   0 | ||||
| 	#     Turns off locking. Warning: this risks metadata corruption if | ||||
| 	#     commands run concurrently. | ||||
| 	#   1 | ||||
| 	#     LVM uses local file-based locking, the standard mode. | ||||
| 	#   2 | ||||
| 	#     LVM uses the external shared library locking_library. | ||||
| 	#   3 | ||||
| 	#     LVM uses built-in clustered locking with clvmd. | ||||
| 	#     This is incompatible with lvmetad. If use_lvmetad is enabled, | ||||
| 	#     LVM prints a warning and disables lvmetad use. | ||||
| 	#   4 | ||||
| 	#     LVM uses read-only locking which forbids any operations that | ||||
| 	#     might change metadata. | ||||
| 	#   5 | ||||
| 	#     Offers dummy locking for tools that do not need any locks. | ||||
| 	#     You should not need to set this directly; the tools will select | ||||
| 	#     when to use it instead of the configured locking_type. | ||||
| 	#     Do not use lvmetad or the kernel device-mapper driver with this | ||||
| 	#     locking type. It is used by the --readonly option that offers | ||||
| 	#     read-only access to Volume Group metadata that cannot be locked | ||||
| 	#     safely because it belongs to an inaccessible domain and might be | ||||
| 	#     in use, for example a virtual machine image or a disk that is | ||||
| 	#     shared by a clustered machine. | ||||
| 	#  | ||||
| 	locking_type = 1 | ||||
|  | ||||
| 	# Configuration option global/wait_for_locks. | ||||
| 	# When disabled, fail if a lock request would block. | ||||
| 	wait_for_locks = 1 | ||||
|  | ||||
| 	# Configuration option global/fallback_to_clustered_locking. | ||||
| 	# Attempt to use built-in cluster locking if locking_type 2 fails. | ||||
| 	# If using external locking (type 2) and initialisation fails, with | ||||
| 	# this enabled, an attempt will be made to use the built-in clustered | ||||
| 	# locking. Disable this if using a customised locking_library. | ||||
| 	fallback_to_clustered_locking = 1 | ||||
|  | ||||
| 	# Configuration option global/fallback_to_local_locking. | ||||
| 	# Use locking_type 1 (local) if locking_type 2 or 3 fail. | ||||
| 	# If an attempt to initialise type 2 or type 3 locking failed, perhaps | ||||
| 	# because cluster components such as clvmd are not running, with this | ||||
| 	# enabled, an attempt will be made to use local file-based locking | ||||
| 	# (type 1). If this succeeds, only commands against local VGs will | ||||
| 	# proceed. VGs marked as clustered will be ignored. | ||||
| 	fallback_to_local_locking = 1 | ||||
|  | ||||
| 	# Configuration option global/locking_dir. | ||||
| 	# Directory to use for LVM command file locks. | ||||
| 	# Local non-LV directory that holds file-based locks while commands are | ||||
| @@ -938,12 +798,24 @@ global { | ||||
| 	# Search this directory first for shared libraries. | ||||
| 	# This configuration option does not have a default value defined. | ||||
|  | ||||
| 	# Configuration option global/locking_library. | ||||
| 	# The external locking library to use for locking_type 2. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# locking_library = "liblvm2clusterlock.so" | ||||
|  | ||||
| 	# Configuration option global/abort_on_internal_errors. | ||||
| 	# Abort a command that encounters an internal error. | ||||
| 	# 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 | ||||
|  | ||||
| 	# Configuration option global/detect_internal_vg_cache_corruption. | ||||
| 	# Internal verification of VG structures. | ||||
| 	# Check if CRC matches when a parsed VG is used multiple times. This | ||||
| 	# is useful to catch unexpected changes to cached VG structures. | ||||
| 	# Please only enable for debugging. | ||||
| 	detect_internal_vg_cache_corruption = 0 | ||||
|  | ||||
| 	# Configuration option global/metadata_read_only. | ||||
| 	# No operations that change on-disk metadata are permitted. | ||||
| 	# Additionally, read-only commands that encounter metadata in need of | ||||
| @@ -978,16 +850,6 @@ global { | ||||
| 	#  | ||||
| 	mirror_segtype_default = "@DEFAULT_MIRROR_SEGTYPE@" | ||||
|  | ||||
| 	# Configuration option global/support_mirrored_mirror_log. | ||||
| 	# Enable mirrored 'mirror' log type for testing. | ||||
| 	#  | ||||
| 	# This type is deprecated to create or convert to but can | ||||
| 	# be enabled to test that activation of existing mirrored | ||||
| 	# logs and conversion to disk/core works. | ||||
| 	#  | ||||
| 	# Not supported for regular operation! | ||||
| 	support_mirrored_mirror_log = 0 | ||||
|  | ||||
| 	# Configuration option global/raid10_segtype_default. | ||||
| 	# The segment type used by the -i -m combination. | ||||
| 	# The --type raid10|mirror option overrides this setting. | ||||
| @@ -1036,20 +898,41 @@ global { | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# lvdisplay_shows_full_device_path = 0 | ||||
|  | ||||
| 	# Configuration option global/event_activation. | ||||
| 	# Activate LVs based on system-generated device events. | ||||
| 	# When a device appears on the system, a system-generated event runs | ||||
| 	# the pvscan command to activate LVs if the new PV completes the VG. | ||||
| 	# Use auto_activation_volume_list to select which LVs should be | ||||
| 	# activated from these events (the default is all.) | ||||
| 	# When event_activation is disabled, the system will generally run | ||||
| 	# a direct activation command to activate LVs in complete VGs. | ||||
| 	event_activation = 1 | ||||
| 	# Configuration option global/use_lvmetad. | ||||
| 	# Use lvmetad to cache metadata and reduce disk scanning. | ||||
| 	# When enabled (and running), lvmetad provides LVM commands with VG | ||||
| 	# metadata and PV state. LVM commands then avoid reading this | ||||
| 	# information from disks which can be slow. When disabled (or not | ||||
| 	# running), LVM commands fall back to scanning disks to obtain VG | ||||
| 	# metadata. lvmetad is kept updated via udev rules which must be set | ||||
| 	# up for LVM to work correctly. (The udev rules should be installed | ||||
| 	# by default.) Without a proper udev setup, changes in the system's | ||||
| 	# block device configuration will be unknown to LVM, and ignored | ||||
| 	# until a manual 'pvscan --cache' is run. If lvmetad was running | ||||
| 	# while use_lvmetad was disabled, it must be stopped, use_lvmetad | ||||
| 	# enabled, and then started. When using lvmetad, LV activation is | ||||
| 	# switched to an automatic, event-based mode. In this mode, LVs are | ||||
| 	# activated based on incoming udev events that inform lvmetad when | ||||
| 	# PVs appear on the system. When a VG is complete (all PVs present), | ||||
| 	# it is auto-activated. The auto_activation_volume_list setting | ||||
| 	# controls which LVs are auto-activated (all by default.) | ||||
| 	# When lvmetad is updated (automatically by udev events, or directly | ||||
| 	# by pvscan --cache), devices/filter is ignored and all devices are | ||||
| 	# scanned by default. lvmetad always keeps unfiltered information | ||||
| 	# which is provided to LVM commands. Each LVM command then filters | ||||
| 	# based on devices/filter. This does not apply to other, non-regexp, | ||||
| 	# filtering settings: component filters such as multipath and MD | ||||
| 	# are checked during pvscan --cache. To filter a device and prevent | ||||
| 	# scanning from the LVM system entirely, including lvmetad, use | ||||
| 	# devices/global_filter. | ||||
| 	use_lvmetad = @DEFAULT_USE_LVMETAD@ | ||||
|  | ||||
| 	# Configuration option global/use_aio. | ||||
| 	# Use async I/O when reading and writing devices. | ||||
| 	# Configuration option global/lvmetad_update_wait_time. | ||||
| 	# The number of seconds a command will wait for lvmetad update to finish. | ||||
| 	# After waiting for this period, a command will not use lvmetad, and | ||||
| 	# will revert to disk scanning. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# use_aio = 1 | ||||
| 	# lvmetad_update_wait_time = 10 | ||||
|  | ||||
| 	# Configuration option global/use_lvmlockd. | ||||
| 	# Use lvmlockd for locking among hosts using LVM on shared storage. | ||||
| @@ -1130,7 +1013,7 @@ global { | ||||
| 	# Configuration option global/cache_disabled_features. | ||||
| 	# Features to not use in the cache driver. | ||||
| 	# This can be helpful for testing, or to avoid using a feature that is | ||||
| 	# causing problems. Features include: policy_mq, policy_smq, metadata2. | ||||
| 	# causing problems. Features include: policy_mq, policy_smq. | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# cache_disabled_features = [ "policy_smq" ] | ||||
| @@ -1175,23 +1058,6 @@ global { | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# cache_repair_options = [ "" ] | ||||
|  | ||||
| 	# Configuration option global/vdo_format_executable. | ||||
| 	# The full path to the vdoformat command. | ||||
| 	# LVM uses this command to initial data volume for VDO type logical volume | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_format_executable = "@VDO_FORMAT_CMD@" | ||||
|  | ||||
| 	# Configuration option global/vdo_format_options. | ||||
| 	# List of options passed added to standard vdoformat command. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_format_options = [ "" ] | ||||
|  | ||||
| 	# Configuration option global/fsadm_executable. | ||||
| 	# The full path to the fsadm command. | ||||
| 	# LVM uses this command to help with lvresize -r operations. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# fsadm_executable = "@FSADM_PATH@" | ||||
|  | ||||
| 	# Configuration option global/system_id_source. | ||||
| 	# The method LVM uses to set the local system ID. | ||||
| 	# Volume Groups can also be given a system ID (by vgcreate, vgchange, | ||||
| @@ -1243,16 +1109,6 @@ global { | ||||
| 	# When enabled, an LVM command that changes PVs, changes VG metadata, | ||||
| 	# or changes the activation state of an LV will send a notification. | ||||
| 	notify_dbus = 1 | ||||
|  | ||||
| 	# Configuration option global/io_memory_size. | ||||
| 	# The amount of memory in KiB that LVM allocates to perform disk io. | ||||
| 	# LVM performance may benefit from more io memory when there are many | ||||
| 	# disks or VG metadata is large. Increasing this size may be necessary | ||||
| 	# when a single copy of VG metadata is larger than the current setting. | ||||
| 	# This value should usually not be decreased from the default; setting | ||||
| 	# it too low can result in lvm failing to read VGs. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# io_memory_size = 8192 | ||||
| } | ||||
|  | ||||
| # Configuration section activation. | ||||
| @@ -1421,10 +1277,9 @@ activation { | ||||
|  | ||||
| 	# Configuration option activation/raid_region_size. | ||||
| 	# Size in KiB of each raid or mirror synchronization region. | ||||
| 	# The clean/dirty state of data is tracked for each region. | ||||
| 	# The value is rounded down to a power of two if necessary, and | ||||
| 	# is ignored if it is not a multiple of the machine memory page size. | ||||
| 	raid_region_size = 2048 | ||||
| 	# For raid or mirror segment types, this is the amount of data that is | ||||
| 	# copied at once when initializing, or moved at once by pvmove. | ||||
| 	raid_region_size = 512 | ||||
|  | ||||
| 	# Configuration option activation/error_when_full. | ||||
| 	# Return errors if a thin pool runs out of space. | ||||
| @@ -1569,33 +1424,6 @@ activation { | ||||
| 	#  | ||||
| 	thin_pool_autoextend_percent = 20 | ||||
|  | ||||
| 	# Configuration option activation/vdo_pool_autoextend_threshold. | ||||
| 	# Auto-extend a VDO pool when its usage exceeds this percent. | ||||
| 	# Setting this to 100 disables automatic extension. | ||||
| 	# The minimum value is 50 (a smaller value is treated as 50.) | ||||
| 	# Also see vdo_pool_autoextend_percent. | ||||
| 	# Automatic extension requires dmeventd to be monitoring the LV. | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# Using 70% autoextend threshold and 20% autoextend size, when a 10G | ||||
| 	# VDO pool exceeds 7G, it is extended to 12G, and when it exceeds | ||||
| 	# 8.4G, it is extended to 14.4G: | ||||
| 	# vdo_pool_autoextend_threshold = 70 | ||||
| 	#  | ||||
| 	vdo_pool_autoextend_threshold = 100 | ||||
|  | ||||
| 	# Configuration option activation/vdo_pool_autoextend_percent. | ||||
| 	# Auto-extending a VDO pool adds this percent extra space. | ||||
| 	# The amount of additional space added to a VDO pool is this | ||||
| 	# percent of its current size. | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# Using 70% autoextend threshold and 20% autoextend size, when a 10G | ||||
| 	# VDO pool exceeds 7G, it is extended to 12G, and when it exceeds | ||||
| 	# 8.4G, it is extended to 14.4G: | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_pool_autoextend_percent = 20 | ||||
|  | ||||
| 	# Configuration option activation/mlock_filter. | ||||
| 	# Do not mlock these memory areas. | ||||
| 	# While activating devices, I/O to devices being (re)configured is | ||||
| @@ -1741,19 +1569,13 @@ activation { | ||||
| 	# vgmetadatacopies = 0 | ||||
|  | ||||
| 	# Configuration option metadata/pvmetadatasize. | ||||
| 	# The default size of the metadata area in units of 512 byte sectors. | ||||
| 	# The metadata area begins at an offset of the page size from the start | ||||
| 	# of the device. The first PE is by default at 1 MiB from the start of | ||||
| 	# the device. The space between these is the default metadata area size. | ||||
| 	# The actual size of the metadata area may be larger than what is set | ||||
| 	# here due to default_data_alignment making the first PE a MiB multiple. | ||||
| 	# The metadata area begins with a 512 byte header and is followed by a | ||||
| 	# circular buffer used for VG metadata text. The maximum size of the VG | ||||
| 	# metadata is about half the size of the metadata buffer. VGs with large | ||||
| 	# numbers of PVs or LVs, or VGs containing complex LV structures, may need | ||||
| 	# additional space for VG metadata. The --metadatasize option overrides | ||||
| 	# this setting. | ||||
| 	# This configuration option does not have a default value defined. | ||||
| 	# Approximate number of sectors to use for each metadata copy. | ||||
| 	# VGs with large numbers of PVs or LVs, or VGs containing complex LV | ||||
| 	# structures, may need additional space for VG 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. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# pvmetadatasize = 255 | ||||
|  | ||||
| 	# Configuration option metadata/pvmetadataignore. | ||||
| 	# Ignore metadata areas on a new PV. | ||||
| @@ -1768,6 +1590,24 @@ activation { | ||||
| 	# This configuration option is advanced. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# stripesize = 64 | ||||
|  | ||||
| 	# Configuration option metadata/dirs. | ||||
| 	# Directories holding live copies of text format metadata. | ||||
| 	# These directories must not be on logical volumes! | ||||
| 	# It's possible to use LVM 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 are absolutely sure you know what you are doing! | ||||
| 	# Use the supplied toolset to make changes (e.g. vgcfgrestore). | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ] | ||||
| 	#  | ||||
| 	# This configuration option is advanced. | ||||
| 	# This configuration option does not have a default value defined. | ||||
| # } | ||||
|  | ||||
| # Configuration section report. | ||||
| @@ -2214,27 +2054,9 @@ dmeventd { | ||||
| 	# or metadata volume gets above 50%. | ||||
| 	# Command which starts with 'lvm ' prefix is internal lvm command. | ||||
| 	# You can write your own handler to customise behaviour in more details. | ||||
| 	# User handler is specified with the full path starting with '/'. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# thin_command = "lvm lvextend --use-policies" | ||||
|  | ||||
| 	# Configuration option dmeventd/vdo_library. | ||||
| 	# The library dmeventd uses when monitoring a VDO pool device. | ||||
| 	# libdevmapper-event-lvm2vdo.so monitors the filling of a pool | ||||
| 	# and emits a warning through syslog when the usage exceeds 80%. The | ||||
| 	# warning is repeated when 85%, 90% and 95% of the pool is filled. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_library = "libdevmapper-event-lvm2vdo.so" | ||||
|  | ||||
| 	# Configuration option dmeventd/vdo_command. | ||||
| 	# The plugin runs command with each 5% increment when VDO pool volume | ||||
| 	# gets above 50%. | ||||
| 	# Command which starts with 'lvm ' prefix is internal lvm command. | ||||
| 	# You can write your own handler to customise behaviour in more details. | ||||
| 	# User handler is specified with the full path starting with '/'. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# vdo_command = "lvm lvextend --use-policies" | ||||
|  | ||||
| 	# Configuration option dmeventd/executable. | ||||
| 	# The full path to the dmeventd binary. | ||||
| 	# This configuration option has an automatic default value. | ||||
|   | ||||
							
								
								
									
										19
									
								
								conf/lvmlocal.conf.base
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								conf/lvmlocal.conf.base
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| # This is a local configuration file template for the LVM2 system | ||||
| # which should be installed as @DEFAULT_SYS_DIR@/lvmlocal.conf . | ||||
| # | ||||
| # Refer to 'man lvm.conf' for information about 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. | ||||
| # | ||||
| # The lvmlocal.conf file is normally expected to contain only the | ||||
| # "local" section which contains settings that should not be shared or | ||||
| # repeated among different hosts.  (But if other sections are present, | ||||
| # they *will* get processed.  Settings in this file override equivalent | ||||
| # ones in lvm.conf and are in turn overridden by ones in any enabled | ||||
| # lvm_<tag>.conf files.) | ||||
| # | ||||
| # Please take care that each setting only appears once if uncommenting | ||||
| # example settings in this file and never copy this file between hosts. | ||||
|  | ||||
| @@ -1,24 +0,0 @@ | ||||
| # Demo configuration for 'VDO' using less memory. | ||||
| # ~lvmconfig --type full | grep vdo | ||||
|  | ||||
| allocation { | ||||
| 	vdo_use_compression=1 | ||||
| 	vdo_use_deduplication=1 | ||||
| 	vdo_use_metadata_hints=1 | ||||
| 	vdo_minimum_io_size=4096 | ||||
| 	vdo_block_map_cache_size_mb=128 | ||||
| 	vdo_block_map_period=16380 | ||||
| 	vdo_check_point_frequency=0 | ||||
| 	vdo_use_sparse_index=0 | ||||
| 	vdo_index_memory_size_mb=256 | ||||
| 	vdo_slab_size_mb=2048 | ||||
| 	vdo_ack_threads=1 | ||||
| 	vdo_bio_threads=1 | ||||
| 	vdo_bio_rotation=64 | ||||
| 	vdo_cpu_threads=2 | ||||
| 	vdo_hash_zone_threads=1 | ||||
| 	vdo_logical_threads=1 | ||||
| 	vdo_physical_threads=1 | ||||
| 	vdo_write_policy="auto" | ||||
| 	vdo_max_discard=1 | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -41,19 +41,6 @@ struct lv_segment *last_seg(const struct logical_volume *lv) | ||||
| 	return ((struct lv_segment **)lv)[0]; | ||||
| } | ||||
|  | ||||
| const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
| 	return "STRING"; | ||||
| } | ||||
|  | ||||
| struct logical_volume *origin_from_cow(const struct logical_volume *lv) | ||||
| { | ||||
| 	if (lv) | ||||
| 		return lv; | ||||
|  | ||||
| 	__coverity_panic__(); | ||||
| } | ||||
|  | ||||
| /* simple_memccpy() from glibc */ | ||||
| void *memccpy(void *dest, const void *src, int c, size_t n) | ||||
| { | ||||
| @@ -84,17 +71,6 @@ void model_FD_ZERO(void *fdset) | ||||
| 		((long*)fdset)[i] = 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Resent Coverity reports quite weird errors... */ | ||||
| int *__errno_location(void) | ||||
| { | ||||
| } | ||||
| const unsigned short **__ctype_b_loc (void) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Added extra pointer check to not need these models, | ||||
|  * for now just keep then in file | ||||
|   | ||||
| @@ -15,7 +15,11 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| .PHONY: dmeventd cmirrord lvmpolld lvmlockd | ||||
| .PHONY: dmeventd clvmd cmirrord lvmetad lvmpolld lvmlockd | ||||
|  | ||||
| ifneq ("@CLVMD@", "none") | ||||
|   SUBDIRS += clvmd | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_CMIRRORD@", "yes") | ||||
|   SUBDIRS += cmirrord | ||||
| @@ -28,6 +32,10 @@ daemons.cflow: dmeventd.cflow | ||||
| endif | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LVMETAD@", "yes") | ||||
|   SUBDIRS += lvmetad | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LVMPOLLD@", "yes") | ||||
|   SUBDIRS += lvmpolld | ||||
| endif | ||||
| @@ -41,7 +49,7 @@ ifeq ("@BUILD_LVMDBUSD@", "yes") | ||||
| endif | ||||
|  | ||||
| ifeq ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = cmirrord dmeventd lvmpolld lvmlockd lvmdbusd | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd | ||||
| endif | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|   | ||||
							
								
								
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| clvmd | ||||
							
								
								
									
										103
									
								
								daemons/clvmd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								daemons/clvmd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| # | ||||
| # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| CMAN_LIBS = @CMAN_LIBS@ | ||||
| CMAN_CFLAGS = @CMAN_CFLAGS@ | ||||
| CMAP_LIBS = @CMAP_LIBS@ | ||||
| CMAP_CFLAGS = @CMAP_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@ | ||||
| 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 | ||||
|  | ||||
| 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 += $(CMAP_LIBS) $(CONFDB_LIBS) $(CPG_LIBS) $(DLM_LIBS) $(QUORUM_LIBS) | ||||
| 	CFLAGS += $(CMAP_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-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) | ||||
|  | ||||
| CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS) | ||||
| LDFLAGS += $(EXTRA_EXEC_LDFLAGS) | ||||
|  | ||||
| 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) | ||||
							
								
								
									
										85
									
								
								daemons/clvmd/clvm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								daemons/clvmd/clvm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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" | ||||
| #include <inttypes.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 or FLAG_REMOTE 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 */ | ||||
| #define CLVMD_FLAG_REMOTE	8	/* Do this on all nodes except for the local node */ | ||||
|  | ||||
| /* Name of the local socket to communicate between lvm and clvmd */ | ||||
| #define 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 | ||||
| #define CLVMD_CMD_SYNC_NAMES	    45 | ||||
|  | ||||
| /* Used internally by some callers, but not part of the protocol.*/ | ||||
| #ifndef NODE_ALL | ||||
| #  define NODE_ALL	"*" | ||||
| #  define NODE_LOCAL	"." | ||||
| #  define NODE_REMOTE	"^" | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										505
									
								
								daemons/clvmd/clvmd-cman.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								daemons/clvmd/clvmd-cman.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,505 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * CMAN communication layer for clvmd. | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include "clvmd-comms.h" | ||||
| #include "clvm.h" | ||||
| #include "clvmd.h" | ||||
| #include "lvm-functions.h" | ||||
|  | ||||
| #include <libdlm.h> | ||||
|  | ||||
| #include <syslog.h> | ||||
|  | ||||
| #define LOCKSPACE_NAME "clvmd" | ||||
|  | ||||
| struct clvmd_node | ||||
| { | ||||
| 	struct cman_node *node; | ||||
| 	int clvmd_up; | ||||
| }; | ||||
|  | ||||
| static int num_nodes; | ||||
| static struct cman_node *nodes = NULL; | ||||
| static struct cman_node this_node; | ||||
| static int count_nodes; /* size of allocated nodes array */ | ||||
| static struct dm_hash_table *node_updown_hash; | ||||
| static dlm_lshandle_t *lockspace; | ||||
| static cman_handle_t c_handle; | ||||
|  | ||||
| static void count_clvmds_running(void); | ||||
| static void get_members(void); | ||||
| static int nodeid_from_csid(const char *csid); | ||||
| static int name_from_nodeid(int nodeid, char *name); | ||||
| static void event_callback(cman_handle_t handle, void *private, int reason, int arg); | ||||
| static void data_callback(cman_handle_t handle, void *private, | ||||
| 			  char *buf, int len, uint8_t port, int nodeid); | ||||
|  | ||||
| struct lock_wait { | ||||
| 	pthread_cond_t cond; | ||||
| 	pthread_mutex_t mutex; | ||||
| 	struct dlm_lksb lksb; | ||||
| }; | ||||
|  | ||||
| static int _init_cluster(void) | ||||
| { | ||||
| 	node_updown_hash = dm_hash_create(100); | ||||
|  | ||||
| 	/* Open the cluster communication socket */ | ||||
| 	c_handle = cman_init(NULL); | ||||
| 	if (!c_handle) { | ||||
| 		syslog(LOG_ERR, "Can't open cluster manager socket: %m"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	DEBUGLOG("Connected to CMAN\n"); | ||||
|  | ||||
| 	if (cman_start_recv_data(c_handle, data_callback, CLUSTER_PORT_CLVMD)) { | ||||
| 		syslog(LOG_ERR, "Can't bind cluster socket: %m"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (cman_start_notification(c_handle, event_callback)) { | ||||
| 		syslog(LOG_ERR, "Can't start cluster event listening"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Get the cluster members list */ | ||||
| 	get_members(); | ||||
| 	count_clvmds_running(); | ||||
|  | ||||
| 	DEBUGLOG("CMAN initialisation complete\n"); | ||||
|  | ||||
| 	/* Create a lockspace for LV & VG locks to live in */ | ||||
| 	lockspace = dlm_open_lockspace(LOCKSPACE_NAME); | ||||
| 	if (!lockspace) { | ||||
| 		lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600); | ||||
| 		if (!lockspace) { | ||||
| 			syslog(LOG_ERR, "Unable to create DLM lockspace for CLVM: %m"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		DEBUGLOG("Created DLM lockspace for CLVMD.\n"); | ||||
| 	} else | ||||
| 		DEBUGLOG("Opened existing DLM lockspace for CLVMD.\n"); | ||||
|  | ||||
| 	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(void) | ||||
| { | ||||
| 	return cman_get_fd(c_handle); | ||||
| } | ||||
|  | ||||
| static int _get_num_nodes(void) | ||||
| { | ||||
| 	int i; | ||||
| 	int nnodes = 0; | ||||
|  | ||||
| 	/* return number of ACTIVE nodes */ | ||||
| 	for (i=0; i<num_nodes; i++) { | ||||
| 		if (nodes[i].cn_member && nodes[i].cn_nodeid) | ||||
| 			nnodes++; | ||||
| 	} | ||||
| 	return nnodes; | ||||
| } | ||||
|  | ||||
| /* send_message with the fd check removed */ | ||||
| static int _cluster_send_message(const void *buf, int msglen, const char *csid, | ||||
| 				 const char *errtext) | ||||
| { | ||||
| 	int nodeid = 0; | ||||
|  | ||||
| 	if (csid) | ||||
| 		memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN); | ||||
|  | ||||
| 	if (cman_send_data(c_handle, buf, msglen, 0, CLUSTER_PORT_CLVMD, nodeid) <= 0) | ||||
| 	{ | ||||
| 		log_error("%s", errtext); | ||||
| 	} | ||||
| 	return msglen; | ||||
| } | ||||
|  | ||||
| static void _get_our_csid(char *csid) | ||||
| { | ||||
| 	if (this_node.cn_nodeid == 0) { | ||||
| 		cman_get_node(c_handle, 0, &this_node); | ||||
| 	} | ||||
| 	memcpy(csid, &this_node.cn_nodeid, CMAN_MAX_CSID_LEN); | ||||
| } | ||||
|  | ||||
| /* Call a callback routine for each node is that known (down means not running a clvmd) */ | ||||
| static int _cluster_do_node_callback(struct local_client *client, | ||||
| 				     void (*callback) (struct local_client *, | ||||
| 						       const char *, | ||||
| 						       int)) | ||||
| { | ||||
| 	int i; | ||||
| 	int somedown = 0; | ||||
|  | ||||
| 	for (i = 0; i < _get_num_nodes(); i++) { | ||||
| 		if (nodes[i].cn_member && nodes[i].cn_nodeid) { | ||||
| 			int up = (int)(long)dm_hash_lookup_binary(node_updown_hash, (char *)&nodes[i].cn_nodeid, sizeof(int)); | ||||
|  | ||||
| 			callback(client, (char *)&nodes[i].cn_nodeid, up); | ||||
| 			if (!up) | ||||
| 				somedown = -1; | ||||
| 		} | ||||
| 	} | ||||
| 	return somedown; | ||||
| } | ||||
|  | ||||
| /* Process OOB messages from the cluster socket */ | ||||
| static void event_callback(cman_handle_t handle, void *private, int reason, int arg) | ||||
| { | ||||
| 	char namebuf[MAX_CLUSTER_MEMBER_NAME_LEN]; | ||||
|  | ||||
| 	switch (reason) { | ||||
|         case CMAN_REASON_PORTCLOSED: | ||||
| 		name_from_nodeid(arg, namebuf); | ||||
| 		log_notice("clvmd on node %s has died\n", namebuf); | ||||
| 		DEBUGLOG("Got port closed message, removing node %s\n", namebuf); | ||||
|  | ||||
| 		dm_hash_insert_binary(node_updown_hash, (char *)&arg, sizeof(int), (void *)0); | ||||
| 		break; | ||||
|  | ||||
| 	case CMAN_REASON_STATECHANGE: | ||||
| 		DEBUGLOG("Got state change message, re-reading members list\n"); | ||||
| 		get_members(); | ||||
| 		break; | ||||
|  | ||||
| #if defined(LIBCMAN_VERSION) && LIBCMAN_VERSION >= 2 | ||||
| 	case CMAN_REASON_PORTOPENED: | ||||
| 		/* Ignore this, wait for startup message from clvmd itself */ | ||||
| 		break; | ||||
|  | ||||
| 	case CMAN_REASON_TRY_SHUTDOWN: | ||||
| 		DEBUGLOG("Got try shutdown, sending OK\n"); | ||||
| 		cman_replyto_shutdown(c_handle, 1); | ||||
| 		break; | ||||
| #endif | ||||
| 	default: | ||||
| 		/* ERROR */ | ||||
| 		DEBUGLOG("Got unknown event callback message: %d\n", reason); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static struct local_client *cman_client; | ||||
| static int _cluster_fd_callback(struct local_client *fd, char *buf, int len, | ||||
| 				const char *csid, | ||||
| 				struct local_client **new_client) | ||||
| { | ||||
|  | ||||
| 	/* Save this for data_callback */ | ||||
| 	cman_client = fd; | ||||
|  | ||||
| 	/* We never return a new client */ | ||||
| 	*new_client = NULL; | ||||
|  | ||||
| 	return cman_dispatch(c_handle, 0); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void data_callback(cman_handle_t handle, void *private, | ||||
| 			  char *buf, int len, uint8_t port, int nodeid) | ||||
| { | ||||
| 	/* Ignore looped back messages */ | ||||
| 	if (nodeid == this_node.cn_nodeid) | ||||
| 		return; | ||||
| 	process_message(cman_client, buf, len, (char *)&nodeid); | ||||
| } | ||||
|  | ||||
| static void _add_up_node(const char *csid) | ||||
| { | ||||
| 	/* It's up ! */ | ||||
| 	int nodeid = nodeid_from_csid(csid); | ||||
|  | ||||
| 	dm_hash_insert_binary(node_updown_hash, (char *)&nodeid, sizeof(int), (void *)1); | ||||
| 	DEBUGLOG("Added new node %d to updown list\n", nodeid); | ||||
| } | ||||
|  | ||||
| static void _cluster_closedown(void) | ||||
| { | ||||
| 	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(void) | ||||
| { | ||||
| 	int retnodes; | ||||
| 	int status; | ||||
| 	int i; | ||||
| 	int high_nodeid = 0; | ||||
|  | ||||
| 	num_nodes = cman_get_node_count(c_handle); | ||||
| 	if (num_nodes == -1) { | ||||
| 		log_error("Unable to get node count"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	/* Not enough room for new nodes list ? */ | ||||
| 	if (num_nodes > count_nodes && nodes) { | ||||
| 		free(nodes); | ||||
| 		nodes = NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (nodes == NULL) { | ||||
| 		count_nodes = num_nodes + 10; /* Overallocate a little */ | ||||
| 		nodes = malloc(count_nodes * sizeof(struct cman_node)); | ||||
| 		if (!nodes) { | ||||
| 			log_error("Unable to allocate nodes array\n"); | ||||
| 			exit(5); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	status = cman_get_nodes(c_handle, count_nodes, &retnodes, nodes); | ||||
| 	if (status < 0) { | ||||
| 		log_error("Unable to get node details"); | ||||
| 		exit(6); | ||||
| 	} | ||||
|  | ||||
| 	/* Get the highest nodeid */ | ||||
| 	for (i=0; i<retnodes; i++) { | ||||
| 		if (nodes[i].cn_nodeid > high_nodeid) | ||||
| 			high_nodeid = nodes[i].cn_nodeid; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Convert a node name to a CSID */ | ||||
| static int _csid_from_name(char *csid, const char *name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < num_nodes; i++) { | ||||
| 		if (strcmp(name, nodes[i].cn_name) == 0) { | ||||
| 			memcpy(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Convert a CSID to a node name */ | ||||
| static int _name_from_csid(const char *csid, char *name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < num_nodes; i++) { | ||||
| 		if (memcmp(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN) == 0) { | ||||
| 			strcpy(name, nodes[i].cn_name); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Who?? */ | ||||
| 	strcpy(name, "Unknown"); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Convert a node ID to a node name */ | ||||
| static int name_from_nodeid(int nodeid, char *name) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < num_nodes; i++) { | ||||
| 		if (nodeid == nodes[i].cn_nodeid) { | ||||
| 			strcpy(name, nodes[i].cn_name); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Who?? */ | ||||
| 	strcpy(name, "Unknown"); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /* Convert a CSID to a node ID */ | ||||
| static int nodeid_from_csid(const char *csid) | ||||
| { | ||||
|         int nodeid; | ||||
|  | ||||
| 	memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN); | ||||
|  | ||||
| 	return nodeid; | ||||
| } | ||||
|  | ||||
| static int _is_quorate(void) | ||||
| { | ||||
| 	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 = { | ||||
| 	.name                     = "cman", | ||||
| 	.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; | ||||
| } | ||||
							
								
								
									
										414
									
								
								daemons/clvmd/clvmd-command.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								daemons/clvmd/clvmd-command.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,414 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "clvmd-comms.h" | ||||
| #include "clvm.h" | ||||
| #include "clvmd.h" | ||||
| #include "lvm-globals.h" | ||||
| #include "lvm-functions.h" | ||||
|  | ||||
| #include "locking.h" | ||||
|  | ||||
| #include <sys/utsname.h> | ||||
|  | ||||
| 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) { | ||||
| 			if (uname(&nodeinfo)) | ||||
| 				memset(&nodeinfo, 0, sizeof(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]; | ||||
| 		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_SYNC_NAMES: | ||||
| 		lvm_do_fs_unlock(); | ||||
| 		break; | ||||
|  | ||||
| 	case CLVMD_CMD_SET_DEBUG: | ||||
| 		clvmd_set_debug((debug_t) args[0]); | ||||
| 		break; | ||||
|  | ||||
| 	case CLVMD_CMD_RESTART: | ||||
| 		status = 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; | ||||
| 	int lock_mode; | ||||
| 	char *args = header->node + strlen(header->node) + 1; | ||||
| 	int lkid; | ||||
| 	int status; | ||||
| 	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) { | ||||
| 		if (!(lock_hash = dm_hash_create(3))) | ||||
| 			return ENOMEM; | ||||
| 		client->bits.localsock.private = (void *) lock_hash; | ||||
| 	} else | ||||
| 		lock_hash = (struct dm_hash_table *) client->bits.localsock.private; | ||||
|  | ||||
| 	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) { | ||||
| 		if (!(lkid = (int) (long) dm_hash_lookup(lock_hash, lockname))) | ||||
| 			return EINVAL; | ||||
|  | ||||
| 		if ((status = sync_unlock(lockname, lkid))) | ||||
| 			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; | ||||
|  | ||||
| 		if ((status = sync_lock(lockname, lock_mode, (lock_cmd & LCK_NONBLOCK) ? LCKF_NOQUEUE : 0, &lkid))) | ||||
| 			status = errno; | ||||
| 		else if (!dm_hash_insert(lock_hash, lockname, (void *) (long) lkid)) | ||||
| 			return ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	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 = 0; | ||||
| 	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_SYNC_NAMES: | ||||
| 	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 = NULL; | ||||
| 		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; | ||||
|  | ||||
| 	default: | ||||
| 		/* Nothing to do here */ | ||||
| 		break; | ||||
| 	} | ||||
| 	return status; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Called when the client is about to be deleted */ | ||||
| void cmd_client_cleanup(struct local_client *client) | ||||
| { | ||||
| 	struct dm_hash_node *v; | ||||
| 	struct dm_hash_table *lock_hash; | ||||
| 	int lkid; | ||||
| 	char *lockname; | ||||
|  | ||||
| 	DEBUGLOG("Client thread cleanup (%p)\n", client); | ||||
| 	if (!client->bits.localsock.private) | ||||
| 		return; | ||||
|  | ||||
| 	lock_hash = (struct dm_hash_table *)client->bits.localsock.private; | ||||
|  | ||||
| 	dm_hash_iterate(v, lock_hash) { | ||||
| 		lkid = (int)(long)dm_hash_get_data(lock_hash, v); | ||||
| 		lockname = dm_hash_get_key(lock_hash, v); | ||||
| 		DEBUGLOG("Cleanup (%p): Unlocking lock %s %x\n", client, lockname, lkid); | ||||
| 		(void) sync_unlock(lockname, lkid); | ||||
| 	} | ||||
|  | ||||
| 	dm_hash_destroy(lock_hash); | ||||
| 	client->bits.localsock.private = NULL; | ||||
| } | ||||
|  | ||||
| static int restart_clvmd(void) | ||||
| { | ||||
| 	const char **argv; | ||||
| 	char *lv_name; | ||||
| 	int argc = 0, max_locks = 0; | ||||
| 	struct dm_hash_node *hn = NULL; | ||||
| 	char debug_arg[16]; | ||||
| 	const char *clvmd = getenv("LVM_CLVMD_BINARY") ? : CLVMD_PATH; | ||||
|  | ||||
| 	DEBUGLOG("clvmd restart requested\n"); | ||||
|  | ||||
| 	/* Count exclusively-open LVs */ | ||||
| 	do { | ||||
| 		hn = get_next_excl_lock(hn, &lv_name); | ||||
| 		if (lv_name) { | ||||
| 			max_locks++; | ||||
| 			if (!*lv_name) | ||||
| 				break; /* FIXME: Is this error ? */ | ||||
| 		} | ||||
| 	} while (hn); | ||||
|  | ||||
| 	/* clvmd + locks (-E uuid) + debug (-d X) + NULL */ | ||||
| 	if (!(argv = malloc((max_locks * 2 + 6) * sizeof(*argv)))) | ||||
| 		goto_out; | ||||
|  | ||||
| 	/* | ||||
| 	 * Build the command-line | ||||
| 	 */ | ||||
| 	argv[argc++] = "clvmd"; | ||||
|  | ||||
| 	/* Propagate debug options */ | ||||
| 	if (clvmd_get_debug()) { | ||||
| 		if (dm_snprintf(debug_arg, sizeof(debug_arg), "-d%u", clvmd_get_debug()) < 0) | ||||
| 			goto_out; | ||||
| 		argv[argc++] = debug_arg; | ||||
| 	} | ||||
|  | ||||
| 	/* Propagate foreground options */ | ||||
| 	if (clvmd_get_foreground()) | ||||
| 		argv[argc++] = "-f"; | ||||
|  | ||||
| 	argv[argc++] = "-I"; | ||||
| 	argv[argc++] = clops->name; | ||||
|  | ||||
| 	/* Now add the exclusively-open LVs */ | ||||
| 	hn = NULL; | ||||
| 	do { | ||||
| 		hn = get_next_excl_lock(hn, &lv_name); | ||||
| 		if (lv_name) { | ||||
| 			if (!*lv_name) | ||||
| 				break; /* FIXME: Is this error ? */ | ||||
| 			argv[argc++] = "-E"; | ||||
| 			argv[argc++] = lv_name; | ||||
| 			DEBUGLOG("excl lock: %s\n", lv_name); | ||||
| 		} | ||||
| 	} while (hn); | ||||
| 	argv[argc] = NULL; | ||||
|  | ||||
| 	/* Exec new clvmd */ | ||||
| 	DEBUGLOG("--- Restarting %s ---\n", clvmd); | ||||
| 	for (argc = 1; argv[argc]; argc++) DEBUGLOG("--- %d: %s\n", argc, argv[argc]); | ||||
|  | ||||
| 	/* NOTE: This will fail when downgrading! */ | ||||
| 	execvp(clvmd, (char **)argv); | ||||
| out: | ||||
| 	/* We failed */ | ||||
| 	DEBUGLOG("Restart of clvmd failed.\n"); | ||||
|  | ||||
| 	free(argv); | ||||
|  | ||||
| 	return EIO; | ||||
| } | ||||
| @@ -1,6 +1,5 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.   | ||||
|  * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2010 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -12,3 +11,17 @@ | ||||
|  * along with this program; if not, write to the Free Software Foundation, | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This file must be included first by every clvmd source file. | ||||
|  */ | ||||
| #ifndef _LVM_CLVMD_COMMON_H | ||||
| #define _LVM_CLVMD_COMMON_H | ||||
| 
 | ||||
| #define _REENTRANT | ||||
| 
 | ||||
| #include "tool.h" | ||||
| 
 | ||||
| #include "lvm-logging.h" | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										119
									
								
								daemons/clvmd/clvmd-comms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								daemons/clvmd/clvmd-comms.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Abstraction layer for clvmd cluster communications | ||||
|  */ | ||||
|  | ||||
| #ifndef _CLVMD_COMMS_H | ||||
| #define _CLVMD_COMMS_H | ||||
|  | ||||
| struct local_client; | ||||
|  | ||||
| struct cluster_ops { | ||||
| 	const char *name; | ||||
| 	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_CMAN | ||||
| #  include <netinet/in.h> | ||||
| #  include "libcman.h" | ||||
| #  define CMAN_MAX_CSID_LEN 4 | ||||
| #  ifndef MAX_CSID_LEN | ||||
| #    define MAX_CSID_LEN CMAN_MAX_CSID_LEN | ||||
| #  endif | ||||
| #  undef MAX_CLUSTER_MEMBER_NAME_LEN | ||||
| #  define MAX_CLUSTER_MEMBER_NAME_LEN   CMAN_MAX_NODENAME_LEN | ||||
| #  define CMAN_MAX_CLUSTER_MESSAGE 1500 | ||||
| #  define CLUSTER_PORT_CLVMD 11 | ||||
| struct cluster_ops *init_cman_cluster(void); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_OPENAIS | ||||
| #  include <openais/saAis.h> | ||||
| #  include <corosync/totem/totem.h> | ||||
| #  define OPENAIS_CSID_LEN (sizeof(int)) | ||||
| #  define OPENAIS_MAX_CLUSTER_MESSAGE         MESSAGE_SIZE_MAX | ||||
| #  define OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH | ||||
| #  ifndef MAX_CLUSTER_MEMBER_NAME_LEN | ||||
| #    define MAX_CLUSTER_MEMBER_NAME_LEN       SA_MAX_NAME_LENGTH | ||||
| #  endif | ||||
| #  ifndef CMAN_MAX_CLUSTER_MESSAGE | ||||
| #    define CMAN_MAX_CLUSTER_MESSAGE          MESSAGE_SIZE_MAX | ||||
| #  endif | ||||
| #  ifndef MAX_CSID_LEN | ||||
| #    define MAX_CSID_LEN sizeof(int) | ||||
| #  endif | ||||
| struct cluster_ops *init_openais_cluster(void); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_COROSYNC | ||||
| #  include <corosync/corotypes.h> | ||||
| #  define COROSYNC_CSID_LEN (sizeof(int)) | ||||
| #  define COROSYNC_MAX_CLUSTER_MESSAGE         65535 | ||||
| #  define COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH | ||||
| #  ifndef MAX_CLUSTER_MEMBER_NAME_LEN | ||||
| #    define MAX_CLUSTER_MEMBER_NAME_LEN       CS_MAX_NAME_LENGTH | ||||
| #  endif | ||||
| #  ifndef CMAN_MAX_CLUSTER_MESSAGE | ||||
| #    define CMAN_MAX_CLUSTER_MESSAGE          65535 | ||||
| #  endif | ||||
| #  ifndef MAX_CSID_LEN | ||||
| #    define MAX_CSID_LEN sizeof(int) | ||||
| #  endif | ||||
| struct cluster_ops *init_corosync_cluster(void); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_SINGLENODE | ||||
| #  define SINGLENODE_CSID_LEN (sizeof(int)) | ||||
| #  ifndef MAX_CLUSTER_MEMBER_NAME_LEN | ||||
| #    define MAX_CLUSTER_MEMBER_NAME_LEN       64 | ||||
| #  endif | ||||
| #  define SINGLENODE_MAX_CLUSTER_MESSAGE          65535 | ||||
| #  ifndef MAX_CSID_LEN | ||||
| #    define MAX_CSID_LEN sizeof(int) | ||||
| #  endif | ||||
| struct cluster_ops *init_singlenode_cluster(void); | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										662
									
								
								daemons/clvmd/clvmd-corosync.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										662
									
								
								daemons/clvmd/clvmd-corosync.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,662 @@ | ||||
| /* | ||||
|  * Copyright (C) 2009-2012 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser 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 | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * This provides the interface between clvmd and corosync/DLM as the cluster | ||||
|  * and lock manager. | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include "clvm.h" | ||||
| #include "clvmd-comms.h" | ||||
| #include "clvmd.h" | ||||
| #include "lvm-functions.h" | ||||
|  | ||||
| #include "locking.h" | ||||
|  | ||||
| #include <corosync/cpg.h> | ||||
| #include <corosync/quorum.h> | ||||
|  | ||||
| #ifdef HAVE_COROSYNC_CONFDB_H | ||||
| #  include <corosync/confdb.h> | ||||
| #elif defined HAVE_COROSYNC_CMAP_H | ||||
| #  include <corosync/cmap.h> | ||||
| #else | ||||
| #  error "Either HAVE_COROSYNC_CONFDB_H or HAVE_COROSYNC_CMAP_H must be defined." | ||||
| #endif | ||||
|  | ||||
| #include <libdlm.h> | ||||
|  | ||||
| #include <syslog.h> | ||||
|  | ||||
| /* Timeout value for several corosync calls */ | ||||
| #define LOCKSPACE_NAME "clvmd" | ||||
|  | ||||
| static void corosync_cpg_deliver_callback (cpg_handle_t handle, | ||||
| 				  const struct cpg_name *groupName, | ||||
| 				  uint32_t nodeid, | ||||
| 				  uint32_t pid, | ||||
| 				  void *msg, | ||||
| 				  size_t msg_len); | ||||
| static void corosync_cpg_confchg_callback(cpg_handle_t handle, | ||||
| 				 const struct cpg_name *groupName, | ||||
| 				 const struct cpg_address *member_list, size_t member_list_entries, | ||||
| 				 const struct cpg_address *left_list, size_t left_list_entries, | ||||
| 				 const struct cpg_address *joined_list, size_t joined_list_entries); | ||||
| static void _cluster_closedown(void); | ||||
|  | ||||
| /* Hash list of nodes in the cluster */ | ||||
| static struct dm_hash_table *node_hash; | ||||
|  | ||||
| /* Number of active nodes */ | ||||
| static int num_nodes; | ||||
| static unsigned int our_nodeid; | ||||
|  | ||||
| static struct local_client *cluster_client; | ||||
|  | ||||
| /* Corosync handles */ | ||||
| static cpg_handle_t cpg_handle; | ||||
| static quorum_handle_t quorum_handle; | ||||
|  | ||||
| /* DLM Handle */ | ||||
| static dlm_lshandle_t *lockspace; | ||||
|  | ||||
| static struct cpg_name cpg_group_name; | ||||
|  | ||||
| /* Corosync callback structs */ | ||||
| cpg_callbacks_t corosync_cpg_callbacks = { | ||||
| 	.cpg_deliver_fn =            corosync_cpg_deliver_callback, | ||||
| 	.cpg_confchg_fn =            corosync_cpg_confchg_callback, | ||||
| }; | ||||
|  | ||||
| quorum_callbacks_t quorum_callbacks = { | ||||
| 	.quorum_notify_fn = NULL, | ||||
| }; | ||||
|  | ||||
| struct node_info | ||||
| { | ||||
| 	enum {NODE_DOWN, NODE_CLVMD} state; | ||||
| 	int nodeid; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* Set errno to something approximating the right value and return 0 or -1 */ | ||||
| static int cs_to_errno(cs_error_t err) | ||||
| { | ||||
| 	switch(err) | ||||
| 	{ | ||||
| 	case CS_OK: | ||||
| 		return 0; | ||||
|         case CS_ERR_LIBRARY: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_VERSION: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_INIT: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_TIMEOUT: | ||||
| 		errno = ETIME; | ||||
| 		break; | ||||
|         case CS_ERR_TRY_AGAIN: | ||||
| 		errno = EAGAIN; | ||||
| 		break; | ||||
|         case CS_ERR_INVALID_PARAM: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_NO_MEMORY: | ||||
| 		errno = ENOMEM; | ||||
| 		break; | ||||
|         case CS_ERR_BAD_HANDLE: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_BUSY: | ||||
| 		errno = EBUSY; | ||||
| 		break; | ||||
|         case CS_ERR_ACCESS: | ||||
| 		errno = EPERM; | ||||
| 		break; | ||||
|         case CS_ERR_NOT_EXIST: | ||||
| 		errno = ENOENT; | ||||
| 		break; | ||||
|         case CS_ERR_NAME_TOO_LONG: | ||||
| 		errno = ENAMETOOLONG; | ||||
| 		break; | ||||
|         case CS_ERR_EXIST: | ||||
| 		errno = EEXIST; | ||||
| 		break; | ||||
|         case CS_ERR_NO_SPACE: | ||||
| 		errno = ENOSPC; | ||||
| 		break; | ||||
|         case CS_ERR_INTERRUPT: | ||||
| 		errno = EINTR; | ||||
| 		break; | ||||
| 	case CS_ERR_NAME_NOT_FOUND: | ||||
| 		errno = ENOENT; | ||||
| 		break; | ||||
|         case CS_ERR_NO_RESOURCES: | ||||
| 		errno = ENOMEM; | ||||
| 		break; | ||||
|         case CS_ERR_NOT_SUPPORTED: | ||||
| 		errno = EOPNOTSUPP; | ||||
| 		break; | ||||
|         case CS_ERR_BAD_OPERATION: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_FAILED_OPERATION: | ||||
| 		errno = EIO; | ||||
| 		break; | ||||
|         case CS_ERR_MESSAGE_ERROR: | ||||
| 		errno = EIO; | ||||
| 		break; | ||||
|         case CS_ERR_QUEUE_FULL: | ||||
| 		errno = EXFULL; | ||||
| 		break; | ||||
|         case CS_ERR_QUEUE_NOT_AVAILABLE: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_BAD_FLAGS: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case CS_ERR_TOO_BIG: | ||||
| 		errno = E2BIG; | ||||
| 		break; | ||||
|         case CS_ERR_NO_SECTIONS: | ||||
| 		errno = ENOMEM; | ||||
| 		break; | ||||
| 	default: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static char *print_corosync_csid(const char *csid) | ||||
| { | ||||
| 	static char buf[128]; | ||||
| 	int id; | ||||
|  | ||||
| 	memcpy(&id, csid, sizeof(int)); | ||||
| 	sprintf(buf, "%d", id); | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static void corosync_cpg_deliver_callback (cpg_handle_t handle, | ||||
| 				  const struct cpg_name *groupName, | ||||
| 				  uint32_t nodeid, | ||||
| 				  uint32_t pid, | ||||
| 				  void *msg, | ||||
| 				  size_t msg_len) | ||||
| { | ||||
| 	int target_nodeid; | ||||
|  | ||||
| 	memcpy(&target_nodeid, msg, COROSYNC_CSID_LEN); | ||||
|  | ||||
| 	DEBUGLOG("%u got message from nodeid %d for %d. len %zd\n", | ||||
| 		 our_nodeid, nodeid, target_nodeid, msg_len-4); | ||||
|  | ||||
| 	if (nodeid != our_nodeid) | ||||
| 		if (target_nodeid == our_nodeid || target_nodeid == 0) | ||||
| 			process_message(cluster_client, (char *)msg+COROSYNC_CSID_LEN, | ||||
| 					msg_len-COROSYNC_CSID_LEN, (char*)&nodeid); | ||||
| } | ||||
|  | ||||
| static void corosync_cpg_confchg_callback(cpg_handle_t handle, | ||||
| 				 const struct cpg_name *groupName, | ||||
| 				 const struct cpg_address *member_list, size_t member_list_entries, | ||||
| 				 const struct cpg_address *left_list, size_t left_list_entries, | ||||
| 				 const struct cpg_address *joined_list, size_t joined_list_entries) | ||||
| { | ||||
| 	int i; | ||||
| 	struct node_info *ninfo; | ||||
|  | ||||
| 	DEBUGLOG("confchg callback. %zd joined, %zd left, %zd members\n", | ||||
| 		 joined_list_entries, left_list_entries, member_list_entries); | ||||
|  | ||||
| 	for (i=0; i<joined_list_entries; i++) { | ||||
| 		ninfo = dm_hash_lookup_binary(node_hash, | ||||
| 					      (char *)&joined_list[i].nodeid, | ||||
| 					      COROSYNC_CSID_LEN); | ||||
| 		if (!ninfo) { | ||||
| 			ninfo = malloc(sizeof(struct node_info)); | ||||
| 			if (!ninfo) { | ||||
| 				break; | ||||
| 			} | ||||
| 			else { | ||||
| 				ninfo->nodeid = joined_list[i].nodeid; | ||||
| 				dm_hash_insert_binary(node_hash, | ||||
| 						      (char *)&ninfo->nodeid, | ||||
| 						      COROSYNC_CSID_LEN, ninfo); | ||||
| 			} | ||||
| 		} | ||||
| 		ninfo->state = NODE_CLVMD; | ||||
| 	} | ||||
|  | ||||
| 	for (i=0; i<left_list_entries; i++) { | ||||
| 		ninfo = dm_hash_lookup_binary(node_hash, | ||||
| 					      (char *)&left_list[i].nodeid, | ||||
| 					      COROSYNC_CSID_LEN); | ||||
| 		if (ninfo) | ||||
| 			ninfo->state = NODE_DOWN; | ||||
| 	} | ||||
|  | ||||
| 	num_nodes = member_list_entries; | ||||
| } | ||||
|  | ||||
| static int _init_cluster(void) | ||||
| { | ||||
| 	cs_error_t err; | ||||
|  | ||||
| #ifdef QUORUM_SET	/* corosync/quorum.h */ | ||||
| 	uint32_t quorum_type; | ||||
| #endif | ||||
|  | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| #ifdef QUORUM_SET | ||||
| 	err = quorum_initialize(&quorum_handle, | ||||
| 				&quorum_callbacks, | ||||
| 				&quorum_type); | ||||
|  | ||||
| 	if (quorum_type != QUORUM_SET) { | ||||
| 		syslog(LOG_ERR, "Corosync quorum service is not configured"); | ||||
| 		DEBUGLOG("Corosync quorum service is not configured"); | ||||
| 		return EINVAL; | ||||
| 	} | ||||
| #else | ||||
| 	err = quorum_initialize(&quorum_handle, | ||||
| 				&quorum_callbacks); | ||||
| #endif | ||||
|  | ||||
| 	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_open_lockspace(LOCKSPACE_NAME); | ||||
| 	if (!lockspace) { | ||||
| 		lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600); | ||||
| 		if (!lockspace) { | ||||
| 			syslog(LOG_ERR, "Unable to create DLM lockspace for CLVM: %m"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		DEBUGLOG("Created DLM lockspace for CLVMD.\n"); | ||||
| 	} else | ||||
| 		DEBUGLOG("Opened existing DLM lockspace for CLVMD.\n"); | ||||
|  | ||||
| 	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) | ||||
| { | ||||
| 	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(void) | ||||
| { | ||||
| 	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; | ||||
|  | ||||
| 	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_CLVMD) | ||||
| 			callback(master_client, csid, 1); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* 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(void) | ||||
| { | ||||
| 	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) | ||||
| { | ||||
| 	static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
| 	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; | ||||
|  | ||||
| 	pthread_mutex_lock(&_mutex); | ||||
| 	err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2); | ||||
| 	pthread_mutex_unlock(&_mutex); | ||||
|  | ||||
| 	return cs_to_errno(err); | ||||
| } | ||||
|  | ||||
| #ifdef HAVE_COROSYNC_CONFDB_H | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
|  | ||||
| #elif defined HAVE_COROSYNC_CMAP_H | ||||
|  | ||||
| static int _get_cluster_name(char *buf, int buflen) | ||||
| { | ||||
| 	cmap_handle_t cmap_handle = 0; | ||||
| 	int result; | ||||
| 	char *name = NULL; | ||||
|  | ||||
| 	/* This is a default in case everything else fails */ | ||||
| 	strncpy(buf, "Corosync", buflen); | ||||
|  | ||||
| 	/* Look for a cluster name in cmap */ | ||||
| 	result = cmap_initialize(&cmap_handle); | ||||
| 	if (result != CS_OK) | ||||
| 		return 0; | ||||
|  | ||||
| 	result = cmap_get_string(cmap_handle, "totem.cluster_name", &name); | ||||
| 	if (result != CS_OK) | ||||
| 		goto out; | ||||
|  | ||||
| 	memset(buf, 0, buflen); | ||||
| 	strncpy(buf, name, buflen - 1); | ||||
|  | ||||
| out: | ||||
| 	if (name) | ||||
| 		free(name); | ||||
| 	cmap_finalize(cmap_handle); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| static struct cluster_ops _cluster_corosync_ops = { | ||||
| 	.name                     = "corosync", | ||||
| 	.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; | ||||
| } | ||||
							
								
								
									
										689
									
								
								daemons/clvmd/clvmd-openais.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										689
									
								
								daemons/clvmd/clvmd-openais.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,689 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * This provides the interface between clvmd and OpenAIS as the cluster | ||||
|  * and lock manager. | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <fcntl.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| #include <openais/saAis.h> | ||||
| #include <openais/saLck.h> | ||||
|  | ||||
| #include <corosync/corotypes.h> | ||||
| #include <corosync/cpg.h> | ||||
|  | ||||
| #include "locking.h" | ||||
| #include "clvm.h" | ||||
| #include "clvmd-comms.h" | ||||
| #include "lvm-functions.h" | ||||
| #include "clvmd.h" | ||||
|  | ||||
| /* Timeout value for several openais calls */ | ||||
| #define TIMEOUT 10 | ||||
|  | ||||
| static void openais_cpg_deliver_callback (cpg_handle_t handle, | ||||
| 				  const struct cpg_name *groupName, | ||||
| 				  uint32_t nodeid, | ||||
| 				  uint32_t pid, | ||||
| 				  void *msg, | ||||
| 				  size_t msg_len); | ||||
| static void openais_cpg_confchg_callback(cpg_handle_t handle, | ||||
| 				 const struct cpg_name *groupName, | ||||
| 				 const struct cpg_address *member_list, size_t member_list_entries, | ||||
| 				 const struct cpg_address *left_list, size_t left_list_entries, | ||||
| 				 const struct cpg_address *joined_list, size_t joined_list_entries); | ||||
|  | ||||
| static void _cluster_closedown(void); | ||||
|  | ||||
| /* Hash list of nodes in the cluster */ | ||||
| static struct dm_hash_table *node_hash; | ||||
|  | ||||
| /* For associating lock IDs & resource handles */ | ||||
| static struct dm_hash_table *lock_hash; | ||||
|  | ||||
| /* Number of active nodes */ | ||||
| static int num_nodes; | ||||
| static unsigned int our_nodeid; | ||||
|  | ||||
| static struct local_client *cluster_client; | ||||
|  | ||||
| /* OpenAIS handles */ | ||||
| static cpg_handle_t cpg_handle; | ||||
| static SaLckHandleT lck_handle; | ||||
|  | ||||
| static struct cpg_name cpg_group_name; | ||||
|  | ||||
| /* Openais callback structs */ | ||||
| cpg_callbacks_t openais_cpg_callbacks = { | ||||
| 	.cpg_deliver_fn =            openais_cpg_deliver_callback, | ||||
| 	.cpg_confchg_fn =            openais_cpg_confchg_callback, | ||||
| }; | ||||
|  | ||||
| struct node_info | ||||
| { | ||||
| 	enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state; | ||||
| 	int nodeid; | ||||
| }; | ||||
|  | ||||
| struct lock_info | ||||
| { | ||||
| 	SaLckResourceHandleT res_handle; | ||||
| 	SaLckLockIdT         lock_id; | ||||
| 	SaNameT              lock_name; | ||||
| }; | ||||
|  | ||||
| /* Set errno to something approximating the right value and return 0 or -1 */ | ||||
| static int ais_to_errno(SaAisErrorT err) | ||||
| { | ||||
| 	switch(err) | ||||
| 	{ | ||||
| 	case SA_AIS_OK: | ||||
| 		return 0; | ||||
|         case SA_AIS_ERR_LIBRARY: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_VERSION: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_INIT: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_TIMEOUT: | ||||
| 		errno = ETIME; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_TRY_AGAIN: | ||||
| 		errno = EAGAIN; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_INVALID_PARAM: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NO_MEMORY: | ||||
| 		errno = ENOMEM; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_BAD_HANDLE: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_BUSY: | ||||
| 		errno = EBUSY; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_ACCESS: | ||||
| 		errno = EPERM; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NOT_EXIST: | ||||
| 		errno = ENOENT; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NAME_TOO_LONG: | ||||
| 		errno = ENAMETOOLONG; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_EXIST: | ||||
| 		errno = EEXIST; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NO_SPACE: | ||||
| 		errno = ENOSPC; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_INTERRUPT: | ||||
| 		errno = EINTR; | ||||
| 		break; | ||||
| 	case SA_AIS_ERR_NAME_NOT_FOUND: | ||||
| 		errno = ENOENT; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NO_RESOURCES: | ||||
| 		errno = ENOMEM; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NOT_SUPPORTED: | ||||
| 		errno = EOPNOTSUPP; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_BAD_OPERATION: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_FAILED_OPERATION: | ||||
| 		errno = EIO; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_MESSAGE_ERROR: | ||||
| 		errno = EIO; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_QUEUE_FULL: | ||||
| 		errno = EXFULL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_QUEUE_NOT_AVAILABLE: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_BAD_FLAGS: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_TOO_BIG: | ||||
| 		errno = E2BIG; | ||||
| 		break; | ||||
|         case SA_AIS_ERR_NO_SECTIONS: | ||||
| 		errno = ENOMEM; | ||||
| 		break; | ||||
| 	default: | ||||
| 		errno = EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static char *print_openais_csid(const char *csid) | ||||
| { | ||||
| 	static char buf[128]; | ||||
| 	int id; | ||||
|  | ||||
| 	memcpy(&id, csid, sizeof(int)); | ||||
| 	sprintf(buf, "%d", id); | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static int add_internal_client(int fd, fd_callback_t callback) | ||||
| { | ||||
| 	struct local_client *client; | ||||
|  | ||||
| 	DEBUGLOG("Add_internal_client, fd = %d\n", fd); | ||||
|  | ||||
| 	if (!(client = dm_zalloc(sizeof(*client)))) { | ||||
| 		DEBUGLOG("malloc failed\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	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 %" PRIsize_t "\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. %" PRIsize_t " joined, " | ||||
| 		 FMTsize_t " left, %" PRIsize_t " members\n", | ||||
| 		 joined_list_entries, left_list_entries, member_list_entries); | ||||
|  | ||||
| 	for (i=0; i<joined_list_entries; i++) { | ||||
| 		ninfo = dm_hash_lookup_binary(node_hash, | ||||
| 					      (char *)&joined_list[i].nodeid, | ||||
| 					      OPENAIS_CSID_LEN); | ||||
| 		if (!ninfo) { | ||||
| 			ninfo = malloc(sizeof(struct node_info)); | ||||
| 			if (!ninfo) { | ||||
| 				break; | ||||
| 			} | ||||
| 			else { | ||||
| 				ninfo->nodeid = joined_list[i].nodeid; | ||||
| 				dm_hash_insert_binary(node_hash, | ||||
| 						      (char *)&ninfo->nodeid, | ||||
| 						      OPENAIS_CSID_LEN, ninfo); | ||||
| 			} | ||||
| 		} | ||||
| 		ninfo->state = NODE_CLVMD; | ||||
| 	} | ||||
|  | ||||
| 	for (i=0; i<left_list_entries; i++) { | ||||
| 		ninfo = dm_hash_lookup_binary(node_hash, | ||||
| 					      (char *)&left_list[i].nodeid, | ||||
| 					      OPENAIS_CSID_LEN); | ||||
| 		if (ninfo) | ||||
| 			ninfo->state = NODE_DOWN; | ||||
| 	} | ||||
|  | ||||
| 	for (i=0; i<member_list_entries; i++) { | ||||
| 		if (member_list[i].nodeid == 0) continue; | ||||
| 		ninfo = dm_hash_lookup_binary(node_hash, | ||||
| 				(char *)&member_list[i].nodeid, | ||||
| 				OPENAIS_CSID_LEN); | ||||
| 		if (!ninfo) { | ||||
| 			ninfo = malloc(sizeof(struct node_info)); | ||||
| 			if (!ninfo) { | ||||
| 				break; | ||||
| 			} | ||||
| 			else { | ||||
| 				ninfo->nodeid = member_list[i].nodeid; | ||||
| 				dm_hash_insert_binary(node_hash, | ||||
| 						(char *)&ninfo->nodeid, | ||||
| 						OPENAIS_CSID_LEN, ninfo); | ||||
| 			} | ||||
| 		} | ||||
| 		ninfo->state = NODE_CLVMD; | ||||
| 	} | ||||
|  | ||||
| 	num_nodes = member_list_entries; | ||||
| } | ||||
|  | ||||
| static int lck_dispatch(struct local_client *client, char *buf, int len, | ||||
| 			const char *csid, struct local_client **new_client) | ||||
| { | ||||
| 	*new_client = NULL; | ||||
| 	saLckDispatch(lck_handle, SA_DISPATCH_ONE); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _init_cluster(void) | ||||
| { | ||||
| 	SaAisErrorT err; | ||||
| 	SaVersionT  ver = { 'B', 1, 1 }; | ||||
| 	int select_fd; | ||||
|  | ||||
| 	node_hash = dm_hash_create(100); | ||||
| 	lock_hash = dm_hash_create(10); | ||||
|  | ||||
| 	err = cpg_initialize(&cpg_handle, | ||||
| 			     &openais_cpg_callbacks); | ||||
| 	if (err != SA_AIS_OK) { | ||||
| 		syslog(LOG_ERR, "Cannot initialise OpenAIS CPG service: %d", | ||||
| 		       err); | ||||
| 		DEBUGLOG("Cannot initialise OpenAIS CPG service: %d", err); | ||||
| 		return ais_to_errno(err); | ||||
| 	} | ||||
|  | ||||
| 	err = saLckInitialize(&lck_handle, | ||||
| 					NULL, | ||||
| 			      &ver); | ||||
| 	if (err != SA_AIS_OK) { | ||||
| 		cpg_initialize(&cpg_handle, &openais_cpg_callbacks); | ||||
| 		syslog(LOG_ERR, "Cannot initialise OpenAIS lock service: %d", | ||||
| 		       err); | ||||
| 		DEBUGLOG("Cannot initialise OpenAIS lock service: %d\n\n", err); | ||||
| 		return ais_to_errno(err); | ||||
| 	} | ||||
|  | ||||
| 	/* Connect to the clvmd group */ | ||||
| 	strcpy((char *)cpg_group_name.value, "clvmd"); | ||||
| 	cpg_group_name.length = strlen((char *)cpg_group_name.value); | ||||
| 	err = cpg_join(cpg_handle, &cpg_group_name); | ||||
| 	if (err != SA_AIS_OK) { | ||||
| 		cpg_finalize(cpg_handle); | ||||
| 		saLckFinalize(lck_handle); | ||||
| 		syslog(LOG_ERR, "Cannot join clvmd process group"); | ||||
| 		DEBUGLOG("Cannot join clvmd process group: %d\n", err); | ||||
| 		return ais_to_errno(err); | ||||
| 	} | ||||
|  | ||||
| 	err = cpg_local_get(cpg_handle, | ||||
| 			    &our_nodeid); | ||||
| 	if (err != SA_AIS_OK) { | ||||
| 		cpg_finalize(cpg_handle); | ||||
| 		saLckFinalize(lck_handle); | ||||
| 		syslog(LOG_ERR, "Cannot get local node id\n"); | ||||
| 		return ais_to_errno(err); | ||||
| 	} | ||||
| 	DEBUGLOG("Our local node id is %d\n", our_nodeid); | ||||
|  | ||||
| 	saLckSelectionObjectGet(lck_handle, (SaSelectionObjectT *)&select_fd); | ||||
| 	add_internal_client(select_fd, lck_dispatch); | ||||
|  | ||||
| 	DEBUGLOG("Connected to OpenAIS\n"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void _cluster_closedown(void) | ||||
| { | ||||
| 	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=%" PRIx64 "\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: %" PRIx64 "\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 = { | ||||
| 	.name                     = "openais", | ||||
| 	.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; | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
							
								
								
									
										382
									
								
								daemons/clvmd/clvmd-singlenode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								daemons/clvmd/clvmd-singlenode.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,382 @@ | ||||
| /* | ||||
|  * Copyright (C) 2009-2013 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser 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 | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include "locking.h" | ||||
| #include "clvm.h" | ||||
| #include "clvmd-comms.h" | ||||
| #include "clvmd.h" | ||||
|  | ||||
| #include <sys/un.h> | ||||
| #include <sys/socket.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| static const char SINGLENODE_CLVMD_SOCKNAME[] = DEFAULT_RUN_DIR "/clvmd_singlenode.sock"; | ||||
| static int listen_fd = -1; | ||||
|  | ||||
| static struct dm_hash_table *_locks; | ||||
| static int _lockid; | ||||
|  | ||||
| static pthread_mutex_t _lock_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
| /* Using one common condition for all locks for simplicity */ | ||||
| static pthread_cond_t _lock_cond = PTHREAD_COND_INITIALIZER; | ||||
|  | ||||
| struct lock { | ||||
| 	struct dm_list list; | ||||
| 	int lockid; | ||||
| 	int mode; | ||||
| }; | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	mode_t old_mask; | ||||
| 	struct sockaddr_un addr = { .sun_family = AF_UNIX }; | ||||
|  | ||||
| 	if (!dm_strncpy(addr.sun_path, SINGLENODE_CLVMD_SOCKNAME, | ||||
| 			sizeof(addr.sun_path))) { | ||||
| 		DEBUGLOG("%s: singlenode socket name too long.", | ||||
| 			 SINGLENODE_CLVMD_SOCKNAME); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	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 */ | ||||
| 	if (fcntl(listen_fd, F_SETFD, 1)) { | ||||
| 		DEBUGLOG("Setting CLOEXEC on client fd failed: %s\n", strerror(errno)); | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	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; | ||||
|  | ||||
| 	if (!(_locks = dm_hash_create(128))) { | ||||
| 		DEBUGLOG("Failed to allocate single-node hash table.\n"); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	r = init_comms(); | ||||
| 	if (r) { | ||||
| 		dm_hash_destroy(_locks); | ||||
| 		_locks = NULL; | ||||
| 		return r; | ||||
| 	} | ||||
|  | ||||
| 	DEBUGLOG("Single-node cluster initialised.\n"); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void _cluster_closedown(void) | ||||
| { | ||||
| 	close_comms(); | ||||
|  | ||||
| 	/* If there is any awaited resource, kill it softly */ | ||||
| 	pthread_mutex_lock(&_lock_mutex); | ||||
| 	dm_hash_destroy(_locks); | ||||
| 	_locks = NULL; | ||||
| 	_lockid = 0; | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* wakeup waiters */ | ||||
| 	pthread_mutex_unlock(&_lock_mutex); | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	strcpy(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 const char *_get_mode(int mode) | ||||
| { | ||||
| 	switch (mode) { | ||||
| 	case LCK_NULL: return "NULL"; | ||||
| 	case LCK_READ: return "READ"; | ||||
| 	case LCK_PREAD: return "PREAD"; | ||||
| 	case LCK_WRITE: return "WRITE"; | ||||
| 	case LCK_EXCL: return "EXCLUSIVE"; | ||||
| 	case LCK_UNLOCK: return "UNLOCK"; | ||||
| 	default: return "????"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Real locking */ | ||||
| static int _lock_resource(const char *resource, int mode, int flags, int *lockid) | ||||
| { | ||||
| 	/* DLM table of allowed transition states */ | ||||
| 	static const int _dlm_table[6][6] = { | ||||
| 	/* Mode	   NL	CR	CW	PR	PW	EX */ | ||||
| 	/* NL */ { 1,	 1,	 1,	 1,	 1,	 1}, | ||||
| 	/* CR */ { 1,	 1,	 1,	 1,	 1,	 0}, | ||||
| 	/* CW */ { 1,	 1,	 1,	 0,	 0,	 0}, | ||||
| 	/* PR */ { 1,	 1,	 0,	 1,	 0,	 0}, | ||||
| 	/* PW */ { 1,	 1,	 0,	 0,	 0,	 0}, | ||||
| 	/* EX */ { 1,	 0,	 0,	 0,	 0,	 0} | ||||
| 	}; | ||||
|  | ||||
| 	struct lock *lck = NULL, *lckt; | ||||
| 	struct dm_list *head; | ||||
|  | ||||
| 	DEBUGLOG("Locking resource %s, flags=0x%02x (%s%s%s), mode=%s (%d)\n", | ||||
| 		 resource, flags, | ||||
| 		 (flags & LCKF_NOQUEUE) ? "NOQUEUE" : "", | ||||
| 		 ((flags & (LCKF_NOQUEUE | LCKF_CONVERT)) == | ||||
| 		  (LCKF_NOQUEUE | LCKF_CONVERT)) ? "|" : "", | ||||
| 		 (flags & LCKF_CONVERT) ? "CONVERT" : "", | ||||
| 		 _get_mode(mode), mode); | ||||
|  | ||||
| 	mode &= LCK_TYPE_MASK; | ||||
| 	pthread_mutex_lock(&_lock_mutex); | ||||
|  | ||||
| retry: | ||||
| 	if (!(head = dm_hash_lookup(_locks, resource))) { | ||||
| 		if (flags & LCKF_CONVERT) { | ||||
| 			/* In real DLM, lock is identified only by lockid, resource is not used */ | ||||
| 			DEBUGLOG("Unlocked resource %s cannot be converted\n", resource); | ||||
| 			goto_bad; | ||||
| 		} | ||||
| 		/* Add new locked resource */ | ||||
| 		if (!(head = dm_malloc(sizeof(struct dm_list))) || | ||||
| 		    !dm_hash_insert(_locks, resource, head)) { | ||||
| 			dm_free(head); | ||||
| 			goto_bad; | ||||
| 		} | ||||
|  | ||||
| 		dm_list_init(head); | ||||
| 	} else	/* Update/convert locked resource */ | ||||
| 		dm_list_iterate_items(lck, head) { | ||||
| 			/* Check is all locks are compatible with requested lock */ | ||||
| 			if (flags & LCKF_CONVERT) { | ||||
| 				if (lck->lockid != *lockid) | ||||
| 					continue; | ||||
|  | ||||
| 				DEBUGLOG("Converting resource %s lockid=%d mode:%s -> %s...\n", | ||||
| 					 resource, lck->lockid, _get_mode(lck->mode), _get_mode(mode)); | ||||
| 				dm_list_iterate_items(lckt, head) { | ||||
| 					if ((lckt->lockid != *lockid) && | ||||
| 					    !_dlm_table[mode][lckt->mode]) { | ||||
| 						if (!(flags & LCKF_NOQUEUE) && | ||||
| 						    /* TODO: Real dlm uses here conversion queues */ | ||||
| 						    !pthread_cond_wait(&_lock_cond, &_lock_mutex) && | ||||
| 						    _locks) /* End of the game? */ | ||||
| 							goto retry; | ||||
| 						goto bad; | ||||
| 					} | ||||
| 				} | ||||
| 				lck->mode = mode; /* Lock is now converted */ | ||||
| 				goto out; | ||||
| 			} else if (!_dlm_table[mode][lck->mode]) { | ||||
| 				DEBUGLOG("Resource %s already locked lockid=%d, mode:%s\n", | ||||
| 					 resource, lck->lockid, _get_mode(lck->mode)); | ||||
| 				if (!(flags & LCKF_NOQUEUE) && | ||||
| 				    !pthread_cond_wait(&_lock_cond, &_lock_mutex) && | ||||
| 				    _locks) { /* End of the game? */ | ||||
| 					DEBUGLOG("Resource %s retrying lock in mode:%s...\n", | ||||
| 						 resource, _get_mode(mode)); | ||||
| 					goto retry; | ||||
| 				} | ||||
| 				goto bad; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	if (!(flags & LCKF_CONVERT)) { | ||||
| 		if (!(lck = dm_malloc(sizeof(struct lock)))) | ||||
| 			goto_bad; | ||||
|  | ||||
| 		*lockid = lck->lockid = ++_lockid; | ||||
| 		lck->mode = mode; | ||||
| 		dm_list_add(head, &lck->list); | ||||
| 	} | ||||
| out: | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */ | ||||
| 	pthread_mutex_unlock(&_lock_mutex); | ||||
| 	DEBUGLOG("Locked resource %s, lockid=%d, mode=%s\n", | ||||
| 		 resource, lck->lockid, _get_mode(lck->mode)); | ||||
|  | ||||
| 	return 0; | ||||
| bad: | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */ | ||||
| 	pthread_mutex_unlock(&_lock_mutex); | ||||
| 	DEBUGLOG("Failed to lock resource %s\n", resource); | ||||
|  | ||||
| 	return 1; /* fail */ | ||||
| } | ||||
|  | ||||
| static int _unlock_resource(const char *resource, int lockid) | ||||
| { | ||||
| 	struct lock *lck; | ||||
| 	struct dm_list *head; | ||||
| 	int r = 1; | ||||
|  | ||||
| 	if (lockid < 0) { | ||||
| 		DEBUGLOG("Not tracking unlock of lockid -1: %s, lockid=%d\n", | ||||
| 			 resource, lockid); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	DEBUGLOG("Unlocking resource %s, lockid=%d\n", resource, lockid); | ||||
| 	pthread_mutex_lock(&_lock_mutex); | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* wakeup waiters */ | ||||
|  | ||||
| 	if (!(head = dm_hash_lookup(_locks, resource))) { | ||||
| 		pthread_mutex_unlock(&_lock_mutex); | ||||
| 		DEBUGLOG("Resource %s is not locked.\n", resource); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	dm_list_iterate_items(lck, head) | ||||
| 		if (lck->lockid == lockid) { | ||||
| 			dm_list_del(&lck->list); | ||||
| 			dm_free(lck); | ||||
| 			r = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 	DEBUGLOG("Resource %s has wrong lockid %d.\n", resource, lockid); | ||||
| out: | ||||
| 	if (dm_list_empty(head)) { | ||||
| 		//DEBUGLOG("Resource %s is no longer hashed (lockid=%d).\n", resource, lockid); | ||||
| 		dm_hash_remove(_locks, resource); | ||||
| 		dm_free(head); | ||||
| 	} | ||||
|  | ||||
| 	pthread_mutex_unlock(&_lock_mutex); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	return dm_strncpy(buf, "localcluster", buflen) ? 0 : 1; | ||||
| } | ||||
|  | ||||
| static struct cluster_ops _cluster_singlenode_ops = { | ||||
| 	.name                     = "singlenode", | ||||
| 	.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; | ||||
|  | ||||
| 	return NULL; | ||||
| } | ||||
							
								
								
									
										2381
									
								
								daemons/clvmd/clvmd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2381
									
								
								daemons/clvmd/clvmd.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										126
									
								
								daemons/clvmd/clvmd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								daemons/clvmd/clvmd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 */ | ||||
| 	int cleanup_needed;     /* helper for cleanup_zombie */ | ||||
| 	struct local_client *pipe_client; | ||||
| 	pthread_t threadid; | ||||
| 	enum { PRE_COMMAND, POST_COMMAND } state; | ||||
| 	pthread_mutex_t mutex;	/* Main thread and worker synchronisation */ | ||||
| 	pthread_cond_t cond; | ||||
| }; | ||||
|  | ||||
| /* 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, char *buf, | ||||
| 			    int len, const char *csid); | ||||
| extern void debuglog(const char *fmt, ... ) | ||||
|   __attribute__ ((format(printf, 1, 2))); | ||||
|  | ||||
| void clvmd_set_debug(debug_t new_de); | ||||
| debug_t clvmd_get_debug(void); | ||||
| int clvmd_get_foreground(void); | ||||
|  | ||||
| int sync_lock(const char *resource, int mode, int flags, int *lockid); | ||||
| int sync_unlock(const char *resource, int lockid); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										939
									
								
								daemons/clvmd/lvm-functions.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										939
									
								
								daemons/clvmd/lvm-functions.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,939 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include "clvm.h" | ||||
| #include "clvmd-comms.h" | ||||
| #include "clvmd.h" | ||||
| #include "lvm-functions.h" | ||||
|  | ||||
| /* LVM2 headers */ | ||||
| #include "toolcontext.h" | ||||
| #include "lvmcache.h" | ||||
| #include "lvm-globals.h" | ||||
| #include "activate.h" | ||||
| #include "archiver.h" | ||||
| #include "memlock.h" | ||||
|  | ||||
| #include <syslog.h> | ||||
|  | ||||
| static struct cmd_context *cmd = NULL; | ||||
| static struct dm_hash_table *lv_hash = NULL; | ||||
| static pthread_mutex_t lv_hash_lock; | ||||
| static pthread_mutex_t lvm_lock; | ||||
| static char last_error[1024]; | ||||
|  | ||||
| struct lv_info { | ||||
| 	int lock_id; | ||||
| 	int lock_mode; | ||||
| }; | ||||
|  | ||||
| static const char *decode_full_locking_cmd(uint32_t cmdl) | ||||
| { | ||||
| 	static char buf[128]; | ||||
| 	const char *type; | ||||
| 	const char *scope; | ||||
| 	const char *command; | ||||
|  | ||||
| 	switch (cmdl & LCK_TYPE_MASK) { | ||||
| 	case LCK_NULL: | ||||
| 		type = "NULL"; | ||||
| 		break; | ||||
| 	case LCK_READ: | ||||
| 		type = "READ"; | ||||
| 		break; | ||||
| 	case LCK_PREAD: | ||||
| 		type = "PREAD"; | ||||
| 		break; | ||||
| 	case LCK_WRITE: | ||||
| 		type = "WRITE"; | ||||
| 		break; | ||||
| 	case LCK_EXCL: | ||||
| 		type = "EXCL"; | ||||
| 		break; | ||||
| 	case LCK_UNLOCK: | ||||
| 		type = "UNLOCK"; | ||||
| 		break; | ||||
| 	default: | ||||
| 		type = "unknown"; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	switch (cmdl & LCK_SCOPE_MASK) { | ||||
| 	case LCK_VG: | ||||
| 		scope = "VG"; | ||||
| 		command = "LCK_VG"; | ||||
| 		break; | ||||
| 	case LCK_LV: | ||||
| 		scope = "LV"; | ||||
| 		switch (cmdl & LCK_MASK) { | ||||
| 		case LCK_LV_EXCLUSIVE & LCK_MASK: | ||||
| 			command = "LCK_LV_EXCLUSIVE"; | ||||
| 			break; | ||||
| 		case LCK_LV_SUSPEND & LCK_MASK: | ||||
| 			command = "LCK_LV_SUSPEND"; | ||||
| 			break; | ||||
| 		case LCK_LV_RESUME & LCK_MASK: | ||||
| 			command = "LCK_LV_RESUME"; | ||||
| 			break; | ||||
| 		case LCK_LV_ACTIVATE & LCK_MASK: | ||||
| 			command = "LCK_LV_ACTIVATE"; | ||||
| 			break; | ||||
| 		case LCK_LV_DEACTIVATE & LCK_MASK: | ||||
| 			command = "LCK_LV_DEACTIVATE"; | ||||
| 			break; | ||||
| 		default: | ||||
| 			command = "unknown"; | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		scope = "unknown"; | ||||
| 		command = "unknown"; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	sprintf(buf, "0x%x %s (%s|%s%s%s%s%s)", cmdl, command, type, scope, | ||||
| 		cmdl & LCK_NONBLOCK   ? "|NONBLOCK" : "", | ||||
| 		cmdl & LCK_HOLD       ? "|HOLD" : "", | ||||
| 		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%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_TEST_MODE ? "TEST|" : "", | ||||
| 		flags & LCK_CONVERT_MODE ? "CONVERT|" : "", | ||||
| 		flags & LCK_DMEVENTD_MONITOR_IGNORE ? "DMEVENTD_MONITOR_IGNORE|" : "", | ||||
| 		flags & LCK_REVERT_MODE ? "REVERT|" : ""); | ||||
|  | ||||
| 	if (len > 1) | ||||
| 		buf[len - 2] = ' '; | ||||
| 	else | ||||
| 		buf[0] = '\0'; | ||||
|  | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| char *get_last_lvm_error(void) | ||||
| { | ||||
| 	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 int insert_info(const char *resource, struct lv_info *lvi) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| 	pthread_mutex_lock(&lv_hash_lock); | ||||
| 	ret = dm_hash_insert(lv_hash, resource, lvi); | ||||
| 	pthread_mutex_unlock(&lv_hash_lock); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void remove_info(const char *resource) | ||||
| { | ||||
| 	int num_open; | ||||
|  | ||||
| 	pthread_mutex_lock(&lv_hash_lock); | ||||
| 	dm_hash_remove(lv_hash, resource); | ||||
|  | ||||
| 	/* When last lock is remove, validate there are not left opened devices */ | ||||
| 	if (!dm_hash_get_first(lv_hash)) { | ||||
| 		if (critical_section()) | ||||
| 			log_error(INTERNAL_ERROR "No volumes are locked however clvmd is in activation mode critical section."); | ||||
| 		if ((num_open = dev_cache_check_for_open_devices())) | ||||
| 			log_error(INTERNAL_ERROR "No volumes are locked however %d devices are still open.", num_open); | ||||
| 	} | ||||
|  | ||||
| 	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(void) | ||||
| { | ||||
| 	/* Create hash table for keeping LV locks & status */ | ||||
| 	lv_hash = dm_hash_create(1024); | ||||
| 	pthread_mutex_init(&lv_hash_lock, NULL); | ||||
| 	pthread_mutex_init(&lvm_lock, NULL); | ||||
| } | ||||
|  | ||||
| /* Called at shutdown to tidy the lockspace */ | ||||
| void destroy_lvhash(void) | ||||
| { | ||||
| 	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)); | ||||
| 		dm_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) { | ||||
| 		if (lvi->lock_mode == mode) { | ||||
| 			DEBUGLOG("hold_lock, lock mode %d already held\n", | ||||
| 				 mode); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if ((lvi->lock_mode == LCK_EXCL) && (mode == LCK_WRITE)) { | ||||
| 			DEBUGLOG("hold_lock, lock already held LCK_EXCL, " | ||||
| 				 "ignoring LCK_WRITE request\n"); | ||||
| 			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; | ||||
| 		else | ||||
| 			DEBUGLOG("hold_lock. convert to %d failed: %s\n", mode, | ||||
| 				 strerror(errno)); | ||||
| 		errno = saved_errno; | ||||
| 	} else { | ||||
| 		if (!(lvi = dm_malloc(sizeof(struct lv_info)))) { | ||||
| 			errno = ENOMEM; | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		lvi->lock_mode = mode; | ||||
| 		lvi->lock_id = 0; | ||||
| 		status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id); | ||||
| 		saved_errno = errno; | ||||
| 		if (status) { | ||||
| 			dm_free(lvi); | ||||
| 			DEBUGLOG("hold_lock. lock at %d failed: %s\n", mode, | ||||
| 				 strerror(errno)); | ||||
| 		} else | ||||
| 			if (!insert_info(resource, lvi)) { | ||||
| 				errno = ENOMEM; | ||||
| 				return -1; | ||||
| 			} | ||||
|  | ||||
| 		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); | ||||
| 		dm_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 command, 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 && (command & 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, NULL)) | ||||
| 		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 (!test_mode() && command & LCK_CLUSTER_VG) { | ||||
| 		status = hold_lock(resource, mode, LCKF_NOQUEUE | ((lock_flags & LCK_CONVERT_MODE) ? 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) { | ||||
| 		critical_section_inc(cmd, "resuming"); | ||||
| 		if (!lv_resume(cmd, resource, 0, NULL)) { | ||||
| 			critical_section_dec(cmd, "resumed"); | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Now activate it */ | ||||
| 	if (!lv_activate(cmd, resource, exclusive, 0, 0, NULL)) | ||||
| 		goto error; | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| error: | ||||
| 	if (!test_mode() && (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 command, unsigned char lock_flags) | ||||
| { | ||||
| 	int oldmode, origin_only, exclusive, revert; | ||||
|  | ||||
| 	/* Is it open ? */ | ||||
| 	oldmode = get_current_lock(resource); | ||||
| 	if (oldmode == -1 && (command & LCK_CLUSTER_VG)) { | ||||
| 		DEBUGLOG("do_resume_lv, lock not already held\n"); | ||||
| 		return 0;	/* We don't need to do anything */ | ||||
| 	} | ||||
| 	origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0; | ||||
| 	exclusive = (oldmode == LCK_EXCL) ? 1 : 0; | ||||
| 	revert = (lock_flags & LCK_REVERT_MODE) ? 1 : 0; | ||||
|  | ||||
| 	if (!lv_resume_if_active(cmd, resource, origin_only, exclusive, revert, NULL)) | ||||
| 		return EIO; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Suspend the device if active */ | ||||
| static int do_suspend_lv(char *resource, unsigned char command, unsigned char lock_flags) | ||||
| { | ||||
| 	int oldmode; | ||||
| 	unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0; | ||||
| 	unsigned exclusive; | ||||
|  | ||||
| 	/* Is it open ? */ | ||||
| 	oldmode = get_current_lock(resource); | ||||
| 	if (oldmode == -1 && (command & LCK_CLUSTER_VG)) { | ||||
| 		DEBUGLOG("do_suspend_lv, lock not already held\n"); | ||||
| 		return 0; /* Not active, so it's OK */ | ||||
| 	} | ||||
|  | ||||
| 	exclusive = (oldmode == LCK_EXCL) ? 1 : 0; | ||||
|  | ||||
| 	/* Always call lv_suspend to read commited and precommited data */ | ||||
| 	if (!lv_suspend_if_active(cmd, resource, origin_only, exclusive, NULL, NULL)) | ||||
| 		return EIO; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int do_deactivate_lv(char *resource, unsigned char command, unsigned char lock_flags) | ||||
| { | ||||
| 	int oldmode; | ||||
| 	int status; | ||||
|  | ||||
| 	/* Is it open ? */ | ||||
| 	oldmode = get_current_lock(resource); | ||||
| 	if (oldmode == -1 && (command & 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, NULL)) | ||||
| 		return EIO; | ||||
|  | ||||
| 	if (!test_mode() && command & 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; | ||||
|  | ||||
| 	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; | ||||
| 	default: type = NULL; | ||||
| 	} | ||||
|  | ||||
| 	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, critical_section = %d\n", | ||||
| 		 resource, decode_locking_cmd(command), decode_flags(lock_flags), critical_section()); | ||||
|  | ||||
| 	if (!cmd->initialized.config || 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); | ||||
| 	init_test((lock_flags & LCK_TEST_MODE) ? 1 : 0); | ||||
|  | ||||
| 	if (lock_flags & LCK_MIRROR_NOSYNC_MODE) | ||||
| 		init_mirror_in_sync(1); | ||||
|  | ||||
| 	if (lock_flags & LCK_DMEVENTD_MONITOR_IGNORE) | ||||
| 		init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE); | ||||
| 	else { | ||||
| 		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, command, lock_flags, LCK_EXCL); | ||||
| 		break; | ||||
|  | ||||
| 	case LCK_LV_SUSPEND: | ||||
| 		status = do_suspend_lv(resource, command, lock_flags); | ||||
| 		break; | ||||
|  | ||||
| 	case LCK_UNLOCK: | ||||
| 	case LCK_LV_RESUME:	/* if active */ | ||||
| 		status = do_resume_lv(resource, command, lock_flags); | ||||
| 		break; | ||||
|  | ||||
| 	case LCK_LV_ACTIVATE: | ||||
| 		status = do_activate_lv(resource, command, lock_flags, LCK_READ); | ||||
| 		break; | ||||
|  | ||||
| 	case LCK_LV_DEACTIVATE: | ||||
| 		status = do_deactivate_lv(resource, command, 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); | ||||
| 	init_test(0); | ||||
| 	pthread_mutex_unlock(&lvm_lock); | ||||
|  | ||||
| 	DEBUGLOG("Command return is %d, critical_section is %d\n", status, critical_section()); | ||||
| 	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 && | ||||
| 	    (command & LCK_CLUSTER_VG)) { | ||||
| 		DEBUGLOG("pre_lock_lv: resource '%s', cmd = %s, flags = %s\n", | ||||
| 			 resource, decode_locking_cmd(command), decode_flags(lock_flags)); | ||||
|  | ||||
| 		if (!(lock_flags & LCK_TEST_MODE) && | ||||
| 		    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 && | ||||
| 	    (command & 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 (!(lock_flags & LCK_TEST_MODE)) { | ||||
| 				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(void) | ||||
| { | ||||
| 	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_force_next_label_scan(); | ||||
| 	lvmcache_label_scan(cmd); | ||||
| 	dm_pool_empty(cmd->mem); | ||||
|  | ||||
| 	pthread_mutex_unlock(&lvm_lock); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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, critical_section = %d\n", | ||||
| 		 resource, decode_full_locking_cmd(lock_cmd), decode_flags(lock_flags), critical_section()); | ||||
|  | ||||
| 	/* P_#global causes a full cache refresh */ | ||||
| 	if (!strcmp(resource, "P_" VG_GLOBAL)) { | ||||
| 		do_refresh_cache(); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	pthread_mutex_lock(&lvm_lock); | ||||
| 	init_test((lock_flags & LCK_TEST_MODE) ? 1 : 0); | ||||
|  | ||||
| 	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); | ||||
| 	} | ||||
|  | ||||
| 	init_test(0); | ||||
| 	pthread_mutex_unlock(&lvm_lock); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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 int get_initial_state(struct dm_hash_table *excl_uuid) | ||||
| { | ||||
| 	int lock_mode; | ||||
| 	char lv[65], vg[65], flags[26], vg_flags[26]; /* with space for '\0' */ | ||||
| 	char uuid[65]; | ||||
| 	char line[255]; | ||||
| 	char *lvs_cmd; | ||||
| 	const char *lvm_binary = getenv("LVM_BINARY") ? : LVM_PATH; | ||||
| 	FILE *lvs; | ||||
|  | ||||
| 	if (dm_asprintf(&lvs_cmd, "%s lvs  --config 'log{command_names=0 prefix=\"\"}' " | ||||
| 			"--nolocking --noheadings -o vg_uuid,lv_uuid,lv_attr,vg_attr", | ||||
| 			lvm_binary) < 0) | ||||
| 		return_0; | ||||
|  | ||||
| 	/* FIXME: Maybe link and use liblvm2cmd directly instead of fork */ | ||||
| 	if (!(lvs = popen(lvs_cmd, "r"))) { | ||||
| 		dm_free(lvs_cmd); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	while (fgets(line, sizeof(line), lvs)) { | ||||
| 	        if (sscanf(line, "%64s %64s %25s %25s\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'; | ||||
|  | ||||
| 				/* Look for this lock in the list of EX locks | ||||
| 				   we were passed on the command-line */ | ||||
| 				lock_mode = (dm_hash_lookup(excl_uuid, uuid)) ? | ||||
| 					LCK_EXCL : LCK_READ; | ||||
|  | ||||
| 				DEBUGLOG("getting initial lock for %s\n", uuid); | ||||
| 				if (hold_lock(uuid, lock_mode, LCKF_NOQUEUE)) | ||||
| 					DEBUGLOG("Failed to hold lock %s\n", uuid); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (pclose(lvs)) | ||||
| 		DEBUGLOG("lvs pclose failed: %s\n", strerror(errno)); | ||||
|  | ||||
| 	dm_free(lvs_cmd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| 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_CFG, NULL); | ||||
|  | ||||
| 	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_CFG, NULL); | ||||
| 		if (libname && 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*/, WARN_PV_READ, &consistent); | ||||
|  | ||||
| 	if (vg && consistent) | ||||
| 		check_current_backup(vg); | ||||
| 	else | ||||
| 		log_error("Error backing up metadata, can't find VG for group %s", vgname); | ||||
|  | ||||
| 	release_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; | ||||
| } | ||||
|  | ||||
| void lvm_do_fs_unlock(void) | ||||
| { | ||||
| 	pthread_mutex_lock(&lvm_lock); | ||||
| 	DEBUGLOG("Syncing device names\n"); | ||||
| 	fs_unlock(); | ||||
| 	pthread_mutex_unlock(&lvm_lock); | ||||
| } | ||||
|  | ||||
| /* Called to initialise the LVM context of the daemon */ | ||||
| int init_clvm(struct dm_hash_table *excl_uuid) | ||||
| { | ||||
| 	/* Use LOG_DAEMON for syslog messages instead of LOG_USER */ | ||||
| 	init_syslog(LOG_DAEMON); | ||||
| 	openlog("clvmd", LOG_PID, LOG_DAEMON); | ||||
|  | ||||
| 	/* Initialise already held locks */ | ||||
| 	if (!get_initial_state(excl_uuid)) | ||||
| 		log_error("Cannot load initial lock states."); | ||||
|  | ||||
| 	if (!udev_init_library_context()) | ||||
| 		stack; | ||||
|  | ||||
| 	if (!(cmd = create_toolcontext(1, NULL, 0, 1, 1, 1))) { | ||||
| 		log_error("Failed to allocate command context"); | ||||
| 		udev_fin_library_context(); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (stored_errno()) { | ||||
| 		destroy_toolcontext(cmd); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	cmd->cmd_line = "clvmd"; | ||||
|  | ||||
| 	/* Check lvm.conf is setup for cluster-LVM */ | ||||
| 	check_config(); | ||||
| 	init_ignore_suspended_devices(1); | ||||
|  | ||||
| 	/* 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); | ||||
| 		udev_fin_library_context(); | ||||
| 		cmd = NULL; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										41
									
								
								daemons/clvmd/lvm-functions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								daemons/clvmd/lvm-functions.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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(struct dm_hash_table *excl_uuid); | ||||
| 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); | ||||
| void lvm_do_fs_unlock(void); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										382
									
								
								daemons/clvmd/refresh_clvmd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								daemons/clvmd/refresh_clvmd.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,382 @@ | ||||
| /* | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| /* FIXME Remove duplicated functions from this file. */ | ||||
|  | ||||
| /* | ||||
|  * Send a command to a running clvmd from the command-line | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
|  | ||||
| #include "clvm.h" | ||||
| #include "refresh_clvmd.h" | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/un.h> | ||||
|  | ||||
| typedef struct lvm_response { | ||||
| 	char node[255]; | ||||
| 	char *response; | ||||
| 	int status; | ||||
| 	int len; | ||||
| } lvm_response_t; | ||||
|  | ||||
| /* | ||||
|  * This gets stuck at the start of memory we allocate so we | ||||
|  * can sanity-check it at deallocation time | ||||
|  */ | ||||
| #define LVM_SIGNATURE 0x434C564D | ||||
|  | ||||
| static int _clvmd_sock = -1; | ||||
|  | ||||
| /* Open connection to the clvm daemon */ | ||||
| static int _open_local_sock(void) | ||||
| { | ||||
| 	int local_socket; | ||||
| 	struct sockaddr_un sockaddr = { .sun_family = AF_UNIX }; | ||||
|  | ||||
| 	if (!dm_strncpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(sockaddr.sun_path))) { | ||||
| 		fprintf(stderr, "%s: clvmd socket name too long.", CLVMD_SOCKNAME); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Open local socket */ | ||||
| 	if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { | ||||
| 		fprintf(stderr, "Local socket creation failed: %s", strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	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; | ||||
| 	unsigned 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, | ||||
| 			  unsigned int len) | ||||
| { | ||||
| 	head->cmd = cmd; | ||||
| 	head->status = 0; | ||||
| 	head->flags = 0; | ||||
| 	head->xid = 0; | ||||
| 	head->clientid = 0; | ||||
| 	if (len) | ||||
| 		/* 1 byte is used from struct clvm_header.args[1], so -> len - 1 */ | ||||
| 		head->arglen = len - 1; | ||||
| 	else { | ||||
| 		head->arglen = 0; | ||||
| 		*head->args = '\0'; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Translate special node names. | ||||
| 	 */ | ||||
| 	if (!node || !strcmp(node, NODE_ALL)) | ||||
| 		head->node[0] = '\0'; | ||||
| 	else if (!strcmp(node, NODE_LOCAL)) { | ||||
| 		head->node[0] = '\0'; | ||||
| 		head->flags = CLVMD_FLAG_LOCAL; | ||||
| 	} else | ||||
| 		strcpy(head->node, node); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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); | ||||
| 	if (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 = NULL; | ||||
| 	if (!(rarray = dm_malloc(sizeof(lvm_response_t) * num_responses + | ||||
| 				 sizeof(int) * 2))) { | ||||
| 		errno = ENOMEM; | ||||
| 		status = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* 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); | ||||
| 			dm_free(rarray); | ||||
| 			errno = ENOMEM; | ||||
| 			status = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		strcpy(rarray[i].response, inptr); | ||||
| 		rarray[i].len = strlen(inptr); | ||||
| 		inptr += strlen(inptr) + 1; | ||||
| 		i++; | ||||
| 	} | ||||
| 	*num = num_responses; | ||||
| 	*response = rarray; | ||||
|  | ||||
|       out: | ||||
| 	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 ? NODE_ALL : NODE_LOCAL, 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 ? NODE_ALL : NODE_LOCAL, 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 = NODE_ALL; | ||||
| 	else | ||||
| 		nodes = NODE_LOCAL; | ||||
|  | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										19
									
								
								daemons/clvmd/refresh_clvmd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								daemons/clvmd/refresh_clvmd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
|  | ||||
| int refresh_clvmd(int all_nodes); | ||||
| int restart_clvmd(int all_nodes); | ||||
| int debug_clvmd(int level, int clusterwide); | ||||
|  | ||||
| @@ -17,6 +17,8 @@ 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 | ||||
|  | ||||
| @@ -24,15 +26,14 @@ TARGETS = cmirrord | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LMLIBS += $(CPG_LIBS) | ||||
| CFLAGS += $(CPG_CFLAGS) $(EXTRA_EXEC_CFLAGS) | ||||
| LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) | ||||
| LIBS += -ldevmapper | ||||
| LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS) | ||||
| CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS) | ||||
| LDFLAGS += $(EXTRA_EXEC_LDFLAGS) | ||||
|  | ||||
| cmirrord: $(OBJECTS) | ||||
| 	@echo "    [CC] $@" | ||||
| 	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \ | ||||
| 		$(LMLIBS) -L$(top_builddir)/libdm -ldevmapper $(LIBS) | ||||
| cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \ | ||||
| 		$(LVMLIBS) $(LMLIBS) $(LIBS) | ||||
|  | ||||
| install: $(TARGETS) | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_PROGRAM) -D cmirrord $(usrsbindir)/cmirrord | ||||
| 	$(INSTALL_PROGRAM) -D cmirrord $(usrsbindir)/cmirrord | ||||
|   | ||||
| @@ -16,11 +16,7 @@ | ||||
| #include "functions.h" | ||||
| #include "link_mon.h" | ||||
| #include "local.h" | ||||
| #include "lib/mm/xlate.h" | ||||
| #include "base/memory/zalloc.h" | ||||
|  | ||||
| /* FIXME: remove this and the code */ | ||||
| #define CMIRROR_HAS_CHECKPOINT 0 | ||||
| #include "xlate.h" | ||||
|  | ||||
| #include <corosync/cpg.h> | ||||
| #include <errno.h> | ||||
| @@ -170,9 +166,6 @@ int cluster_send(struct clog_request *rq) | ||||
| { | ||||
| 	int r; | ||||
| 	int found = 0; | ||||
| #if CMIRROR_HAS_CHECKPOINT | ||||
| 	int count = 0; | ||||
| #endif | ||||
| 	struct iovec iov; | ||||
| 	struct clog_cpg *entry; | ||||
|  | ||||
| @@ -189,7 +182,7 @@ int cluster_send(struct clog_request *rq) | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Once the request heads for the cluster, the luid loses | ||||
| 	 * Once the request heads for the cluster, the luid looses | ||||
| 	 * all its meaning. | ||||
| 	 */ | ||||
| 	rq->u_rq.luid = 0; | ||||
| @@ -210,6 +203,8 @@ int cluster_send(struct clog_request *rq) | ||||
|  | ||||
| #if CMIRROR_HAS_CHECKPOINT | ||||
| 	do { | ||||
| 		int count = 0; | ||||
|  | ||||
| 		r = cpg_mcast_joined(entry->handle, CPG_TYPE_AGREED, &iov, 1); | ||||
| 		if (r != SA_AIS_ERR_TRY_AGAIN) | ||||
| 			break; | ||||
| @@ -403,12 +398,13 @@ static struct checkpoint_data *prepare_checkpoint(struct clog_cpg *entry, | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	new = zalloc(sizeof(*new)); | ||||
| 	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); | ||||
|  | ||||
| @@ -643,12 +639,13 @@ static int export_checkpoint(struct checkpoint_data *cp) | ||||
| 	rq_size += RECOVERING_REGION_SECTION_SIZE; | ||||
| 	rq_size += cp->bitmap_size * 2; /* clean|sync_bits */ | ||||
|  | ||||
| 	rq = zalloc(rq_size); | ||||
| 	rq = malloc(rq_size); | ||||
| 	if (!rq) { | ||||
| 		LOG_ERROR("export_checkpoint: " | ||||
| 			  "Unable to allocate transfer structs"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	memset(rq, 0, rq_size); | ||||
|  | ||||
| 	dm_list_init(&rq->u.list); | ||||
| 	rq->u_rq.request_type = DM_ULOG_CHECKPOINT_READY; | ||||
| @@ -1620,11 +1617,12 @@ int create_cluster_cpg(char *uuid, uint64_t luid) | ||||
| 			return -EEXIST; | ||||
| 		} | ||||
|  | ||||
| 	new = zalloc(sizeof(*new)); | ||||
| 	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); | ||||
| @@ -1632,7 +1630,7 @@ int create_cluster_cpg(char *uuid, uint64_t luid) | ||||
|  | ||||
| 	size = ((strlen(uuid) + 1) > CPG_MAX_NAME_LENGTH) ? | ||||
| 		CPG_MAX_NAME_LENGTH : (strlen(uuid) + 1); | ||||
| 	(void) dm_strncpy(new->name.value, uuid, size); | ||||
| 	strncpy(new->name.value, uuid, size); | ||||
| 	new->name.length = (uint32_t)size; | ||||
| 	new->luid = luid; | ||||
|  | ||||
|   | ||||
| @@ -12,8 +12,8 @@ | ||||
| #ifndef _LVM_CLOG_CLUSTER_H | ||||
| #define _LVM_CLOG_CLUSTER_H | ||||
|  | ||||
| #include "libdm/misc/dm-log-userspace.h" | ||||
| #include "libdm/libdevmapper.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 | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| #include "logging.h" | ||||
| #include "cluster.h" | ||||
| #include "compat.h" | ||||
| #include "lib/mm/xlate.h" | ||||
| #include "xlate.h" | ||||
|  | ||||
| #include <errno.h> | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "functions.h" | ||||
| #include "base/memory/zalloc.h" | ||||
|  | ||||
| #include <sys/sysmacros.h> | ||||
| #include <dirent.h> | ||||
| @@ -378,7 +377,7 @@ static int _clog_ctr(char *uuid, uint64_t luid, | ||||
| 	uint32_t block_on_error = 0; | ||||
|  | ||||
| 	int disk_log; | ||||
| 	char disk_path[PATH_MAX]; | ||||
| 	char disk_path[128]; | ||||
| 	int unlink_path = 0; | ||||
| 	long page_size; | ||||
| 	int pages; | ||||
| @@ -436,7 +435,7 @@ static int _clog_ctr(char *uuid, uint64_t luid, | ||||
| 			block_on_error = 1; | ||||
| 	} | ||||
|  | ||||
| 	lc = zalloc(sizeof(*lc)); | ||||
| 	lc = dm_zalloc(sizeof(*lc)); | ||||
| 	if (!lc) { | ||||
| 		LOG_ERROR("Unable to allocate cluster log context"); | ||||
| 		r = -ENOMEM; | ||||
| @@ -452,19 +451,15 @@ static int _clog_ctr(char *uuid, uint64_t luid, | ||||
| 	lc->skip_bit_warning = region_count; | ||||
| 	lc->disk_fd = -1; | ||||
| 	lc->log_dev_failed = 0; | ||||
| 	if (!dm_strncpy(lc->uuid, uuid, DM_UUID_LEN)) { | ||||
| 		LOG_ERROR("Cannot use too long UUID %s.", uuid); | ||||
| 		r = -EINVAL; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	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); | ||||
| 		r = -EINVAL; | ||||
| 		goto fail; | ||||
| 		dm_free(lc); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	dm_list_init(&lc->mark_list); | ||||
| @@ -533,9 +528,9 @@ fail: | ||||
| 			LOG_ERROR("Close device error, %s: %s", | ||||
| 				  disk_path, strerror(errno)); | ||||
| 		free(lc->disk_buffer); | ||||
| 		free(lc->sync_bits); | ||||
| 		free(lc->clean_bits); | ||||
| 		free(lc); | ||||
| 		dm_free(lc->sync_bits); | ||||
| 		dm_free(lc->clean_bits); | ||||
| 		dm_free(lc); | ||||
| 	} | ||||
| 	return r; | ||||
| } | ||||
| @@ -660,9 +655,9 @@ static int clog_dtr(struct dm_ulog_request *rq) | ||||
| 			  strerror(errno)); | ||||
| 	if (lc->disk_buffer) | ||||
| 		free(lc->disk_buffer); | ||||
| 	free(lc->clean_bits); | ||||
| 	free(lc->sync_bits); | ||||
| 	free(lc); | ||||
| 	dm_free(lc->clean_bits); | ||||
| 	dm_free(lc->sync_bits); | ||||
| 	dm_free(lc); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| #ifndef _LVM_CLOG_FUNCTIONS_H | ||||
| #define _LVM_CLOG_FUNCTIONS_H | ||||
|  | ||||
| #include "device_mapper/misc/dm-log-userspace.h" | ||||
| #include "dm-log-userspace.h" | ||||
| #include "cluster.h" | ||||
|  | ||||
| #define LOG_RESUMED   1 | ||||
|   | ||||
| @@ -13,6 +13,10 @@ | ||||
| #ifndef _LVM_CLOG_LOGGING_H | ||||
| #define _LVM_CLOG_LOGGING_H | ||||
|  | ||||
| #define _GNU_SOURCE | ||||
| #define _FILE_OFFSET_BITS 64 | ||||
|  | ||||
| #include "configure.h" | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <syslog.h> | ||||
|   | ||||
| @@ -56,18 +56,18 @@ include $(top_builddir)/make.tmpl | ||||
| all: device-mapper | ||||
| device-mapper: $(TARGETS) | ||||
|  | ||||
| LIBS += -ldevmapper | ||||
| LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS) | ||||
|  | ||||
| CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS) | ||||
| LIBS += $(PTHREAD_LIBS) -L$(top_builddir)/libdm -ldevmapper | ||||
|  | ||||
| dmeventd: $(LIB_SHARED) dmeventd.o | ||||
| 	@echo "    [CC] $@" | ||||
| 	$(Q) $(CC) $(CFLAGS) -L. $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) dmeventd.o \ | ||||
| 		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) -lm | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) -L. -o $@ dmeventd.o \ | ||||
| 	$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic | ||||
|  | ||||
| dmeventd.static: $(LIB_STATIC) dmeventd.o | ||||
| 	@echo "    [CC] $@" | ||||
| 	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) dmeventd.o \ | ||||
| 		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS) | ||||
| dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \ | ||||
| 	dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS) | ||||
|  | ||||
| ifeq ("@PKGCONFIG@", "yes") | ||||
|   INSTALL_LIB_TARGETS += install_pkgconfig | ||||
| @@ -75,6 +75,7 @@ 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 | ||||
| @@ -82,28 +83,23 @@ CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES)) | ||||
| endif | ||||
|  | ||||
| install_include: $(srcdir)/libdevmapper-event.h | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_DATA) -D $< $(includedir)/$(<F) | ||||
| 	$(INSTALL_DATA) -D $< $(includedir)/$(<F) | ||||
|  | ||||
| install_pkgconfig: libdevmapper-event.pc | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper-event.pc | ||||
| 	$(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper-event.pc | ||||
|  | ||||
| install_lib_dynamic: install_lib_shared | ||||
|  | ||||
| install_lib_static: $(LIB_STATIC) | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F) | ||||
| 	$(INSTALL_DATA) -D $< $(usrlibdir)/$(<F) | ||||
|  | ||||
| install_lib: $(INSTALL_LIB_TARGETS) | ||||
|  | ||||
| install_dmeventd_dynamic: dmeventd | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|  | ||||
| install_dmeventd_static: dmeventd.static | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F) | ||||
| 	$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F) | ||||
|  | ||||
| install_dmeventd: $(INSTALL_DMEVENTD_TARGETS) | ||||
|  | ||||
|   | ||||
| @@ -16,12 +16,12 @@ | ||||
|  * dmeventd - dm event daemon to monitor active mapped devices | ||||
|  */ | ||||
|  | ||||
| #include "dm-logging.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd.h" | ||||
|  | ||||
| #include "libdm/misc/dm-logging.h" | ||||
| #include "base/memory/zalloc.h" | ||||
| #include "tool.h" | ||||
|  | ||||
| #include <dlfcn.h> | ||||
| #include <pthread.h> | ||||
| @@ -33,8 +33,6 @@ | ||||
| #include <signal.h> | ||||
| #include <arpa/inet.h>		/* for htonl, ntohl */ | ||||
| #include <fcntl.h>		/* for musl libc */ | ||||
| #include <unistd.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| #ifdef __linux__ | ||||
| /* | ||||
| @@ -62,8 +60,8 @@ | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #define DM_SIGNALED_EXIT  1 | ||||
| #define DM_SCHEDULED_EXIT 2 | ||||
| #include <syslog.h> | ||||
|  | ||||
| static volatile sig_atomic_t _exit_now = 0;	/* set to '1' when signal is given to exit */ | ||||
|  | ||||
| /* List (un)link macros. */ | ||||
| @@ -264,19 +262,19 @@ static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER; | ||||
| /* DSO data allocate/free. */ | ||||
| static void _free_dso_data(struct dso_data *data) | ||||
| { | ||||
| 	free(data->dso_name); | ||||
| 	free(data); | ||||
| 	dm_free(data->dso_name); | ||||
| 	dm_free(data); | ||||
| } | ||||
|  | ||||
| static struct dso_data *_alloc_dso_data(struct message_data *data) | ||||
| { | ||||
| 	struct dso_data *ret = (typeof(ret)) zalloc(sizeof(*ret)); | ||||
| 	struct dso_data *ret = (typeof(ret)) dm_zalloc(sizeof(*ret)); | ||||
|  | ||||
| 	if (!ret) | ||||
| 		return_NULL; | ||||
|  | ||||
| 	if (!(ret->dso_name = strdup(data->dso_name))) { | ||||
| 		free(ret); | ||||
| 	if (!(ret->dso_name = dm_strdup(data->dso_name))) { | ||||
| 		dm_free(ret); | ||||
| 		return_NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -397,9 +395,9 @@ static void _free_thread_status(struct thread_status *thread) | ||||
| 	_lib_put(thread->dso_data); | ||||
| 	if (thread->wait_task) | ||||
| 		dm_task_destroy(thread->wait_task); | ||||
| 	free(thread->device.uuid); | ||||
| 	free(thread->device.name); | ||||
| 	free(thread); | ||||
| 	dm_free(thread->device.uuid); | ||||
| 	dm_free(thread->device.name); | ||||
| 	dm_free(thread); | ||||
| } | ||||
|  | ||||
| /* Note: events_field must not be 0, ensured by caller */ | ||||
| @@ -408,7 +406,7 @@ static struct thread_status *_alloc_thread_status(const struct message_data *dat | ||||
| { | ||||
| 	struct thread_status *thread; | ||||
|  | ||||
| 	if (!(thread = zalloc(sizeof(*thread)))) { | ||||
| 	if (!(thread = dm_zalloc(sizeof(*thread)))) { | ||||
| 		log_error("Cannot create new thread, out of memory."); | ||||
| 		return NULL; | ||||
| 	} | ||||
| @@ -422,11 +420,11 @@ static struct thread_status *_alloc_thread_status(const struct message_data *dat | ||||
| 	if (!dm_task_set_uuid(thread->wait_task, data->device_uuid)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	if (!(thread->device.uuid = strdup(data->device_uuid))) | ||||
| 	if (!(thread->device.uuid = dm_strdup(data->device_uuid))) | ||||
| 		goto_out; | ||||
|  | ||||
| 	/* Until real name resolved, use UUID */ | ||||
| 	if (!(thread->device.name = strdup(data->device_uuid))) | ||||
| 	if (!(thread->device.name = dm_strdup(data->device_uuid))) | ||||
| 		goto_out; | ||||
|  | ||||
| 	/* runs ioctl and may register lvm2 pluging */ | ||||
| @@ -470,7 +468,7 @@ static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void * | ||||
| 	/* | ||||
| 	 * We use a smaller stack since it gets preallocated in its entirety | ||||
| 	 */ | ||||
| 	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE + getpagesize()); | ||||
| 	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE); | ||||
|  | ||||
| 	/* | ||||
| 	 * If no-one will be waiting, we need to detach. | ||||
| @@ -515,7 +513,7 @@ static int _fetch_string(char **ptr, char **src, const int delimiter) | ||||
| 	if ((p = strchr(*src, delimiter))) { | ||||
| 		if (*src < p) { | ||||
| 			*p = 0; /* Temporary exit with \0 */ | ||||
| 			if (!(*ptr = strdup(*src))) { | ||||
| 			if (!(*ptr = dm_strdup(*src))) { | ||||
| 				log_error("Failed to fetch item %s.", *src); | ||||
| 				ret = 0; /* Allocation fail */ | ||||
| 			} | ||||
| @@ -525,7 +523,7 @@ static int _fetch_string(char **ptr, char **src, const int delimiter) | ||||
| 		(*src)++; /* Skip delmiter, next field */ | ||||
| 	} else if ((len = strlen(*src))) { | ||||
| 		/* No delimiter, item ends with '\0' */ | ||||
| 		if (!(*ptr = strdup(*src))) { | ||||
| 		if (!(*ptr = dm_strdup(*src))) { | ||||
| 			log_error("Failed to fetch last item %s.", *src); | ||||
| 			ret = 0; /* Fail */ | ||||
| 		} | ||||
| @@ -538,11 +536,11 @@ out: | ||||
| /* Free message memory. */ | ||||
| static void _free_message(struct message_data *message_data) | ||||
| { | ||||
| 	free(message_data->id); | ||||
| 	free(message_data->dso_name); | ||||
| 	free(message_data->device_uuid); | ||||
| 	free(message_data->events_str); | ||||
| 	free(message_data->timeout_str); | ||||
| 	dm_free(message_data->id); | ||||
| 	dm_free(message_data->dso_name); | ||||
| 	dm_free(message_data->device_uuid); | ||||
| 	dm_free(message_data->events_str); | ||||
| 	dm_free(message_data->timeout_str); | ||||
| } | ||||
|  | ||||
| /* Parse a register message from the client. */ | ||||
| @@ -574,7 +572,7 @@ static int _parse_message(struct message_data *message_data) | ||||
| 		ret = 1; | ||||
| 	} | ||||
|  | ||||
| 	free(msg->data); | ||||
| 	dm_free(msg->data); | ||||
| 	msg->data = NULL; | ||||
|  | ||||
| 	return ret; | ||||
| @@ -608,8 +606,8 @@ static int _fill_device_data(struct thread_status *ts) | ||||
| 	if (!dm_task_run(dmt)) | ||||
| 		goto fail; | ||||
|  | ||||
| 	free(ts->device.name); | ||||
| 	if (!(ts->device.name = strdup(dm_task_get_name(dmt)))) | ||||
| 	dm_free(ts->device.name); | ||||
| 	if (!(ts->device.name = dm_strdup(dm_task_get_name(dmt)))) | ||||
| 		goto fail; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &dmi)) | ||||
| @@ -696,8 +694,8 @@ static int _get_status(struct message_data *message_data) | ||||
|  | ||||
| 	len = strlen(message_data->id); | ||||
| 	msg->size = size + len + 1; | ||||
| 	free(msg->data); | ||||
| 	if (!(msg->data = malloc(msg->size))) | ||||
| 	dm_free(msg->data); | ||||
| 	if (!(msg->data = dm_malloc(msg->size))) | ||||
| 		goto out; | ||||
|  | ||||
| 	memcpy(msg->data, message_data->id, len); | ||||
| @@ -712,7 +710,7 @@ static int _get_status(struct message_data *message_data) | ||||
| 	ret = 0; | ||||
|  out: | ||||
| 	for (j = 0; j < i; ++j) | ||||
| 		free(buffers[j]); | ||||
| 		dm_free(buffers[j]); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
| @@ -721,7 +719,7 @@ static int _get_parameters(struct message_data *message_data) { | ||||
| 	struct dm_event_daemon_message *msg = message_data->msg; | ||||
| 	int size; | ||||
|  | ||||
| 	free(msg->data); | ||||
| 	dm_free(msg->data); | ||||
| 	if ((size = dm_asprintf(&msg->data, "%s pid=%d daemon=%s exec_method=%s", | ||||
| 				message_data->id, getpid(), | ||||
| 				_foreground ? "no" : "yes", | ||||
| @@ -754,7 +752,6 @@ static void *_timeout_thread(void *unused __attribute__((unused))) | ||||
| 	struct thread_status *thread; | ||||
| 	struct timespec timeout; | ||||
| 	time_t curr_time; | ||||
| 	int ret; | ||||
|  | ||||
| 	DEBUGLOG("Timeout thread starting."); | ||||
| 	pthread_cleanup_push(_exit_timeout, NULL); | ||||
| @@ -776,10 +773,7 @@ static void *_timeout_thread(void *unused __attribute__((unused))) | ||||
| 				} else { | ||||
| 					DEBUGLOG("Sending SIGALRM to Thr %x for timeout.", | ||||
| 						 (int) thread->thread); | ||||
| 					ret = pthread_kill(thread->thread, SIGALRM); | ||||
| 					if (ret && (ret != ESRCH)) | ||||
| 						log_error("Unable to wakeup Thr %x for timeout: %s.", | ||||
| 							  (int) thread->thread, strerror(ret)); | ||||
| 					pthread_kill(thread->thread, SIGALRM); | ||||
| 				} | ||||
| 				_unlock_mutex(); | ||||
| 			} | ||||
| @@ -869,7 +863,6 @@ static int _event_wait(struct thread_status *thread) | ||||
| 	 * This is so that you can break out of waiting on an event, | ||||
| 	 * either for a timeout event, or to cancel the thread. | ||||
| 	 */ | ||||
| 	sigemptyset(&old); | ||||
| 	sigemptyset(&set); | ||||
| 	sigaddset(&set, SIGALRM); | ||||
| 	if (pthread_sigmask(SIG_UNBLOCK, &set, &old) != 0) { | ||||
| @@ -1225,7 +1218,7 @@ static int _registered_device(struct message_data *message_data, | ||||
| 	int r; | ||||
| 	struct dm_event_daemon_message *msg = message_data->msg; | ||||
|  | ||||
| 	free(msg->data); | ||||
| 	dm_free(msg->data); | ||||
|  | ||||
| 	if ((r = dm_asprintf(&(msg->data), "%s %s %s %u", | ||||
| 			     message_data->id, | ||||
| @@ -1365,7 +1358,7 @@ static int _get_timeout(struct message_data *message_data) | ||||
| 	if (!thread) | ||||
| 		return -ENODEV; | ||||
|  | ||||
| 	free(msg->data); | ||||
| 	dm_free(msg->data); | ||||
| 	msg->size = dm_asprintf(&(msg->data), "%s %" PRIu32, | ||||
| 				message_data->id, thread->timeout); | ||||
|  | ||||
| @@ -1502,7 +1495,7 @@ static int _client_read(struct dm_event_fifos *fifos, | ||||
| 			bytes = 0; | ||||
| 			if (!size) | ||||
| 				break; /* No data -> error */ | ||||
| 			buf = msg->data = malloc(msg->size); | ||||
| 			buf = msg->data = dm_malloc(msg->size); | ||||
| 			if (!buf) | ||||
| 				break; /* No mem -> error */ | ||||
| 			header = 0; | ||||
| @@ -1510,7 +1503,7 @@ static int _client_read(struct dm_event_fifos *fifos, | ||||
| 	} | ||||
|  | ||||
| 	if (bytes != size) { | ||||
| 		free(msg->data); | ||||
| 		dm_free(msg->data); | ||||
| 		msg->data = NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -1530,7 +1523,7 @@ static int _client_write(struct dm_event_fifos *fifos, | ||||
| 	fd_set fds; | ||||
|  | ||||
| 	size_t size = 2 * sizeof(uint32_t) + ((msg->data) ? msg->size : 0); | ||||
| 	uint32_t *header = malloc(size); | ||||
| 	uint32_t *header = dm_malloc(size); | ||||
| 	char *buf = (char *)header; | ||||
|  | ||||
| 	if (!header) { | ||||
| @@ -1560,7 +1553,7 @@ static int _client_write(struct dm_event_fifos *fifos, | ||||
| 	} | ||||
|  | ||||
| 	if (header != temp) | ||||
| 		free(header); | ||||
| 		dm_free(header); | ||||
|  | ||||
| 	return (bytes == size); | ||||
| } | ||||
| @@ -1622,7 +1615,7 @@ static int _do_process_request(struct dm_event_daemon_message *msg) | ||||
| 			msg->size = dm_asprintf(&(msg->data), "%s %s %d", answer, | ||||
| 						(msg->cmd == DM_EVENT_CMD_DIE) ? "DYING" : "HELLO", | ||||
| 						DM_EVENT_PROTOCOL_VERSION); | ||||
| 			free(answer); | ||||
| 			dm_free(answer); | ||||
| 		} | ||||
| 	} else if (msg->cmd != DM_EVENT_CMD_ACTIVE && !_parse_message(&message_data)) { | ||||
| 		stack; | ||||
| @@ -1664,7 +1657,7 @@ static void _process_request(struct dm_event_fifos *fifos) | ||||
|  | ||||
| 	DEBUGLOG("<<< CMD:%s (0x%x) completed (result %d).", decode_cmd(cmd), cmd, msg.cmd); | ||||
|  | ||||
| 	free(msg.data); | ||||
| 	dm_free(msg.data); | ||||
|  | ||||
| 	if (cmd == DM_EVENT_CMD_DIE) { | ||||
| 		if (unlink(DMEVENTD_PIDFILE)) | ||||
| @@ -1757,7 +1750,7 @@ static void _init_thread_signals(void) | ||||
|  */ | ||||
| static void _exit_handler(int sig __attribute__((unused))) | ||||
| { | ||||
| 	_exit_now = DM_SIGNALED_EXIT; | ||||
| 	_exit_now = 1; | ||||
| } | ||||
|  | ||||
| #ifdef __linux__ | ||||
| @@ -1975,7 +1968,7 @@ static int _reinstate_registrations(struct dm_event_fifos *fifos) | ||||
| 	int i, ret; | ||||
|  | ||||
| 	ret = daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0); | ||||
| 	free(msg.data); | ||||
| 	dm_free(msg.data); | ||||
| 	msg.data = NULL; | ||||
|  | ||||
| 	if (ret) { | ||||
| @@ -2061,13 +2054,13 @@ static void _restart_dmeventd(void) | ||||
| 			++count; | ||||
| 		} | ||||
|  | ||||
| 	if (!(_initial_registrations = malloc(sizeof(char*) * (count + 1)))) { | ||||
| 	if (!(_initial_registrations = dm_malloc(sizeof(char*) * (count + 1)))) { | ||||
| 		fprintf(stderr, "Memory allocation registration failed.\n"); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < count; ++i) { | ||||
| 		if (!(_initial_registrations[i] = strdup(message))) { | ||||
| 		if (!(_initial_registrations[i] = dm_strdup(message))) { | ||||
| 			fprintf(stderr, "Memory allocation for message failed.\n"); | ||||
| 			goto bad; | ||||
| 		} | ||||
| @@ -2255,14 +2248,11 @@ int main(int argc, char *argv[]) | ||||
| 	for (;;) { | ||||
| 		if (_idle_since) { | ||||
| 			if (_exit_now) { | ||||
| 				if (_exit_now == DM_SCHEDULED_EXIT) | ||||
| 					break; /* Only prints shutdown message */ | ||||
| 				log_info("dmeventd detected break while being idle " | ||||
| 					 "for %ld second(s), exiting.", | ||||
| 					 (long) (time(NULL) - _idle_since)); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (idle_exit_timeout) { | ||||
| 			} else if (idle_exit_timeout) { | ||||
| 				now = time(NULL); | ||||
| 				if (now < _idle_since) | ||||
| 					_idle_since = now; /* clock change? */ | ||||
| @@ -2273,14 +2263,15 @@ int main(int argc, char *argv[]) | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} else if (_exit_now == DM_SIGNALED_EXIT) { | ||||
| 			_exit_now = DM_SCHEDULED_EXIT; | ||||
| 		} else if (_exit_now) { | ||||
| 			_exit_now = 0; | ||||
| 			/* | ||||
| 			 * When '_exit_now' is set, signal has been received, | ||||
| 			 * but can not simply exit unless all | ||||
| 			 * threads are done processing. | ||||
| 			 */ | ||||
| 			log_info("dmeventd received break, scheduling exit."); | ||||
| 			log_warn("WARNING: There are still devices being monitored."); | ||||
| 			log_warn("WARNING: Refusing to exit."); | ||||
| 		} | ||||
| 		_process_request(&fifos); | ||||
| 		_cleanup_unused_threads(); | ||||
|   | ||||
| @@ -12,12 +12,10 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "dm-logging.h" | ||||
| #include "dmlib.h" | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd.h" | ||||
| #include "libdm/misc/dm-logging.h" | ||||
| #include "base/memory/zalloc.h" | ||||
|  | ||||
| #include "lib/misc/intl.h" | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <sys/file.h> | ||||
| @@ -27,7 +25,6 @@ | ||||
| #include <arpa/inet.h>		/* for htonl, ntohl */ | ||||
| #include <pthread.h> | ||||
| #include <syslog.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| static int _debug_level = 0; | ||||
| static int _use_syslog = 0; | ||||
| @@ -50,8 +47,8 @@ struct dm_event_handler { | ||||
|  | ||||
| static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh) | ||||
| { | ||||
| 	free(dmevh->dev_name); | ||||
| 	free(dmevh->uuid); | ||||
| 	dm_free(dmevh->dev_name); | ||||
| 	dm_free(dmevh->uuid); | ||||
| 	dmevh->dev_name = dmevh->uuid = NULL; | ||||
| 	dmevh->major = dmevh->minor = 0; | ||||
| } | ||||
| @@ -60,7 +57,7 @@ struct dm_event_handler *dm_event_handler_create(void) | ||||
| { | ||||
| 	struct dm_event_handler *dmevh; | ||||
|  | ||||
| 	if (!(dmevh = zalloc(sizeof(*dmevh)))) { | ||||
| 	if (!(dmevh = dm_zalloc(sizeof(*dmevh)))) { | ||||
| 		log_error("Failed to allocate event handler."); | ||||
| 		return NULL; | ||||
| 	} | ||||
| @@ -71,9 +68,9 @@ struct dm_event_handler *dm_event_handler_create(void) | ||||
| void dm_event_handler_destroy(struct dm_event_handler *dmevh) | ||||
| { | ||||
| 	_dm_event_handler_clear_dev_info(dmevh); | ||||
| 	free(dmevh->dso); | ||||
| 	free(dmevh->dmeventd_path); | ||||
| 	free(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) | ||||
| @@ -81,9 +78,9 @@ int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const cha | ||||
| 	if (!dmeventd_path) /* noop */ | ||||
| 		return 0; | ||||
|  | ||||
| 	free(dmevh->dmeventd_path); | ||||
| 	dm_free(dmevh->dmeventd_path); | ||||
|  | ||||
| 	if (!(dmevh->dmeventd_path = strdup(dmeventd_path))) | ||||
| 	if (!(dmevh->dmeventd_path = dm_strdup(dmeventd_path))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	return 0; | ||||
| @@ -94,9 +91,9 @@ int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path) | ||||
| 	if (!path) /* noop */ | ||||
| 		return 0; | ||||
|  | ||||
| 	free(dmevh->dso); | ||||
| 	dm_free(dmevh->dso); | ||||
|  | ||||
| 	if (!(dmevh->dso = strdup(path))) | ||||
| 	if (!(dmevh->dso = dm_strdup(path))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	return 0; | ||||
| @@ -109,7 +106,7 @@ int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *de | ||||
|  | ||||
| 	_dm_event_handler_clear_dev_info(dmevh); | ||||
|  | ||||
| 	if (!(dmevh->dev_name = strdup(dev_name))) | ||||
| 	if (!(dmevh->dev_name = dm_strdup(dev_name))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	return 0; | ||||
| @@ -122,7 +119,7 @@ int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid) | ||||
|  | ||||
| 	_dm_event_handler_clear_dev_info(dmevh); | ||||
|  | ||||
| 	if (!(dmevh->uuid = strdup(uuid))) | ||||
| 	if (!(dmevh->uuid = dm_strdup(uuid))) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	return 0; | ||||
| @@ -253,16 +250,17 @@ static int _daemon_read(struct dm_event_fifos *fifos, | ||||
| 		if (ret < 0) { | ||||
| 			if ((errno == EINTR) || (errno == EAGAIN)) | ||||
| 				continue; | ||||
|  | ||||
| 			log_error("Unable to read from event server."); | ||||
| 			return 0; | ||||
| 			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 = malloc(msg->size); | ||||
| 			buf = msg->data = dm_malloc(msg->size); | ||||
| 			size = msg->size; | ||||
| 			bytes = 0; | ||||
| 			header = 0; | ||||
| @@ -270,7 +268,7 @@ static int _daemon_read(struct dm_event_fifos *fifos, | ||||
| 	} | ||||
|  | ||||
| 	if (bytes != size) { | ||||
| 		free(msg->data); | ||||
| 		dm_free(msg->data); | ||||
| 		msg->data = NULL; | ||||
| 	} | ||||
| 	return bytes == size; | ||||
| @@ -331,9 +329,10 @@ static int _daemon_write(struct dm_event_fifos *fifos, | ||||
| 		if (ret < 0) { | ||||
| 			if ((errno == EINTR) || (errno == EAGAIN)) | ||||
| 				continue; | ||||
|  | ||||
| 			log_error("Unable to talk to event daemon."); | ||||
| 			return 0; | ||||
| 			else { | ||||
| 				log_error("Unable to talk to event daemon."); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		bytes += ret; | ||||
| @@ -373,13 +372,13 @@ int daemon_talk(struct dm_event_fifos *fifos, | ||||
| 	 */ | ||||
| 	if (!_daemon_write(fifos, msg)) { | ||||
| 		stack; | ||||
| 		free(msg->data); | ||||
| 		dm_free(msg->data); | ||||
| 		msg->data = NULL; | ||||
| 		return -EIO; | ||||
| 	} | ||||
|  | ||||
| 	do { | ||||
| 		free(msg->data); | ||||
| 		dm_free(msg->data); | ||||
| 		msg->data = NULL; | ||||
|  | ||||
| 		if (!_daemon_read(fifos, msg)) { | ||||
| @@ -455,8 +454,7 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos) | ||||
| 		if (close(fifos->client)) | ||||
| 			log_sys_debug("close", fifos->client_path); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	if (errno != ENXIO && errno != ENOENT)  { | ||||
| 	} else if (errno != ENXIO && errno != ENOENT)  { | ||||
| 		/* problem */ | ||||
| 		log_sys_error("open", fifos->client_path); | ||||
| 		return 0; | ||||
| @@ -622,7 +620,7 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag | ||||
|  | ||||
| 	ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0); | ||||
|  | ||||
| 	free(msg->data); | ||||
| 	dm_free(msg->data); | ||||
| 	msg->data = 0; | ||||
|  | ||||
| 	if (!ret) | ||||
| @@ -648,7 +646,6 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh) | ||||
| 	uuid = dm_task_get_uuid(dmt); | ||||
|  | ||||
| 	if (!strstr(dmevh->dso, "libdevmapper-event-lvm2thin.so") && | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2vdo.so") && | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2snapshot.so") && | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2mirror.so") && | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2raid.so")) | ||||
| @@ -663,7 +660,7 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh) | ||||
| 		ret = 0; | ||||
| 	} | ||||
|  | ||||
| 	free(msg.data); | ||||
| 	dm_free(msg.data); | ||||
|  | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| @@ -690,7 +687,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh) | ||||
| 		ret = 0; | ||||
| 	} | ||||
|  | ||||
| 	free(msg.data); | ||||
| 	dm_free(msg.data); | ||||
|  | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| @@ -706,7 +703,7 @@ static char *_fetch_string(char **src, const int delimiter) | ||||
| 	if ((p = strchr(*src, delimiter))) | ||||
| 		*p = 0; | ||||
|  | ||||
| 	if ((ret = strdup(*src))) | ||||
| 	if ((ret = dm_strdup(*src))) | ||||
| 		*src += strlen(ret) + 1; | ||||
|  | ||||
| 	if (p) | ||||
| @@ -726,11 +723,11 @@ static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name, | ||||
| 	    (*dso_name = _fetch_string(&p, ' ')) && | ||||
| 	    (*uuid = _fetch_string(&p, ' '))) { | ||||
| 		*evmask = atoi(p); | ||||
| 		free(id); | ||||
| 		dm_free(id); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	free(id); | ||||
| 	dm_free(id); | ||||
| 	return -ENOMEM; | ||||
| } | ||||
|  | ||||
| @@ -758,10 +755,11 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) | ||||
| 	uuid = dm_task_get_uuid(dmt); | ||||
|  | ||||
| 	/* FIXME Distinguish errors connecting to daemon */ | ||||
| 	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))) { | ||||
| 	if (_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)) { | ||||
| 		log_debug("%s: device not registered.", dm_task_get_name(dmt)); | ||||
| 		ret = -ENOENT; | ||||
| 		goto fail; | ||||
| 	} | ||||
|  | ||||
| @@ -772,7 +770,7 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) | ||||
| 	dm_task_destroy(dmt); | ||||
| 	dmt = NULL; | ||||
|  | ||||
| 	free(msg.data); | ||||
| 	dm_free(msg.data); | ||||
| 	msg.data = NULL; | ||||
|  | ||||
| 	_dm_event_handler_clear_dev_info(dmevh); | ||||
| @@ -781,7 +779,7 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) | ||||
| 		goto fail; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dmevh->uuid = strdup(reply_uuid))) { | ||||
| 	if (!(dmevh->uuid = dm_strdup(reply_uuid))) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto fail; | ||||
| 	} | ||||
| @@ -794,13 +792,13 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) | ||||
| 	dm_event_handler_set_dso(dmevh, reply_dso); | ||||
| 	dm_event_handler_set_event_mask(dmevh, reply_mask); | ||||
|  | ||||
| 	free(reply_dso); | ||||
| 	dm_free(reply_dso); | ||||
| 	reply_dso = NULL; | ||||
|  | ||||
| 	free(reply_uuid); | ||||
| 	dm_free(reply_uuid); | ||||
| 	reply_uuid = NULL; | ||||
|  | ||||
| 	if (!(dmevh->dev_name = strdup(dm_task_get_name(dmt)))) { | ||||
| 	if (!(dmevh->dev_name = dm_strdup(dm_task_get_name(dmt)))) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto fail; | ||||
| 	} | ||||
| @@ -818,9 +816,9 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) | ||||
| 	return ret; | ||||
|  | ||||
|  fail: | ||||
| 	free(msg.data); | ||||
| 	free(reply_dso); | ||||
| 	free(reply_uuid); | ||||
| 	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); | ||||
| @@ -985,12 +983,12 @@ int dm_event_get_timeout(const char *device_path, uint32_t *timeout) | ||||
| 		if (!p) { | ||||
| 			log_error("Malformed reply from dmeventd '%s'.", | ||||
| 				  msg.data); | ||||
| 			free(msg.data); | ||||
| 			dm_free(msg.data); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 		*timeout = atoi(p); | ||||
| 	} | ||||
| 	free(msg.data); | ||||
| 	dm_free(msg.data); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
| #ifndef LIB_DMEVENT_H | ||||
| #define LIB_DMEVENT_H | ||||
|  | ||||
| #include <stdarg.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -8,3 +8,4 @@ Description: device-mapper event library | ||||
| Version: @DM_LIB_PATCHLEVEL@ | ||||
| Cflags: -I${includedir} | ||||
| Libs: -L${libdir} -ldevmapper-event | ||||
| Requires.private: devmapper | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # | ||||
| # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2005, 2011 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| @@ -16,7 +16,27 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| SUBDIRS += lvm2 snapshot raid thin mirror vdo | ||||
| SUBDIRS += lvm2 | ||||
|  | ||||
| ifneq ("@MIRRORS@", "none") | ||||
|   SUBDIRS += mirror | ||||
| endif | ||||
|  | ||||
| ifneq ("@SNAPSHOTS@", "none") | ||||
|   SUBDIRS += snapshot | ||||
| endif | ||||
|  | ||||
| ifneq ("@RAID@", "none") | ||||
|   SUBDIRS += raid | ||||
| endif | ||||
|  | ||||
| ifneq ("@THIN@", "none") | ||||
|   SUBDIRS += thin | ||||
| endif | ||||
|  | ||||
| ifeq ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = lvm2 mirror snapshot raid thin | ||||
| endif | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| @@ -24,4 +44,3 @@ snapshot: lvm2 | ||||
| mirror: lvm2 | ||||
| raid: lvm2 | ||||
| thin: lvm2 | ||||
| vdo: lvm2 | ||||
|   | ||||
| @@ -16,7 +16,6 @@ top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| CLDFLAGS += -L$(top_builddir)/tools | ||||
| LIBS += $(DMEVENT_LIBS) $(PTHREAD_LIBS) @LVM2CMD_LIB@ | ||||
|  | ||||
| SOURCES = dmeventd_lvm.c | ||||
|  | ||||
| @@ -25,6 +24,8 @@ LIB_VERSION = $(LIB_VERSION_LVM) | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS) | ||||
|  | ||||
| install_lvm2: install_lib_shared | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -12,10 +12,10 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "lib/misc/lib.h" | ||||
| #include "lib.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "daemons/dmeventd/libdevmapper-event.h" | ||||
| #include "tools/lvm2cmd.h" | ||||
| #include "libdevmapper-event.h" | ||||
| #include "lvm2cmd.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| @@ -31,13 +31,6 @@ static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
| static int _register_count = 0; | ||||
| static struct dm_pool *_mem_pool = NULL; | ||||
| static void *_lvm_handle = NULL; | ||||
| static DM_LIST_INIT(_env_registry); | ||||
|  | ||||
| struct env_data { | ||||
| 	struct dm_list list; | ||||
| 	const char *cmd; | ||||
| 	const char *data; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("#lvm") | ||||
|  | ||||
| @@ -107,7 +100,6 @@ void dmeventd_lvm2_exit(void) | ||||
| 		lvm2_run(_lvm_handle, "_memlock_dec"); | ||||
| 		dm_pool_destroy(_mem_pool); | ||||
| 		_mem_pool = NULL; | ||||
| 		dm_list_init(&_env_registry); | ||||
| 		lvm2_exit(_lvm_handle); | ||||
| 		_lvm_handle = NULL; | ||||
| 		log_debug("lvm plugin exited."); | ||||
| @@ -132,8 +124,6 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, | ||||
| 	static char _internal_prefix[] =  "_dmeventd_"; | ||||
| 	char *vg = NULL, *lv = NULL, *layer; | ||||
| 	int r; | ||||
| 	struct env_data *env_data; | ||||
| 	const char *env = NULL; | ||||
|  | ||||
| 	if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) { | ||||
| 		log_error("Unable to determine VG name from %s.", | ||||
| @@ -147,36 +137,18 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, | ||||
| 		*layer = '\0'; | ||||
|  | ||||
| 	if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) { | ||||
| 		/* check if ENVVAR wasn't already resolved */ | ||||
| 		dm_list_iterate_items(env_data, &_env_registry) | ||||
| 			if (!strcmp(cmd, env_data->cmd)) { | ||||
| 				env = env_data->data; | ||||
| 				break; | ||||
| 			} | ||||
| 		dmeventd_lvm2_lock(); | ||||
| 		/* output of internal command passed via env var */ | ||||
| 		if (!dmeventd_lvm2_run(cmd)) | ||||
| 			cmd = NULL; | ||||
| 		else if ((cmd = getenv(cmd))) | ||||
| 			cmd = dm_pool_strdup(mem, cmd); /* copy with lock */ | ||||
| 		dmeventd_lvm2_unlock(); | ||||
|  | ||||
| 		if (!env) { | ||||
| 			/* run lvm2 command to find out setting value */ | ||||
| 			dmeventd_lvm2_lock(); | ||||
| 			if (!dmeventd_lvm2_run(cmd) || | ||||
| 			    !(env = getenv(cmd))) { | ||||
| 				dmeventd_lvm2_unlock(); | ||||
| 				log_error("Unable to find configured command."); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			/* output of internal command passed via env var */ | ||||
| 			env = dm_pool_strdup(_mem_pool, env); /* copy with lock */ | ||||
| 			dmeventd_lvm2_unlock(); | ||||
| 			if (!env || | ||||
| 			    !(env_data = dm_pool_zalloc(_mem_pool, sizeof(*env_data))) || | ||||
| 			    !(env_data->cmd = dm_pool_strdup(_mem_pool, cmd))) { | ||||
| 				log_error("Unable to allocate env memory."); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			env_data->data = env; | ||||
| 			/* add to ENVVAR registry */ | ||||
| 			dm_list_add(&_env_registry, &env_data->list); | ||||
| 		if (!cmd) { | ||||
| 			log_error("Unable to find configured command."); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		cmd = env; | ||||
| 	} | ||||
|  | ||||
| 	r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv); | ||||
|   | ||||
| @@ -16,8 +16,8 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2 | ||||
| CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2 | ||||
| LIBS += -ldevmapper-event-lvm2 | ||||
|  | ||||
| SOURCES = dmeventd_mirror.c | ||||
|  | ||||
| @@ -30,6 +30,8 @@ CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -12,10 +12,10 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "lib/misc/lib.h" | ||||
| #include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h" | ||||
| #include "daemons/dmeventd/libdevmapper-event.h" | ||||
| #include "lib/activate/activate.h" | ||||
| #include "lib.h" | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "activate.h"	/* For TARGET_NAME* */ | ||||
|  | ||||
| /* FIXME Reformat to 80 char lines. */ | ||||
|  | ||||
| @@ -25,6 +25,7 @@ | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	char cmd_lvscan[512]; | ||||
| 	char cmd_lvconvert[512]; | ||||
| }; | ||||
|  | ||||
| @@ -98,8 +99,12 @@ static int _get_mirror_event(struct dso_state *state, char *params) | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _remove_failed_devices(const char *cmd_lvconvert, const char *device) | ||||
| static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert, | ||||
| 				  const char *device) | ||||
| { | ||||
| 	if (!dmeventd_lvm2_run_with_lock(cmd_lvscan)) | ||||
| 		log_warn("WARNING: Re-scan of mirrored device %s failed.", device); | ||||
|  | ||||
| 	/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 	if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) { | ||||
| 		log_error("Repair of mirrored device %s failed.", device); | ||||
| @@ -146,7 +151,9 @@ void process_event(struct dm_task *dmt, | ||||
| 			break; | ||||
| 		case ME_FAILURE: | ||||
| 			log_error("Device failure in %s.", device); | ||||
| 			if (!_remove_failed_devices(state->cmd_lvconvert, device)) | ||||
| 			if (!_remove_failed_devices(state->cmd_lvscan, | ||||
| 						    state->cmd_lvconvert, | ||||
| 						    device)) | ||||
| 				/* FIXME Why are all the error return codes unused? Get rid of them? */ | ||||
| 				log_error("Failed to remove faulty devices in %s.", | ||||
| 					  device); | ||||
| @@ -176,10 +183,17 @@ int register_device(const char *device, | ||||
| 	if (!dmeventd_lvm2_init_with_pool("mirror_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
|         /* CANNOT use --config as this disables cached content */ | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --repair --use-policies", device)) | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), | ||||
| 				   "lvscan --cache", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --repair --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	*user = state; | ||||
|  | ||||
| @@ -189,9 +203,6 @@ int register_device(const char *device, | ||||
| bad: | ||||
| 	log_error("Failed to monitor mirror %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -15,8 +15,8 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2 | ||||
| CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2 | ||||
| LIBS += -ldevmapper-event-lvm2 | ||||
|  | ||||
| SOURCES = dmeventd_raid.c | ||||
|  | ||||
| @@ -29,6 +29,8 @@ CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2016 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -12,16 +12,17 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "lib/misc/lib.h" | ||||
| #include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h" | ||||
| #include "daemons/dmeventd/libdevmapper-event.h" | ||||
| #include "lib/config/defaults.h" | ||||
| #include "lib.h" | ||||
| #include "defaults.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "libdevmapper-event.h" | ||||
|  | ||||
| /* Hold enough elements for the mximum number of RAID images */ | ||||
| #define	RAID_DEVS_ELEMS	((DEFAULT_RAID_MAX_IMAGES + 63) / 64) | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	char cmd_lvscan[512]; | ||||
| 	char cmd_lvconvert[512]; | ||||
| 	uint64_t raid_devs[RAID_DEVS_ELEMS]; | ||||
| 	int failed; | ||||
| @@ -58,34 +59,23 @@ static int _process_raid_event(struct dso_state *state, char *params, const char | ||||
| 		dead = 1; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * if we are converting from non-RAID to RAID (e.g. linear -> raid1) | ||||
| 	 * and too many original devices die, such that we cannot continue | ||||
| 	 * the "recover" operation, the sync action will go to "idle", the | ||||
| 	 * unsynced devs will remain at 'a', and the original devices will | ||||
| 	 * NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived. | ||||
| 	 * | ||||
| 	 * This is simply the way the kernel works... | ||||
| 	 */ | ||||
| 	if (!strcmp(status->sync_action, "idle") && | ||||
| 	    (status->dev_health[0] == 'a') && | ||||
| 	    (status->insync_regions < status->total_regions)) { | ||||
| 		log_error("Primary sources for new RAID, %s, have failed.", | ||||
| 			  device); | ||||
| 		dead = 1; /* run it through LVM repair */ | ||||
| 	} | ||||
|  | ||||
| 	if (dead) { | ||||
| 		/* | ||||
| 		 * Use the first event to run a repair ignoring any additonal ones. | ||||
| 		 * | ||||
| 		 * We presume lvconvert to do pre-repair | ||||
| 		 * checks to avoid bloat in this plugin. | ||||
| 		 */ | ||||
| 		if (status->insync_regions < status->total_regions) { | ||||
| 			if (!state->warned) { | ||||
| 				state->warned = 1; | ||||
| 				log_warn("WARNING: waiting for resynchronization to finish " | ||||
| 					 "before initiating repair on RAID device %s.", device); | ||||
| 			} | ||||
|  | ||||
| 			goto out; /* Not yet done syncing with accessible devices */ | ||||
| 		} | ||||
|  | ||||
| 		if (state->failed) | ||||
| 			goto out; /* already reported */ | ||||
|  | ||||
| 		state->failed = 1; | ||||
| 		if (!dmeventd_lvm2_run_with_lock(state->cmd_lvscan)) | ||||
| 			log_warn("WARNING: Re-scan of RAID device %s failed.", device); | ||||
|  | ||||
| 		/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 		if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) { | ||||
| @@ -94,8 +84,6 @@ static int _process_raid_event(struct dso_state *state, char *params, const char | ||||
| 		} | ||||
| 	} else { | ||||
| 		state->failed = 0; | ||||
| 		if (status->insync_regions == status->total_regions) | ||||
| 			memset(&state->raid_devs, 0, sizeof(state->raid_devs)); | ||||
| 		log_info("%s array, %s, is %s in-sync.", | ||||
| 			 status->raid_type, device, | ||||
| 			 (status->insync_regions == status->total_regions) ? "now" : "not"); | ||||
| @@ -148,9 +136,14 @@ int register_device(const char *device, | ||||
| 	if (!dmeventd_lvm2_init_with_pool("raid_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --repair --use-policies", device)) | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), | ||||
| 				   "lvscan --cache", device) || | ||||
| 	    !dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --config devices{ignore_suspended_devices=1} " | ||||
| 				   "--repair --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	*user = state; | ||||
|  | ||||
| @@ -160,9 +153,6 @@ int register_device(const char *device, | ||||
| bad: | ||||
| 	log_error("Failed to monitor RAID %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,8 +16,8 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2 | ||||
| CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2 | ||||
| LIBS += -ldevmapper-event-lvm2 | ||||
|  | ||||
| SOURCES = dmeventd_snapshot.c | ||||
|  | ||||
| @@ -26,6 +26,8 @@ LIB_VERSION = $(LIB_VERSION_LVM) | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -12,9 +12,9 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "lib/misc/lib.h" | ||||
| #include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h" | ||||
| #include "daemons/dmeventd/libdevmapper-event.h" | ||||
| #include "lib.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "libdevmapper-event.h" | ||||
|  | ||||
| #include <sys/sysmacros.h> | ||||
| #include <sys/wait.h> | ||||
| @@ -175,7 +175,6 @@ void process_event(struct dm_task *dmt, | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
| 	int percent; | ||||
| 	struct dm_info info; | ||||
| 	int ret; | ||||
|  | ||||
| 	/* No longer monitoring, waiting for remove */ | ||||
| 	if (!state->percent_check) | ||||
| @@ -206,8 +205,7 @@ void process_event(struct dm_task *dmt, | ||||
| 		/* Maybe configurable ? */ | ||||
| 		_remove(dm_task_get_uuid(dmt)); | ||||
| #endif | ||||
| 		if ((ret = pthread_kill(pthread_self(), SIGALRM)) && (ret != ESRCH)) | ||||
| 			log_sys_error("pthread_kill", "self"); | ||||
| 		pthread_kill(pthread_self(), SIGALRM); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| @@ -215,8 +213,7 @@ void process_event(struct dm_task *dmt, | ||||
| 		/* TODO eventually recognize earlier when room is enough */ | ||||
| 		log_info("Dropping monitoring of fully provisioned snapshot %s.", | ||||
| 			 device); | ||||
| 		if ((ret = pthread_kill(pthread_self(), SIGALRM)) && (ret != ESRCH)) | ||||
| 			log_sys_error("pthread_kill", "self"); | ||||
| 		pthread_kill(pthread_self(), SIGALRM); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| @@ -234,7 +231,7 @@ void process_event(struct dm_task *dmt, | ||||
|  | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			log_warn("WARNING: Snapshot %s is now %.2f%% full.", | ||||
| 				 device, dm_percent_to_round_float(percent, 2)); | ||||
| 				 device, dm_percent_to_float(percent)); | ||||
|  | ||||
| 		/* Try to extend the snapshot, in accord with user-set policies */ | ||||
| 		if (!_extend(state->cmd_lvextend)) | ||||
| @@ -257,8 +254,10 @@ int register_device(const char *device, | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend, | ||||
| 				   sizeof(state->cmd_lvextend), | ||||
| 				   "lvextend --use-policies", device)) | ||||
| 				   "lvextend --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	state->percent_check = CHECK_MINIMUM; | ||||
| 	*user = state; | ||||
| @@ -269,9 +268,6 @@ int register_device(const char *device, | ||||
| bad: | ||||
| 	log_error("Failed to monitor snapshot %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -15,8 +15,8 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2 | ||||
| CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2 | ||||
| LIBS += -ldevmapper-event-lvm2 | ||||
|  | ||||
| SOURCES = dmeventd_thin.c | ||||
|  | ||||
| @@ -29,6 +29,8 @@ CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -12,16 +12,17 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "lib/misc/lib.h" | ||||
| #include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h" | ||||
| #include "daemons/dmeventd/libdevmapper-event.h" | ||||
| #include "lib.h"	/* using here lvm log */ | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "libdevmapper-event.h" | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <stdarg.h> | ||||
| #include <pthread.h> | ||||
|  | ||||
| /* TODO - move this mountinfo code into library to be reusable */ | ||||
| #ifdef __linux__ | ||||
| #  include "libdm/misc/kdev_t.h" | ||||
| #  include "kdev_t.h" | ||||
| #else | ||||
| #  define MAJOR(x) major((x)) | ||||
| #  define MINOR(x) minor((x)) | ||||
| @@ -47,8 +48,10 @@ struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	int metadata_percent_check; | ||||
| 	int metadata_percent; | ||||
| 	int metadata_warn_once; | ||||
| 	int data_percent_check; | ||||
| 	int data_percent; | ||||
| 	int data_warn_once; | ||||
| 	uint64_t known_metadata_size; | ||||
| 	uint64_t known_data_size; | ||||
| 	unsigned fails; | ||||
| @@ -56,32 +59,34 @@ struct dso_state { | ||||
| 	int restore_sigset; | ||||
| 	sigset_t old_sigset; | ||||
| 	pid_t pid; | ||||
| 	char *argv[3]; | ||||
| 	char *cmd_str; | ||||
| 	char **argv; | ||||
| 	char cmd_str[1024]; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("thin") | ||||
|  | ||||
| #define UUID_PREFIX "LVM-" | ||||
|  | ||||
| static int _run_command(struct dso_state *state) | ||||
| { | ||||
| 	char val[16]; | ||||
| 	char val[3][36]; | ||||
| 	char *env[] = { val[0], val[1], val[2], NULL }; | ||||
| 	int i; | ||||
|  | ||||
| 	/* Mark for possible lvm2 command we are running from dmeventd | ||||
| 	 * lvm2 will not try to talk back to dmeventd while processing it */ | ||||
| 	(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1); | ||||
| 	(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1"); | ||||
|  | ||||
| 	if (state->data_percent) { | ||||
| 		/* Prepare some known data to env vars for easy use */ | ||||
| 		if (dm_snprintf(val, sizeof(val), "%d", | ||||
| 				state->data_percent / DM_PERCENT_1) != -1) | ||||
| 			(void) setenv("DMEVENTD_THIN_POOL_DATA", val, 1); | ||||
| 		if (dm_snprintf(val, sizeof(val), "%d", | ||||
| 				state->metadata_percent / DM_PERCENT_1) != -1) | ||||
| 			(void) setenv("DMEVENTD_THIN_POOL_METADATA", val, 1); | ||||
| 		(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d", | ||||
| 				   state->data_percent / DM_PERCENT_1); | ||||
| 		(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d", | ||||
| 				   state->metadata_percent / DM_PERCENT_1); | ||||
| 	} else { | ||||
| 		/* For an error event it's for a user to check status and decide */ | ||||
| 		log_debug("Error event processing."); | ||||
| 		env[1] = NULL; | ||||
| 		log_debug("Error event processing"); | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Executing command: %s", state->cmd_str); | ||||
| @@ -95,7 +100,7 @@ static int _run_command(struct dso_state *state) | ||||
| 		/* child */ | ||||
| 		(void) close(0); | ||||
| 		for (i = 3; i < 255; ++i) (void) close(i); | ||||
| 		execvp(state->argv[0], state->argv); | ||||
| 		execve(state->argv[0], state->argv, env); | ||||
| 		_exit(errno); | ||||
| 	} else if (state->pid == -1) { | ||||
| 		log_error("Can't fork command %s.", state->cmd_str); | ||||
| @@ -111,7 +116,7 @@ static int _use_policy(struct dm_task *dmt, struct dso_state *state) | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("dmeventd executes: %s.", state->cmd_str); | ||||
| #endif | ||||
| 	if (state->argv[0]) | ||||
| 	if (state->argv) | ||||
| 		return _run_command(state); | ||||
|  | ||||
| 	if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) { | ||||
| @@ -170,8 +175,8 @@ void process_event(struct dm_task *dmt, | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("Watch for tp-data:%.2f%%  tp-metadata:%.2f%%.", | ||||
| 		  dm_percent_to_round_float(state->data_percent_check, 2), | ||||
| 		  dm_percent_to_round_float(state->metadata_percent_check, 2)); | ||||
| 		  dm_percent_to_float(state->data_percent_check), | ||||
| 		  dm_percent_to_float(state->metadata_percent_check)); | ||||
| #endif | ||||
| 	if (!_wait_for_pid(state)) { | ||||
| 		log_warn("WARNING: Skipping event, child %d is still running (%s).", | ||||
| @@ -249,10 +254,11 @@ void process_event(struct dm_task *dmt, | ||||
| 	 * action is called for:  >50%, >55% ... >95%, 100% | ||||
| 	 */ | ||||
| 	state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks); | ||||
| 	if ((state->metadata_percent > WARNING_THRESH) && | ||||
| 	    (state->metadata_percent > state->metadata_percent_check)) | ||||
| 	if (state->metadata_percent <= WARNING_THRESH) | ||||
| 		state->metadata_warn_once = 0; /* Dropped bellow threshold, reset warn once */ | ||||
| 	else if (!state->metadata_warn_once++) /* Warn once when raised above threshold */ | ||||
| 		log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.", | ||||
| 			 device, dm_percent_to_round_float(state->metadata_percent, 2)); | ||||
| 			 device, dm_percent_to_float(state->metadata_percent)); | ||||
| 	if (state->metadata_percent > CHECK_MINIMUM) { | ||||
| 		/* Run action when usage raised more than CHECK_STEP since the last time */ | ||||
| 		if (state->metadata_percent > state->metadata_percent_check) | ||||
| @@ -264,10 +270,11 @@ void process_event(struct dm_task *dmt, | ||||
| 		state->metadata_percent_check = CHECK_MINIMUM; | ||||
|  | ||||
| 	state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks); | ||||
| 	if ((state->data_percent > WARNING_THRESH) && | ||||
| 	    (state->data_percent > state->data_percent_check)) | ||||
| 	if (state->data_percent <= WARNING_THRESH) | ||||
| 		state->data_warn_once = 0; | ||||
| 	else if (!state->data_warn_once++) | ||||
| 		log_warn("WARNING: Thin pool %s data is now %.2f%% full.", | ||||
| 			 device, dm_percent_to_round_float(state->data_percent, 2)); | ||||
| 			 device, dm_percent_to_float(state->data_percent)); | ||||
| 	if (state->data_percent > CHECK_MINIMUM) { | ||||
| 		/* Run action when usage raised more than CHECK_STEP since the last time */ | ||||
| 		if (state->data_percent > state->data_percent_check) | ||||
| @@ -286,7 +293,7 @@ void process_event(struct dm_task *dmt, | ||||
| 		if (state->fails++ <= state->max_fails) { | ||||
| 			log_debug("Postponing frequently failing policy (%u <= %u).", | ||||
| 				  state->fails - 1, state->max_fails); | ||||
| 			goto out; | ||||
| 			return; | ||||
| 		} | ||||
| 		if (state->max_fails < MAX_FAILS) | ||||
| 			state->max_fails <<= 1; | ||||
| @@ -346,41 +353,34 @@ int register_device(const char *device, | ||||
| 		    void **user) | ||||
| { | ||||
| 	struct dso_state *state; | ||||
| 	int maxcmd; | ||||
| 	char *str; | ||||
| 	char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */ | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str), | ||||
| 				   "_dmeventd_thin_command", device)) | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_str, | ||||
| 				   sizeof(state->cmd_str), | ||||
| 				   "_dmeventd_thin_command", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	if (strncmp(cmd_str, "lvm ", 4) == 0) { | ||||
| 		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) { | ||||
| 			log_error("Failed to copy lvm command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
| 	} else if (cmd_str[0] == '/') { | ||||
| 		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) { | ||||
| 			log_error("Failed to copy thin command."); | ||||
| 	if (strncmp(state->cmd_str, "lvm ", 4)) { | ||||
| 		maxcmd = 2; /* space for last NULL element */ | ||||
| 		for (str = state->cmd_str; *str; str++) | ||||
| 			if (*str == ' ') | ||||
| 				maxcmd++; | ||||
| 		if (!(str = dm_pool_strdup(state->mem, state->cmd_str)) || | ||||
| 		    !(state->argv = dm_pool_zalloc(state->mem, maxcmd * sizeof(char *)))) { | ||||
| 			log_error("Failed to allocate memory for command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		/* Find last space before 'vg/lv' */ | ||||
| 		if (!(str = strrchr(state->cmd_str, ' '))) | ||||
| 			goto inval; | ||||
|  | ||||
| 		if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str, | ||||
| 						       str - state->cmd_str))) { | ||||
| 			log_error("Failed to copy command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		state->argv[1] = str + 1;  /* 1 argument - vg/lv */ | ||||
| 		dm_split_words(str, maxcmd - 1, 0, state->argv); | ||||
| 		_init_thread_signals(state); | ||||
| 	} else /* Unuspported command format */ | ||||
| 		goto inval; | ||||
| 	} else | ||||
| 		memmove(state->cmd_str, state->cmd_str + 4, strlen(state->cmd_str + 4) + 1); | ||||
|  | ||||
| 	state->pid = -1; | ||||
| 	*user = state; | ||||
| @@ -388,14 +388,9 @@ int register_device(const char *device, | ||||
| 	log_info("Monitoring thin pool %s.", device); | ||||
|  | ||||
| 	return 1; | ||||
| inval: | ||||
| 	log_error("Invalid command for monitoring: %s.", cmd_str); | ||||
| bad: | ||||
| 	log_error("Failed to monitor thin pool %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| process_event | ||||
| register_device | ||||
| unregister_device | ||||
| @@ -1,407 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2018 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser 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 | ||||
|  */ | ||||
|  | ||||
| #include "lib/misc/lib.h" | ||||
| #include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h" | ||||
| #include "daemons/dmeventd/libdevmapper-event.h" | ||||
|  | ||||
| /* Use parser from new device_mapper library */ | ||||
| #include "device_mapper/vdo/status.c" | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <stdarg.h> | ||||
|  | ||||
| /* First warning when VDO pool is 80% full. */ | ||||
| #define WARNING_THRESH	(DM_PERCENT_1 * 80) | ||||
| /* Run a check every 5%. */ | ||||
| #define CHECK_STEP	(DM_PERCENT_1 *  5) | ||||
| /* Do not bother checking VDO pool is less than 50% full. */ | ||||
| #define CHECK_MINIMUM	(DM_PERCENT_1 * 50) | ||||
|  | ||||
| #define MAX_FAILS	(256)  /* ~42 mins between cmd call retry with 10s delay */ | ||||
|  | ||||
| #define VDO_DEBUG 0 | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	int percent_check; | ||||
| 	int percent; | ||||
| 	uint64_t known_data_size; | ||||
| 	unsigned fails; | ||||
| 	unsigned max_fails; | ||||
| 	int restore_sigset; | ||||
| 	sigset_t old_sigset; | ||||
| 	pid_t pid; | ||||
| 	char *argv[3]; | ||||
| 	const char *cmd_str; | ||||
| 	const char *name; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("vdo") | ||||
|  | ||||
| static int _run_command(struct dso_state *state) | ||||
| { | ||||
| 	char val[16]; | ||||
| 	int i; | ||||
|  | ||||
| 	/* Mark for possible lvm2 command we are running from dmeventd | ||||
| 	 * lvm2 will not try to talk back to dmeventd while processing it */ | ||||
| 	(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1); | ||||
|  | ||||
| 	if (state->percent) { | ||||
| 		/* Prepare some known data to env vars for easy use */ | ||||
| 		if (dm_snprintf(val, sizeof(val), "%d", | ||||
| 				state->percent / DM_PERCENT_1) != -1) | ||||
| 			(void) setenv("DMEVENTD_VDO_POOL", val, 1); | ||||
| 	} else { | ||||
| 		/* For an error event it's for a user to check status and decide */ | ||||
| 		log_debug("Error event processing."); | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Executing command: %s", state->cmd_str); | ||||
|  | ||||
| 	/* TODO: | ||||
| 	 *   Support parallel run of 'task' and it's waitpid maintainence | ||||
| 	 *   ATM we can't handle signaling of  SIGALRM | ||||
| 	 *   as signalling is not allowed while 'process_event()' is running | ||||
| 	 */ | ||||
| 	if (!(state->pid = fork())) { | ||||
| 		/* child */ | ||||
| 		(void) close(0); | ||||
| 		for (i = 3; i < 255; ++i) (void) close(i); | ||||
| 		execvp(state->argv[0], state->argv); | ||||
| 		_exit(errno); | ||||
| 	} else if (state->pid == -1) { | ||||
| 		log_error("Can't fork command %s.", state->cmd_str); | ||||
| 		state->fails = 1; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _use_policy(struct dm_task *dmt, struct dso_state *state) | ||||
| { | ||||
| #if VDO_DEBUG | ||||
| 	log_debug("dmeventd executes: %s.", state->cmd_str); | ||||
| #endif | ||||
| 	if (state->argv[0]) | ||||
| 		return _run_command(state); | ||||
|  | ||||
| 	if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) { | ||||
| 		log_error("Failed command for %s.", dm_task_get_name(dmt)); | ||||
| 		state->fails = 1; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	state->fails = 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* Check if executed command has finished | ||||
|  * Only 1 command may run */ | ||||
| static int _wait_for_pid(struct dso_state *state) | ||||
| { | ||||
| 	int status = 0; | ||||
|  | ||||
| 	if (state->pid == -1) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!waitpid(state->pid, &status, WNOHANG)) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* Wait for finish */ | ||||
| 	if (WIFEXITED(status)) { | ||||
| 		log_verbose("Child %d exited with status %d.", | ||||
| 			    state->pid, WEXITSTATUS(status)); | ||||
| 		state->fails = WEXITSTATUS(status) ? 1 : 0; | ||||
| 	} else { | ||||
| 		if (WIFSIGNALED(status)) | ||||
| 			log_verbose("Child %d was terminated with status %d.", | ||||
| 				    state->pid, WTERMSIG(status)); | ||||
| 		state->fails = 1; | ||||
| 	} | ||||
|  | ||||
| 	state->pid = -1; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **user) | ||||
| { | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
| 	struct dso_state *state = *user; | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	int needs_policy = 0; | ||||
| 	struct dm_task *new_dmt = NULL; | ||||
| 	struct dm_vdo_status_parse_result vdop = { .status = NULL }; | ||||
|  | ||||
| #if VDO_DEBUG | ||||
| 	log_debug("Watch for VDO %s:%.2f%%.", state->name, | ||||
| 		  dm_percent_to_round_float(state->percent_check, 2)); | ||||
| #endif | ||||
| 	if (!_wait_for_pid(state)) { | ||||
| 		log_warn("WARNING: Skipping event, child %d is still running (%s).", | ||||
| 			 state->pid, state->cmd_str); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (event & DM_EVENT_DEVICE_ERROR) { | ||||
| #if VDO_DEBUG | ||||
| 		log_debug("VDO event error."); | ||||
| #endif | ||||
| 		/* Error -> no need to check and do instant resize */ | ||||
| 		state->percent = 0; | ||||
| 		if (_use_policy(dmt, state)) | ||||
| 			goto out; | ||||
|  | ||||
| 		stack; | ||||
|  | ||||
| 		if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS))) | ||||
| 			goto_out; | ||||
|  | ||||
| 		if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt))) | ||||
| 			goto_out; | ||||
|  | ||||
| 		/* Non-blocking status read */ | ||||
| 		if (!dm_task_no_flush(new_dmt)) | ||||
| 			log_warn("WARNING: Can't set no_flush for dm status."); | ||||
|  | ||||
| 		if (!dm_task_run(new_dmt)) | ||||
| 			goto_out; | ||||
|  | ||||
| 		dmt = new_dmt; | ||||
| 	} | ||||
|  | ||||
| 	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); | ||||
|  | ||||
| 	if (!target_type || (strcmp(target_type, "vdo") != 0)) { | ||||
| 		log_error("Invalid target type."); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_vdo_status_parse(state->mem, params, &vdop)) { | ||||
| 		log_error("Failed to parse status."); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	state->percent = dm_make_percent(vdop.status->used_blocks, | ||||
| 					 vdop.status->total_blocks); | ||||
|  | ||||
| #if VDO_DEBUG | ||||
| 	log_debug("VDO %s status  %.2f%% " FMTu64 "/" FMTu64 ".", | ||||
| 		  state->name, dm_percent_to_round_float(state->percent, 2), | ||||
| 		  vdop.status->used_blocks, vdop.status->total_blocks); | ||||
| #endif | ||||
|  | ||||
| 	/* VDO pool size had changed. Clear the threshold. */ | ||||
| 	if (state->known_data_size != vdop.status->total_blocks) { | ||||
| 		state->percent_check = CHECK_MINIMUM; | ||||
| 		state->known_data_size = vdop.status->total_blocks; | ||||
| 		state->fails = 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Trigger action when threshold boundary is exceeded. | ||||
| 	 * Report 80% threshold warning when it's used above 80%. | ||||
| 	 * Only 100% is exception as it cannot be surpased so policy | ||||
| 	 * action is called for:  >50%, >55% ... >95%, 100% | ||||
| 	 */ | ||||
| 	if ((state->percent > WARNING_THRESH) && | ||||
| 	    (state->percent > state->percent_check)) | ||||
| 		log_warn("WARNING: VDO %s %s is now %.2f%% full.", | ||||
| 			 state->name, device, | ||||
| 			 dm_percent_to_round_float(state->percent, 2)); | ||||
| 	if (state->percent > CHECK_MINIMUM) { | ||||
| 		/* Run action when usage raised more than CHECK_STEP since the last time */ | ||||
| 		if (state->percent > state->percent_check) | ||||
| 			needs_policy = 1; | ||||
| 		state->percent_check = (state->percent / CHECK_STEP + 1) * CHECK_STEP; | ||||
| 		if (state->percent_check == DM_PERCENT_100) | ||||
| 			state->percent_check--; /* Can't get bigger then 100% */ | ||||
| 	} else | ||||
| 		state->percent_check = CHECK_MINIMUM; | ||||
|  | ||||
| 	/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached. | ||||
| 	 * Avoids too high number of error retries, yet shows some status messages in log regularly. | ||||
| 	 * i.e. PV could have been pvmoved and VG/LV was locked for a while... | ||||
| 	 */ | ||||
| 	if (state->fails) { | ||||
| 		if (state->fails++ <= state->max_fails) { | ||||
| 			log_debug("Postponing frequently failing policy (%u <= %u).", | ||||
| 				  state->fails - 1, state->max_fails); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		if (state->max_fails < MAX_FAILS) | ||||
| 			state->max_fails <<= 1; | ||||
| 		state->fails = needs_policy = 1; /* Retry failing command */ | ||||
| 	} else | ||||
| 		state->max_fails = 1; /* Reset on success */ | ||||
|  | ||||
| 	if (needs_policy) | ||||
| 		_use_policy(dmt, state); | ||||
| out: | ||||
| 	if (vdop.status) | ||||
| 		dm_pool_free(state->mem, vdop.status); | ||||
|  | ||||
| 	if (new_dmt) | ||||
| 		dm_task_destroy(new_dmt); | ||||
| } | ||||
|  | ||||
| /* Handle SIGCHLD for a thread */ | ||||
| static void _sig_child(int signum __attribute__((unused))) | ||||
| { | ||||
| 	/* empty SIG_IGN */; | ||||
| } | ||||
|  | ||||
| /* Setup handler for SIGCHLD when executing external command | ||||
|  * to get quick 'waitpid()' reaction | ||||
|  * It will interrupt syscall just like SIGALRM and | ||||
|  * invoke process_event(). | ||||
|  */ | ||||
| static void _init_thread_signals(struct dso_state *state) | ||||
| { | ||||
| 	struct sigaction act = { .sa_handler = _sig_child }; | ||||
| 	sigset_t my_sigset; | ||||
|  | ||||
| 	sigemptyset(&my_sigset); | ||||
|  | ||||
| 	if (sigaction(SIGCHLD, &act, NULL)) | ||||
| 		log_warn("WARNING: Failed to set SIGCHLD action."); | ||||
| 	else if (sigaddset(&my_sigset, SIGCHLD)) | ||||
| 		log_warn("WARNING: Failed to add SIGCHLD to set."); | ||||
| 	else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset)) | ||||
| 		log_warn("WARNING: Failed to unblock SIGCHLD."); | ||||
| 	else | ||||
| 		state->restore_sigset = 1; | ||||
| } | ||||
|  | ||||
| static void _restore_thread_signals(struct dso_state *state) | ||||
| { | ||||
| 	if (state->restore_sigset && | ||||
| 	    pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL)) | ||||
| 		log_warn("WARNING: Failed to block SIGCHLD."); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid, | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **user) | ||||
| { | ||||
| 	struct dso_state *state; | ||||
| 	const char *cmd; | ||||
| 	char *str; | ||||
| 	char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */ | ||||
|         const char *name = "pool"; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("vdo_pool_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	state->cmd_str = ""; | ||||
|  | ||||
| 	/* Search for command for LVM- prefixed devices only */ | ||||
| 	cmd = (strncmp(uuid, "LVM-", 4) == 0) ? "_dmeventd_vdo_command" : ""; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str), cmd, device)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (strncmp(cmd_str, "lvm ", 4) == 0) { | ||||
| 		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) { | ||||
| 			log_error("Failed to copy lvm VDO command."); | ||||
| 				goto bad; | ||||
| 		} | ||||
| 	} else if (cmd_str[0] == '/') { | ||||
| 		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) { | ||||
| 			log_error("Failed to copy VDO command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		/* Find last space before 'vg/lv' */ | ||||
| 		if (!(str = strrchr(state->cmd_str, ' '))) | ||||
| 			goto inval; | ||||
|  | ||||
| 		if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str, | ||||
| 						       str - state->cmd_str))) { | ||||
| 			log_error("Failed to copy command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		state->argv[1] = str + 1;  /* 1 argument - vg/lv */ | ||||
| 		_init_thread_signals(state); | ||||
| 	} else if (cmd[0] == 0) { | ||||
| 		state->name = "volume"; /* What to use with 'others?' */ | ||||
| 	} else/* Unuspported command format */ | ||||
| 		goto inval; | ||||
|  | ||||
| 	state->pid = -1; | ||||
| 	state->name = name; | ||||
| 	*user = state; | ||||
|  | ||||
| 	log_info("Monitoring VDO %s %s.", name, device); | ||||
|  | ||||
| 	return 1; | ||||
| inval: | ||||
| 	log_error("Invalid command for monitoring: %s.", cmd_str); | ||||
| bad: | ||||
| 	log_error("Failed to monitor VDO %s %s.", name, device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **user) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	const char *name = state->name; | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) { | ||||
| 		if (i == 0) | ||||
| 			/* Give it 2 seconds, then try to terminate & kill it */ | ||||
| 			log_verbose("Child %d still not finished (%s) waiting.", | ||||
| 				    state->pid, state->cmd_str); | ||||
| 		else if (i == 3) { | ||||
| 			log_warn("WARNING: Terminating child %d.", state->pid); | ||||
| 			kill(state->pid, SIGINT); | ||||
| 			kill(state->pid, SIGTERM); | ||||
| 		} else if (i == 5) { | ||||
| 			log_warn("WARNING: Killing child %d.", state->pid); | ||||
| 			kill(state->pid, SIGKILL); | ||||
| 		} | ||||
| 		sleep(1); | ||||
| 	} | ||||
|  | ||||
| 	if (state->pid != -1) | ||||
| 		log_warn("WARNING: Cannot kill child %d!", state->pid); | ||||
|  | ||||
| 	_restore_thread_signals(state); | ||||
|  | ||||
| 	dmeventd_lvm2_exit_with_pool(state); | ||||
| 	log_info("No longer monitoring VDO %s %s.", name, device); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
							
								
								
									
										3
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1 @@ | ||||
| path.py | ||||
| lvmdbusd | ||||
| lvmdb.py | ||||
| lvm_shell_proxy.py | ||||
|   | ||||
| @@ -23,10 +23,13 @@ LVMDBUS_SRCDIR_FILES = \ | ||||
| 	cfg.py \ | ||||
| 	cmdhandler.py \ | ||||
| 	fetch.py \ | ||||
| 	__init__.py \ | ||||
| 	job.py \ | ||||
| 	loader.py \ | ||||
| 	lv.py \ | ||||
| 	lvmdb.py \ | ||||
| 	main.py \ | ||||
| 	lvm_shell_proxy.py \ | ||||
| 	lv.py \ | ||||
| 	manager.py \ | ||||
| 	objectmanager.py \ | ||||
| 	pv.py \ | ||||
| @@ -34,40 +37,30 @@ LVMDBUS_SRCDIR_FILES = \ | ||||
| 	state.py \ | ||||
| 	udevwatch.py \ | ||||
| 	utils.py \ | ||||
| 	vg.py \ | ||||
| 	__init__.py | ||||
| 	vg.py | ||||
|  | ||||
| LVMDBUS_BUILDDIR_FILES = \ | ||||
| 	lvmdb.py \ | ||||
| 	lvm_shell_proxy.py \ | ||||
| 	path.py | ||||
|  | ||||
| LVMDBUSD = lvmdbusd | ||||
|  | ||||
| CLEAN_DIRS += __pycache__ | ||||
| LVMDBUSD = $(srcdir)/lvmdbusd | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| .PHONY: install_lvmdbusd | ||||
|  | ||||
| all: | ||||
| 	$(Q) test -x $(LVMDBUSD) || chmod 755 $(LVMDBUSD) | ||||
|  | ||||
| install_lvmdbusd: | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_DIR) $(sbindir) | ||||
| 	$(Q) $(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir) | ||||
| 	$(Q) $(INSTALL_DIR) $(DESTDIR)$(lvmdbusdir) | ||||
| 	$(Q) (cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(DESTDIR)$(lvmdbusdir)) | ||||
| 	$(Q) $(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir) | ||||
| 	$(Q) PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES) | ||||
| 	$(Q) $(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__ | ||||
| 	$(Q) $(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.py[co] | ||||
| 	$(INSTALL_DIR) $(sbindir) | ||||
| 	$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir) | ||||
| 	$(INSTALL_DIR) $(DESTDIR)$(lvmdbusdir) | ||||
| 	(cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(DESTDIR)$(lvmdbusdir)) | ||||
| 	$(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir) | ||||
| 	PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES) | ||||
| 	$(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__ | ||||
| 	$(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.py[co] | ||||
|  | ||||
| install_lvm2: install_lvmdbusd | ||||
|  | ||||
| install: install_lvm2 | ||||
|  | ||||
| DISTCLEAN_TARGETS+= \ | ||||
| 	$(LVMDBUS_BUILDDIR_FILES) \ | ||||
| 	$(LVMDBUSD) | ||||
| 	$(LVMDBUS_BUILDDIR_FILES) | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class AutomatedProperties(dbus.service.Object): | ||||
| 		props = {} | ||||
|  | ||||
| 		for i in self.interface(): | ||||
| 			props[i] = AutomatedProperties._get_all_prop(self, i) | ||||
| 			props[i] = self.GetAll(i) | ||||
|  | ||||
| 		return self._ap_o_path, props | ||||
|  | ||||
| @@ -65,52 +65,31 @@ class AutomatedProperties(dbus.service.Object): | ||||
|  | ||||
| 		return self._ap_interface | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _get_prop(obj, interface_name, property_name): | ||||
| 		value = getattr(obj, property_name) | ||||
| 	# Properties | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ss', out_signature='v') | ||||
| 	def Get(self, interface_name, property_name): | ||||
| 		value = getattr(self, property_name) | ||||
| 		# Note: If we get an exception in this handler we won't know about it, | ||||
| 		# only the side effect of no returned value! | ||||
| 		log_debug('Get (%s), type (%s), value(%s)' % | ||||
| 					(property_name, str(type(value)), str(value))) | ||||
| 		return value | ||||
|  | ||||
| 	# Properties | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ss', out_signature='v', | ||||
| 							async_callbacks=('cb', 'cbe')) | ||||
| 	def Get(self, interface_name, property_name, cb, cbe): | ||||
| 		# Note: If we get an exception in this handler we won't know about it, | ||||
| 		# only the side effect of no returned value! | ||||
| 		r = cfg.create_request_entry( | ||||
| 			-1, AutomatedProperties._get_prop, | ||||
| 			(self, interface_name, property_name), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _get_all_prop(obj, interface_name): | ||||
| 		if interface_name in obj.interface(True): | ||||
| 							in_signature='s', out_signature='a{sv}') | ||||
| 	def GetAll(self, interface_name): | ||||
| 		if interface_name in self.interface(True): | ||||
| 			# Using introspection, lets build this dynamically | ||||
| 			properties = get_properties(obj) | ||||
| 			properties = get_properties(self) | ||||
| 			if interface_name in properties: | ||||
| 				return properties[interface_name][1] | ||||
| 			return {} | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			obj._ap_interface, | ||||
| 			self._ap_interface, | ||||
| 			'The object %s does not implement the %s interface' | ||||
| 			% (obj.__class__, interface_name)) | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='s', out_signature='a{sv}', | ||||
| 							async_callbacks=('cb', 'cbe')) | ||||
| 	def GetAll(self, interface_name, cb, cbe): | ||||
| 		r = cfg.create_request_entry( | ||||
| 			-1, AutomatedProperties._get_all_prop, | ||||
| 			(self, interface_name), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
| 			% (self.__class__, interface_name)) | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ssv') | ||||
| @@ -155,7 +134,7 @@ class AutomatedProperties(dbus.service.Object): | ||||
| 		# through all dbus objects as some don't have a search method, like | ||||
| 		# 'Manager' object. | ||||
| 		if not self._ap_search_method: | ||||
| 			return 0 | ||||
| 			return | ||||
|  | ||||
| 		search = self.lvm_id | ||||
| 		if search_key: | ||||
|   | ||||
| @@ -9,13 +9,11 @@ | ||||
|  | ||||
| import subprocess | ||||
| from . import cfg | ||||
| from .cmdhandler import options_to_cli_args, LvmExecutionMeta | ||||
| from .cmdhandler import options_to_cli_args | ||||
| import dbus | ||||
| from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\ | ||||
| 	add_no_notify | ||||
| from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug | ||||
| import os | ||||
| import threading | ||||
| import time | ||||
|  | ||||
|  | ||||
| def pv_move_lv_cmd(move_options, lv_full_name, | ||||
| @@ -44,15 +42,6 @@ def _move_merge(interface_name, command, job_state): | ||||
| 	# the command always as we will be getting periodic output from them on | ||||
| 	# the status of the long running operation. | ||||
| 	command.insert(0, cfg.LVM_CMD) | ||||
|  | ||||
| 	# Instruct lvm to not register an event with us | ||||
| 	command = add_no_notify(command) | ||||
|  | ||||
| 	#(self, start, ended, cmd, ec, stdout_txt, stderr_txt) | ||||
| 	meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None) | ||||
|  | ||||
| 	cfg.blackbox.add(meta) | ||||
|  | ||||
| 	process = subprocess.Popen(command, stdout=subprocess.PIPE, | ||||
| 								env=os.environ, | ||||
| 								stderr=subprocess.PIPE, close_fds=True) | ||||
| @@ -70,21 +59,12 @@ def _move_merge(interface_name, command, job_state): | ||||
| 				(device, ignore, percentage) = line_str.split(':') | ||||
| 				job_state.Percent = round( | ||||
| 					float(percentage.strip()[:-1]), 1) | ||||
|  | ||||
| 				# While the move is in progress we need to periodically update | ||||
| 				# the state to reflect where everything is at. | ||||
| 				cfg.load() | ||||
| 		except ValueError: | ||||
| 			log_error("Trying to parse percentage which failed for %s" % | ||||
| 				line_str) | ||||
|  | ||||
| 	out = process.communicate() | ||||
|  | ||||
| 	with meta.lock: | ||||
| 		meta.ended = time.time() | ||||
| 		meta.ec = process.returncode | ||||
| 		meta.stderr_txt = out[1] | ||||
|  | ||||
| 	if process.returncode == 0: | ||||
| 		job_state.Percent = 100 | ||||
| 	else: | ||||
| @@ -158,6 +138,5 @@ def _run_cmd(req): | ||||
|  | ||||
|  | ||||
| def cmd_runner(request): | ||||
| 	t = threading.Thread(target=_run_cmd, args=(request,), | ||||
| 							name="cmd_runner %s" % str(request.method)) | ||||
| 	t = threading.Thread(target=_run_cmd, args=(request,)) | ||||
| 	t.start() | ||||
|   | ||||
| @@ -26,7 +26,7 @@ bus = None | ||||
| args = None | ||||
|  | ||||
| # Set to true if we are depending on external events for updates | ||||
| got_external_event = False | ||||
| ee = False | ||||
|  | ||||
| # Shared state variable across all processes | ||||
| run = multiprocessing.Value('i', 1) | ||||
| @@ -84,16 +84,3 @@ db = None | ||||
|  | ||||
| # lvm flight recorder | ||||
| blackbox = None | ||||
|  | ||||
| # RequestEntry ctor | ||||
| create_request_entry = None | ||||
|  | ||||
|  | ||||
| def exit_daemon(): | ||||
|     """ | ||||
|     Exit the daemon cleanly | ||||
|     :return: | ||||
|     """ | ||||
|     if run and loop: | ||||
|         run.value = 0 | ||||
|         loop.quit() | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import traceback | ||||
| import os | ||||
|  | ||||
| from lvmdbusd import cfg | ||||
| from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify | ||||
| from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error | ||||
| from lvmdbusd.lvm_shell_proxy import LVMShellProxy | ||||
|  | ||||
| try: | ||||
| @@ -37,7 +37,6 @@ cmd_lock = threading.RLock() | ||||
| class LvmExecutionMeta(object): | ||||
|  | ||||
| 	def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt): | ||||
| 		self.lock = threading.RLock() | ||||
| 		self.start = start | ||||
| 		self.ended = ended | ||||
| 		self.cmd = cmd | ||||
| @@ -46,13 +45,12 @@ class LvmExecutionMeta(object): | ||||
| 		self.stderr_txt = stderr_txt | ||||
|  | ||||
| 	def __str__(self): | ||||
| 		with self.lock: | ||||
| 			return "EC= %d for %s\n" \ | ||||
| 				"STARTED: %f, ENDED: %f\n" \ | ||||
| 				"STDOUT=%s\n" \ | ||||
| 				"STDERR=%s\n" % \ | ||||
| 				(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt, | ||||
| 				self.stderr_txt) | ||||
| 		return "EC= %d for %s\n" \ | ||||
| 			"STARTED: %f, ENDED: %f\n" \ | ||||
| 			"STDOUT=%s\n" \ | ||||
| 			"STDERR=%s\n" % \ | ||||
| 			(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt, | ||||
| 			self.stderr_txt) | ||||
|  | ||||
|  | ||||
| class LvmFlightRecorder(object): | ||||
| @@ -67,7 +65,7 @@ class LvmFlightRecorder(object): | ||||
| 		with cmd_lock: | ||||
| 			if len(self.queue): | ||||
| 				log_error("LVM dbus flight recorder START") | ||||
| 				for c in reversed(self.queue): | ||||
| 				for c in self.queue: | ||||
| 					log_error(str(c)) | ||||
| 				log_error("LVM dbus flight recorder END") | ||||
|  | ||||
| @@ -95,7 +93,6 @@ def call_lvm(command, debug=False): | ||||
| 	# Prepend the full lvm executable so that we can run different versions | ||||
| 	# in different locations on the same box | ||||
| 	command.insert(0, cfg.LVM_CMD) | ||||
| 	command = add_no_notify(command) | ||||
|  | ||||
| 	process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True, | ||||
| 					env=os.environ) | ||||
| @@ -217,10 +214,7 @@ def options_to_cli_args(options): | ||||
| 		else: | ||||
| 			rc.append("--%s" % k) | ||||
| 		if v != "": | ||||
| 			if isinstance(v, int): | ||||
| 				rc.append(str(int(v))) | ||||
| 			else: | ||||
| 				rc.append(str(v)) | ||||
| 			rc.append(str(v)) | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| @@ -266,10 +260,10 @@ def lv_tag(lv_name, add, rm, tag_options): | ||||
| 	return _tag('lvchange', lv_name, add, rm, tag_options) | ||||
|  | ||||
|  | ||||
| def vg_rename(vg_uuid, new_name, rename_options): | ||||
| def vg_rename(vg, new_name, rename_options): | ||||
| 	cmd = ['vgrename'] | ||||
| 	cmd.extend(options_to_cli_args(rename_options)) | ||||
| 	cmd.extend([vg_uuid, new_name]) | ||||
| 	cmd.extend([vg, new_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -283,8 +277,8 @@ def vg_remove(vg_name, remove_options): | ||||
| def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--size', '%dB' % size_bytes]) | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	pv_dest_ranges(cmd, pv_dests) | ||||
| 	return call(cmd) | ||||
|  | ||||
| @@ -295,7 +289,7 @@ def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes): | ||||
| 	cmd.extend(["-s"]) | ||||
|  | ||||
| 	if size_bytes != 0: | ||||
| 		cmd.extend(['--size', '%dB' % size_bytes]) | ||||
| 		cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
| @@ -306,11 +300,9 @@ def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool): | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	if not thin_pool: | ||||
| 		cmd.extend(['--size', '%dB' % size_bytes]) | ||||
| 		cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	else: | ||||
| 		cmd.extend(['--thin', '--size', '%dB' % size_bytes]) | ||||
|  | ||||
| 	cmd.extend(['--yes']) | ||||
| 		cmd.extend(['--thin', '--size', str(size_bytes) + 'B']) | ||||
| 	return cmd | ||||
|  | ||||
|  | ||||
| @@ -323,10 +315,10 @@ def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool): | ||||
| def vg_lv_create_striped(vg_name, create_options, name, size_bytes, | ||||
| 							num_stripes, stripe_size_kb, thin_pool): | ||||
| 	cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool) | ||||
| 	cmd.extend(['--stripes', str(int(num_stripes))]) | ||||
| 	cmd.extend(['--stripes', str(num_stripes)]) | ||||
|  | ||||
| 	if stripe_size_kb != 0: | ||||
| 		cmd.extend(['--stripesize', str(int(stripe_size_kb))]) | ||||
| 		cmd.extend(['--stripesize', str(stripe_size_kb)]) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
| @@ -339,15 +331,15 @@ def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes, | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	cmd.extend(['--type', raid_type]) | ||||
| 	cmd.extend(['--size', '%dB' % size_bytes]) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	if num_stripes != 0: | ||||
| 		cmd.extend(['--stripes', str(int(num_stripes))]) | ||||
| 		cmd.extend(['--stripes', str(num_stripes)]) | ||||
|  | ||||
| 	if stripe_size_kb != 0: | ||||
| 		cmd.extend(['--stripesize', str(int(stripe_size_kb))]) | ||||
| 		cmd.extend(['--stripesize', str(stripe_size_kb)]) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -366,9 +358,9 @@ def vg_lv_create_mirror( | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	cmd.extend(['--type', 'mirror']) | ||||
| 	cmd.extend(['--mirrors', str(int(num_copies))]) | ||||
| 	cmd.extend(['--size', '%dB' % size_bytes]) | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	cmd.extend(['--mirrors', str(num_copies)]) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -421,8 +413,8 @@ def lv_resize(lv_full_name, size_change, pv_dests, | ||||
| def lv_lv_create(lv_full_name, create_options, name, size_bytes): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--virtualsize', '%dB' % size_bytes, '-T']) | ||||
| 	cmd.extend(['--name', name, lv_full_name, '--yes']) | ||||
| 	cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T']) | ||||
| 	cmd.extend(['--name', name, lv_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -500,8 +492,7 @@ def lvm_full_report_json(): | ||||
| 	]) | ||||
|  | ||||
| 	rc, out, err = call(cmd) | ||||
| 	# When we have an exported vg the exit code of lvs or fullreport will be 5 | ||||
| 	if rc == 0 or rc == 5: | ||||
| 	if rc == 0: | ||||
| 		# With the current implementation, if we are using the shell then we | ||||
| 		# are using JSON and JSON is returned back to us as it was parsed to | ||||
| 		# figure out if we completed OK or not | ||||
| @@ -559,7 +550,7 @@ def pv_resize(device, size_bytes, create_options): | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	if size_bytes != 0: | ||||
| 		cmd.extend(['--yes', '--setphysicalvolumesize', '%dB' % size_bytes]) | ||||
| 		cmd.extend(['--setphysicalvolumesize', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend([device]) | ||||
| 	return call(cmd) | ||||
| @@ -624,10 +615,10 @@ def vg_reduce(vg_name, missing, pv_devices, reduce_options): | ||||
| 	cmd = ['vgreduce'] | ||||
| 	cmd.extend(options_to_cli_args(reduce_options)) | ||||
|  | ||||
| 	if len(pv_devices) == 0: | ||||
| 		cmd.append('--all') | ||||
| 	if missing: | ||||
| 		cmd.append('--removemissing') | ||||
| 	elif len(pv_devices) == 0: | ||||
| 		cmd.append('--all') | ||||
|  | ||||
| 	cmd.append(vg_name) | ||||
| 	cmd.extend(pv_devices) | ||||
| @@ -655,12 +646,12 @@ def vg_allocation_policy(vg_name, policy, policy_options): | ||||
|  | ||||
|  | ||||
| def vg_max_pv(vg_name, number, max_options): | ||||
| 	return _vg_value_set(vg_name, ['--maxphysicalvolumes', str(int(number))], | ||||
| 	return _vg_value_set(vg_name, ['--maxphysicalvolumes', str(number)], | ||||
| 							max_options) | ||||
|  | ||||
|  | ||||
| def vg_max_lv(vg_name, number, max_options): | ||||
| 	return _vg_value_set(vg_name, ['-l', str(int(number))], max_options) | ||||
| 	return _vg_value_set(vg_name, ['-l', str(number)], max_options) | ||||
|  | ||||
|  | ||||
| def vg_uuid_gen(vg_name, ignore, options): | ||||
|   | ||||
| @@ -14,7 +14,6 @@ from . import cfg | ||||
| from .utils import MThreadRunner, log_debug, log_error | ||||
| import threading | ||||
| import queue | ||||
| import time | ||||
| import traceback | ||||
|  | ||||
|  | ||||
| @@ -83,12 +82,10 @@ class StateUpdate(object): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def update_thread(obj): | ||||
| 		exception_count = 0 | ||||
|  | ||||
| 		queued_requests = [] | ||||
| 		while cfg.run.value != 0: | ||||
| 			# noinspection PyBroadException | ||||
| 			try: | ||||
| 				queued_requests = [] | ||||
| 				refresh = True | ||||
| 				emit_signal = True | ||||
| 				cache_refresh = True | ||||
| @@ -99,7 +96,7 @@ class StateUpdate(object): | ||||
| 					wait = not obj.deferred | ||||
| 					obj.deferred = False | ||||
|  | ||||
| 				if len(queued_requests) == 0 and wait: | ||||
| 				if wait: | ||||
| 					queued_requests.append(obj.queue.get(True, 2)) | ||||
|  | ||||
| 				# Ok we have one or the deferred queue has some, | ||||
| @@ -134,31 +131,11 @@ class StateUpdate(object): | ||||
| 				for i in queued_requests: | ||||
| 					i.set_result(num_changes) | ||||
|  | ||||
| 				# Only clear out the requests after we have given them a result | ||||
| 				# otherwise we can orphan the waiting threads and they never | ||||
| 				# wake up if we get an exception | ||||
| 				queued_requests = [] | ||||
|  | ||||
| 				# We retrieved OK, clear exception count | ||||
| 				exception_count = 0 | ||||
|  | ||||
| 			except queue.Empty: | ||||
| 				pass | ||||
| 			except Exception as e: | ||||
| 			except Exception: | ||||
| 				st = traceback.format_exc() | ||||
| 				log_error("update_thread exception: \n%s" % st) | ||||
| 				cfg.blackbox.dump() | ||||
| 				exception_count += 1 | ||||
| 				if exception_count >= 5: | ||||
| 					for i in queued_requests: | ||||
| 						i.set_result(e) | ||||
|  | ||||
| 					log_error("Too many errors in update_thread, exiting daemon") | ||||
| 					cfg.exit_daemon() | ||||
|  | ||||
| 				else: | ||||
| 					# Slow things down when encountering errors | ||||
| 					time.sleep(1) | ||||
|  | ||||
| 	def __init__(self): | ||||
| 		self.lock = threading.RLock() | ||||
| @@ -169,8 +146,7 @@ class StateUpdate(object): | ||||
| 		load(refresh=False, emit_signal=False, need_main_thread=False) | ||||
|  | ||||
| 		self.thread = threading.Thread(target=StateUpdate.update_thread, | ||||
| 										args=(self,), | ||||
| 										name="StateUpdate.update_thread") | ||||
| 										args=(self,)) | ||||
|  | ||||
| 	def load(self, refresh=True, emit_signal=True, cache_refresh=True, | ||||
| 					log=True, need_main_thread=True): | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
| from .utils import job_obj_path_generate, mt_async_call | ||||
| from .utils import job_obj_path_generate, mt_async_result, mt_run_no_wait | ||||
| from . import cfg | ||||
| from .cfg import JOB_INTERFACE | ||||
| import dbus | ||||
| @@ -30,7 +30,7 @@ class WaitingClient(object): | ||||
| 				# Remove ourselves from waiting client | ||||
| 				wc.job_state.remove_waiting_client(wc) | ||||
| 				wc.timer_id = -1 | ||||
| 				mt_async_call(wc.cb, wc.job_state.Complete) | ||||
| 				mt_async_result(wc.cb, wc.job_state.Complete) | ||||
| 				wc.job_state = None | ||||
|  | ||||
| 	def __init__(self, job_state, tmo, cb, cbe): | ||||
| @@ -55,7 +55,7 @@ class WaitingClient(object): | ||||
| 					GLib.source_remove(self.timer_id) | ||||
| 					self.timer_id = -1 | ||||
|  | ||||
| 				mt_async_call(self.cb, self.job_state.Complete) | ||||
| 				mt_async_result(self.cb, self.job_state.Complete) | ||||
| 				self.job_state = None | ||||
|  | ||||
|  | ||||
| @@ -188,7 +188,7 @@ class Job(AutomatedProperties): | ||||
| 	@Complete.setter | ||||
| 	def Complete(self, value): | ||||
| 		self.state.Complete = value | ||||
| 		mt_async_call(Job._signal_complete, self) | ||||
| 		mt_run_no_wait(Job._signal_complete, self) | ||||
|  | ||||
| 	@property | ||||
| 	def GetError(self): | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| from .utils import vg_obj_path_generate, log_error | ||||
| from .utils import vg_obj_path_generate | ||||
| import dbus | ||||
| from . import cmdhandler | ||||
| from . import cfg | ||||
| @@ -24,8 +24,6 @@ from . import background | ||||
| from .utils import round_size, mt_remove_dbus_objects | ||||
| from .job import JobState | ||||
|  | ||||
| import traceback | ||||
|  | ||||
|  | ||||
| # Try and build a key for a LV, so that we sort the LVs with least dependencies | ||||
| # first.  This may be error prone because of the flexibility LVM | ||||
| @@ -234,6 +232,7 @@ class LvState(State): | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Attr', 's') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'SnapPercent', 'u') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataPercent', 'u') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'CopyPercent', 'u') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'SyncPercent', 'u') | ||||
| @@ -293,22 +292,6 @@ class LvCommon(AutomatedProperties): | ||||
| 				(lv_uuid, lv_name)) | ||||
| 		return dbo | ||||
|  | ||||
| 	def attr_struct(self, index, type_map, default='undisclosed'): | ||||
| 		try: | ||||
| 			if self.state.Attr[index] not in type_map: | ||||
| 				log_error("LV %s %s with lv_attr %s, lv_attr[%d] = " | ||||
| 					"'%s' is not known" % | ||||
| 					(self.Uuid, self.Name, self.Attr, index, | ||||
| 					self.state.Attr[index])) | ||||
|  | ||||
| 			return dbus.Struct((self.state.Attr[index], | ||||
| 				type_map.get(self.state.Attr[index], default)), | ||||
| 								signature="(ss)") | ||||
| 		except BaseException: | ||||
| 			st = traceback.format_exc() | ||||
| 			log_error("attr_struct: \n%s" % st) | ||||
| 			return dbus.Struct(('?', 'Unavailable'), signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def VolumeType(self): | ||||
| 		type_map = {'C': 'Cache', 'm': 'mirrored', | ||||
| @@ -322,14 +305,16 @@ class LvCommon(AutomatedProperties): | ||||
| 					'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data', | ||||
| 					'e': 'raid or pool metadata or pool metadata spare', | ||||
| 					'-': 'Unspecified'} | ||||
| 		return self.attr_struct(0, type_map) | ||||
| 		return dbus.Struct((self.state.Attr[0], type_map[self.state.Attr[0]]), | ||||
| 						signature="as") | ||||
|  | ||||
| 	@property | ||||
| 	def Permissions(self): | ||||
| 		type_map = {'w': 'writable', 'r': 'read-only', | ||||
| 					'R': 'Read-only activation of non-read-only volume', | ||||
| 					'-': 'Unspecified'} | ||||
| 		return self.attr_struct(1, type_map) | ||||
| 		return dbus.Struct((self.state.Attr[1], type_map[self.state.Attr[1]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def AllocationPolicy(self): | ||||
| @@ -338,7 +323,8 @@ class LvCommon(AutomatedProperties): | ||||
| 					'i': 'inherited', 'I': 'inherited locked', | ||||
| 					'l': 'cling', 'L': 'cling locked', | ||||
| 					'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'} | ||||
| 		return self.attr_struct(2, type_map) | ||||
| 		return dbus.Struct((self.state.Attr[2], type_map[self.state.Attr[2]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def FixedMinor(self): | ||||
| @@ -346,20 +332,15 @@ class LvCommon(AutomatedProperties): | ||||
|  | ||||
| 	@property | ||||
| 	def State(self): | ||||
| 		type_map = {'a': 'active', | ||||
| 					's': 'suspended', | ||||
| 					'I': 'Invalid snapshot', | ||||
| 		type_map = {'a': 'active', 's': 'suspended', 'I': 'Invalid snapshot', | ||||
| 					'S': 'invalid Suspended snapshot', | ||||
| 					'm': 'snapshot merge failed', | ||||
| 					'M': 'suspended snapshot (M)erge failed', | ||||
| 					'd': 'mapped device present without  tables', | ||||
| 					'i': 'mapped device present with inactive table', | ||||
| 					'h': 'historical', | ||||
| 					'c': 'check needed suspended thin-pool', | ||||
| 					'C': 'check needed', | ||||
| 					'X': 'unknown', | ||||
| 					'-': 'Unspecified'} | ||||
| 		return self.attr_struct(4, type_map) | ||||
| 					'X': 'unknown', '-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[4], type_map[self.state.Attr[4]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def TargetType(self): | ||||
| @@ -375,18 +356,11 @@ class LvCommon(AutomatedProperties): | ||||
|  | ||||
| 	@property | ||||
| 	def Health(self): | ||||
| 		type_map = {'p': 'partial', | ||||
| 					'r': 'refresh needed', | ||||
| 					'm': 'mismatches', | ||||
| 					'w': 'writemostly', | ||||
| 					'X': 'unknown', | ||||
| 					'-': 'unspecified', | ||||
| 					's': 'reshaping', | ||||
| 					'F': 'failed', | ||||
| 					'D': 'Data space', | ||||
| 					'R': 'Remove', | ||||
| 					'M': 'Metadata'} | ||||
| 		return self.attr_struct(8, type_map) | ||||
| 		type_map = {'p': 'partial', 'r': 'refresh', | ||||
| 					'm': 'mismatches', 'w': 'writemostly', | ||||
| 					'X': 'X unknown', '-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[8], type_map[self.state.Attr[8]]), | ||||
| 					signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def SkipActivation(self): | ||||
| @@ -524,7 +498,7 @@ class Lv(LvCommon): | ||||
| 		# it is a thin lv | ||||
| 		if not dbo.IsThinVolume: | ||||
| 			if optional_size == 0: | ||||
| 				space = dbo.SizeBytes // 80 | ||||
| 				space = dbo.SizeBytes / 80 | ||||
| 				remainder = space % 512 | ||||
| 				optional_size = space + 512 - remainder | ||||
|  | ||||
|   | ||||
							
								
								
									
										11
									
								
								daemons/lvmdbusd/lvm_shell_proxy.py.in → daemons/lvmdbusd/lvm_shell_proxy.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										11
									
								
								daemons/lvmdbusd/lvm_shell_proxy.py.in → daemons/lvmdbusd/lvm_shell_proxy.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!@PYTHON3@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| @@ -29,7 +29,7 @@ except ImportError: | ||||
| 
 | ||||
| 
 | ||||
| from lvmdbusd.cfg import LVM_CMD | ||||
| from lvmdbusd.utils import log_debug, log_error, add_no_notify | ||||
| from lvmdbusd.utils import log_debug, log_error | ||||
| 
 | ||||
| SHELL_PROMPT = "lvm> " | ||||
| 
 | ||||
| @@ -206,8 +206,6 @@ class LVMShellProxy(object): | ||||
| 				self.lvm_shell.returncode, | ||||
| 				"Underlying lvm shell process is not present!") | ||||
| 
 | ||||
| 		argv = add_no_notify(argv) | ||||
| 
 | ||||
| 		# create the command string | ||||
| 		cmd = " ".join(_quote_arg(arg) for arg in argv) | ||||
| 		cmd += "\n" | ||||
| @@ -220,10 +218,7 @@ class LVMShellProxy(object): | ||||
| 
 | ||||
| 		# Parse the report to see what happened | ||||
| 		if 'log' in report_json: | ||||
| 			ret_code = int(report_json['log'][-1:][0]['log_ret_code']) | ||||
| 			# If we have an exported vg we get a log_ret_code == 5 when | ||||
| 			# we do a 'fullreport' | ||||
| 			if (ret_code == 1) or (ret_code == 5 and argv[0] == 'fullreport'): | ||||
| 			if report_json['log'][-1:][0]['log_ret_code'] == '1': | ||||
| 				rc = 0 | ||||
| 			else: | ||||
| 				error_msg = self.get_error_msg() | ||||
							
								
								
									
										32
									
								
								daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										32
									
								
								daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!@PYTHON3@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| @@ -141,22 +141,13 @@ class DataStore(object): | ||||
| 
 | ||||
| 	@staticmethod | ||||
| 	def _parse_vgs(_vgs): | ||||
| 		vgs = sorted(_vgs, key=lambda vk: vk['vg_uuid']) | ||||
| 		vgs = sorted(_vgs, key=lambda vk: vk['vg_name']) | ||||
| 
 | ||||
| 		c_vgs = OrderedDict() | ||||
| 		c_lookup = {} | ||||
| 
 | ||||
| 		for i in vgs: | ||||
| 			vg_name = i['vg_name'] | ||||
| 
 | ||||
| 			# Lvm allows duplicate vg names.  When this occurs, each subsequent | ||||
| 			# matching VG name will be called vg_name:vg_uuid.  Note: ':' is an | ||||
| 			# invalid character for lvm VG names | ||||
| 			if vg_name in c_lookup: | ||||
| 				vg_name = "%s:%s" % (vg_name, i['vg_uuid']) | ||||
| 				i['vg_name'] = vg_name | ||||
| 
 | ||||
| 			c_lookup[vg_name] = i['vg_uuid'] | ||||
| 			c_lookup[i['vg_name']] = i['vg_uuid'] | ||||
| 			DataStore._insert_record(c_vgs, i['vg_uuid'], i, []) | ||||
| 
 | ||||
| 		return c_vgs, c_lookup | ||||
| @@ -171,22 +162,13 @@ class DataStore(object): | ||||
| 				tmp_vg.extend(r['vg']) | ||||
| 
 | ||||
| 		# Sort for consistent output, however this is optional | ||||
| 		vgs = sorted(tmp_vg, key=lambda vk: vk['vg_uuid']) | ||||
| 		vgs = sorted(tmp_vg, key=lambda vk: vk['vg_name']) | ||||
| 
 | ||||
| 		c_vgs = OrderedDict() | ||||
| 		c_lookup = {} | ||||
| 
 | ||||
| 		for i in vgs: | ||||
| 			vg_name = i['vg_name'] | ||||
| 
 | ||||
| 			# Lvm allows duplicate vg names.  When this occurs, each subsequent | ||||
| 			# matching VG name will be called vg_name:vg_uuid.  Note: ':' is an | ||||
| 			# invalid character for lvm VG names | ||||
| 			if vg_name in c_lookup: | ||||
| 				vg_name = "%s:%s" % (vg_name, i['vg_uuid']) | ||||
| 				i['vg_name'] = vg_name | ||||
| 
 | ||||
| 			c_lookup[vg_name] = i['vg_uuid'] | ||||
| 			c_lookup[i['vg_name']] = i['vg_uuid'] | ||||
| 			c_vgs[i['vg_uuid']] = i | ||||
| 
 | ||||
| 		return c_vgs, c_lookup | ||||
| @@ -539,10 +521,6 @@ if __name__ == "__main__": | ||||
| 	for v in ds.vgs.values(): | ||||
| 		pp.pprint(v) | ||||
| 
 | ||||
| 	print("VG name to UUID") | ||||
| 	for k, v in ds.vg_name_to_uuid.items(): | ||||
| 		print("%s: %s" % (k, v)) | ||||
| 
 | ||||
| 	print("LVS") | ||||
| 	for v in ds.lvs.values(): | ||||
| 		pp.pprint(v) | ||||
							
								
								
									
										2
									
								
								daemons/lvmdbusd/lvmdbusd.in → daemons/lvmdbusd/lvmdbusd
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmdbusd/lvmdbusd.in → daemons/lvmdbusd/lvmdbusd
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!@PYTHON3@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| @@ -30,7 +30,6 @@ import argparse | ||||
| import os | ||||
| import sys | ||||
| from .cmdhandler import LvmFlightRecorder | ||||
| from .request import RequestEntry | ||||
|  | ||||
|  | ||||
| class Lvm(objectmanager.ObjectManager): | ||||
| @@ -63,24 +62,6 @@ def check_bb_size(value): | ||||
| 	return v | ||||
|  | ||||
|  | ||||
| def install_signal_handlers(): | ||||
| 	# Because of the glib main loop stuff the python signal handler code is | ||||
| 	# apparently not usable and we need to use the glib calls instead | ||||
| 	signal_add = None | ||||
|  | ||||
| 	if hasattr(GLib, 'unix_signal_add'): | ||||
| 		signal_add = GLib.unix_signal_add | ||||
| 	elif hasattr(GLib, 'unix_signal_add_full'): | ||||
| 		signal_add = GLib.unix_signal_add_full | ||||
|  | ||||
| 	if signal_add: | ||||
| 		signal_add(GLib.PRIORITY_HIGH, signal.SIGHUP, utils.handler, signal.SIGHUP) | ||||
| 		signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, utils.handler, signal.SIGINT) | ||||
| 		signal_add(GLib.PRIORITY_HIGH, signal.SIGUSR1, utils.handler, signal.SIGUSR1) | ||||
| 	else: | ||||
| 		log_error("GLib.unix_signal_[add|add_full] are NOT available!") | ||||
|  | ||||
|  | ||||
| def main(): | ||||
| 	start = time.time() | ||||
| 	# Add simple command line handling | ||||
| @@ -116,7 +97,6 @@ def main(): | ||||
| 	os.environ["LC_ALL"] = "C" | ||||
|  | ||||
| 	cfg.args = parser.parse_args() | ||||
| 	cfg.create_request_entry = RequestEntry | ||||
|  | ||||
| 	# We create a flight recorder in cmdhandler too, but we replace it here | ||||
| 	# as the user may be specifying a different size.  The default one in | ||||
| @@ -130,7 +110,12 @@ def main(): | ||||
| 	# List of threads that we start up | ||||
| 	thread_list = [] | ||||
|  | ||||
| 	install_signal_handlers() | ||||
| 	# Install signal handlers | ||||
| 	for s in [signal.SIGHUP, signal.SIGINT]: | ||||
| 		try: | ||||
| 			signal.signal(s, utils.handler) | ||||
| 		except RuntimeError: | ||||
| 			pass | ||||
|  | ||||
| 	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | ||||
| 	dbus.mainloop.glib.threads_init() | ||||
| @@ -151,8 +136,7 @@ def main(): | ||||
|  | ||||
| 	# Using a thread to process requests, we cannot hang the dbus library | ||||
| 	# thread that is handling the dbus interface | ||||
| 	thread_list.append(threading.Thread(target=process_request, | ||||
| 										name='process_request')) | ||||
| 	thread_list.append(threading.Thread(target=process_request)) | ||||
|  | ||||
| 	# Have a single thread handling updating lvm and the dbus model so we | ||||
| 	# don't have multiple threads doing this as the same time | ||||
| @@ -160,6 +144,7 @@ def main(): | ||||
| 	thread_list.append(updater.thread) | ||||
|  | ||||
| 	cfg.load = updater.load | ||||
| 	cfg.event = updater.event | ||||
|  | ||||
| 	cfg.loop = GLib.MainLoop() | ||||
|  | ||||
| @@ -190,7 +175,5 @@ def main(): | ||||
| 			for thread in thread_list: | ||||
| 				thread.join() | ||||
| 	except KeyboardInterrupt: | ||||
| 		# If we are unable to register signal handler, we will end up here when | ||||
| 		# the service gets a ^C or a kill -2 <parent pid> | ||||
| 		utils.handler(signal.SIGINT) | ||||
| 		utils.handler(signal.SIGINT, None) | ||||
| 	return 0 | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| @@ -47,7 +48,7 @@ class Manager(AutomatedProperties): | ||||
| 		pv = cfg.om.get_object_path_by_uuid_lvm_id(device, device) | ||||
| 		if pv: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, "PV %s Already exists!" % device) | ||||
| 				MANAGER_INTERFACE, "PV Already exists!") | ||||
|  | ||||
| 		rc, out, err = cmdhandler.pv_create(create_options, [device]) | ||||
| 		Manager.handle_execute(rc, out, err) | ||||
| @@ -131,28 +132,11 @@ class Manager(AutomatedProperties): | ||||
| 		r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE) | ||||
| 	def FlightRecorderDump(self): | ||||
| 		""" | ||||
| 		Dump the flight recorder to syslog | ||||
| 		""" | ||||
| 		cfg.blackbox.dump() | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lookup_by_lvm_id(key): | ||||
| 		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key) | ||||
| 		if not p: | ||||
| 			p = '/' | ||||
| 		utils.log_debug('LookUpByLvmId: key = %s, result = %s' % (key, p)) | ||||
| 		return p | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='s', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LookUpByLvmId(self, key, cb, cbe): | ||||
| 		out_signature='o') | ||||
| 	def LookUpByLvmId(self, key): | ||||
| 		""" | ||||
| 		Given a lvm id in one of the forms: | ||||
|  | ||||
| @@ -164,12 +148,12 @@ class Manager(AutomatedProperties): | ||||
| 		return the object path in O(1) time. | ||||
|  | ||||
| 		:param key: The lookup value | ||||
| 		:param cb:	dbus python call back parameter, not client visible | ||||
| 		:param cbe:	dbus python error call back parameter, not client visible | ||||
| 		:return: Return the object path.  If object not found you will get '/' | ||||
| 		""" | ||||
| 		r = RequestEntry(-1, Manager._lookup_by_lvm_id, (key,), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
| 		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key) | ||||
| 		if p: | ||||
| 			return p | ||||
| 		return '/' | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _use_lvm_shell(yes_no): | ||||
| @@ -185,33 +169,25 @@ class Manager(AutomatedProperties): | ||||
| 		:param yes_no: | ||||
| 		:param cb:	dbus python call back parameter, not client visible | ||||
| 		:param cbe:	dbus python error call back parameter, not client visible | ||||
| 		:return: Boolean | ||||
| 		:return: Nothing | ||||
| 		""" | ||||
| 		r = RequestEntry(-1, Manager._use_lvm_shell, (yes_no,), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _external_event(command): | ||||
| 		utils.log_debug("Processing _external_event= %s" % command, | ||||
| 							'bg_black', 'fg_orange') | ||||
| 		cfg.load() | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='s', out_signature='i') | ||||
| 	def ExternalEvent(self, command): | ||||
| 		utils.log_debug("ExternalEvent %s" % command) | ||||
|  | ||||
| 		# If a user didn't explicitly specify udev, we will turn it off now. | ||||
| 		if not cfg.args.use_udev: | ||||
| 			if udevwatch.remove(): | ||||
| 				utils.log_debug("ExternalEvent received, disabling " | ||||
| 								"udev monitoring") | ||||
| 				# We are dependent on external events now to stay current! | ||||
| 				cfg.got_external_event = True | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			-1, Manager._external_event, (command,), None, None, False) | ||||
| 		cfg.worker_q.put(r) | ||||
| 				cfg.ee = True | ||||
| 		utils.log_debug("ExternalEvent %s" % command) | ||||
| 		cfg.event() | ||||
| 		return dbus.Int32(0) | ||||
|  | ||||
| 	@staticmethod | ||||
|   | ||||
| @@ -32,12 +32,14 @@ class ObjectManager(AutomatedProperties): | ||||
| 		self._id_to_object_path = {} | ||||
| 		self.rlock = threading.RLock() | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _get_managed_objects(obj): | ||||
| 		with obj.rlock: | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		out_signature='a{oa{sa{sv}}}') | ||||
| 	def GetManagedObjects(self): | ||||
| 		with self.rlock: | ||||
| 			rc = {} | ||||
| 			try: | ||||
| 				for k, v in list(obj._objects.items()): | ||||
| 				for k, v in list(self._objects.items()): | ||||
| 					path, props = v[0].emit_data() | ||||
| 					rc[path] = props | ||||
| 			except Exception: | ||||
| @@ -45,14 +47,6 @@ class ObjectManager(AutomatedProperties): | ||||
| 				sys.exit(1) | ||||
| 			return rc | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		out_signature='a{oa{sa{sv}}}', async_callbacks=('cb', 'cbe')) | ||||
| 	def GetManagedObjects(self, cb, cbe): | ||||
| 		r = cfg.create_request_entry(-1, ObjectManager._get_managed_objects, | ||||
| 									(self, ), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	def locked(self): | ||||
| 		""" | ||||
| 		If some external code need to run across a number of different | ||||
| @@ -189,8 +183,8 @@ class ObjectManager(AutomatedProperties): | ||||
| 			path = dbus_object.dbus_object_path() | ||||
| 			interfaces = dbus_object.interface() | ||||
|  | ||||
| 			# print('UN-Registering object path %s for %s' % | ||||
| 			#		(path, dbus_object.lvm_id)) | ||||
| 			# print 'UN-Registering object path %s for %s' % \ | ||||
| 			#      (path, dbus_object.lvm_id) | ||||
|  | ||||
| 			self._lookup_remove(path) | ||||
|  | ||||
| @@ -223,9 +217,8 @@ class ObjectManager(AutomatedProperties): | ||||
| 		:param lvm_id: The lvm identifier | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			lookup_rc = self._id_lookup(lvm_id) | ||||
| 			if lookup_rc: | ||||
| 				return self.get_object_by_path(lookup_rc) | ||||
| 			if lvm_id in self._id_to_object_path: | ||||
| 				return self.get_object_by_path(self._id_to_object_path[lvm_id]) | ||||
| 			return None | ||||
|  | ||||
| 	def get_object_path_by_lvm_id(self, lvm_id): | ||||
| @@ -235,24 +228,43 @@ class ObjectManager(AutomatedProperties): | ||||
| 		:return: Object path or '/' if not found | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			lookup_rc = self._id_lookup(lvm_id) | ||||
| 			if lookup_rc: | ||||
| 				return lookup_rc | ||||
| 			if lvm_id in self._id_to_object_path: | ||||
| 				return self._id_to_object_path[lvm_id] | ||||
| 			return '/' | ||||
|  | ||||
| 	def _id_verify(self, path, uuid, lvm_id): | ||||
| 	def _uuid_verify(self, path, uuid, lvm_id): | ||||
| 		""" | ||||
| 		Ensure our lookups are correct | ||||
| 		Ensure uuid is present for a successful lvm_id lookup | ||||
| 		NOTE: Internal call, assumes under object manager lock | ||||
| 		:param path: 		Path to object we looked up | ||||
| 		:param uuid: 		uuid lookup | ||||
| 		:param lvm_id:		lvm_id lookup | ||||
| 		:param uuid: 		lvm uuid to verify | ||||
| 		:param lvm_id:		lvm_id used to find object | ||||
| 		:return: None | ||||
| 		""" | ||||
| 		# There is no durable non-changeable name in lvm | ||||
| 		# This gets called when we found an object based on lvm_id, ensure | ||||
| 		# uuid is correct too, as they can change. There is no durable | ||||
| 		# non-changeable name in lvm | ||||
| 		if lvm_id != uuid: | ||||
| 			obj = self.get_object_by_path(path) | ||||
| 			self._lookup_add(obj, path, lvm_id, uuid) | ||||
| 			if uuid and uuid not in self._id_to_object_path: | ||||
| 				obj = self.get_object_by_path(path) | ||||
| 				self._lookup_add(obj, path, lvm_id, uuid) | ||||
|  | ||||
| 	def _lvm_id_verify(self, path, uuid, lvm_id): | ||||
| 		""" | ||||
| 		Ensure lvm_id is present for a successful uuid lookup | ||||
| 		NOTE: Internal call, assumes under object manager lock | ||||
| 		:param path: 		Path to object we looked up | ||||
| 		:param uuid: 		uuid used to find object | ||||
| 		:param lvm_id:		lvm_id to verify | ||||
| 		:return: None | ||||
| 		""" | ||||
| 		# This gets called when we found an object based on uuid, ensure | ||||
| 		# lvm_id is correct too, as they can change.  There is no durable | ||||
| 		# non-changeable name in lvm | ||||
| 		if lvm_id != uuid: | ||||
| 			if lvm_id and lvm_id not in self._id_to_object_path: | ||||
| 				obj = self.get_object_by_path(path) | ||||
| 				self._lookup_add(obj, path, lvm_id, uuid) | ||||
|  | ||||
| 	def _id_lookup(self, the_id): | ||||
| 		path = None | ||||
| @@ -319,22 +331,22 @@ class ObjectManager(AutomatedProperties): | ||||
| 				# Lets check for the uuid first | ||||
| 				path = self._id_lookup(uuid) | ||||
| 				if path: | ||||
| 					# Ensure table lookups are correct | ||||
| 					self._id_verify(path, uuid, lvm_id) | ||||
| 					# Verify the lvm_id is sane | ||||
| 					self._lvm_id_verify(path, uuid, lvm_id) | ||||
| 				else: | ||||
| 					# Unable to find by UUID, lets lookup by lvm_id | ||||
| 					path = self._id_lookup(lvm_id) | ||||
| 					if path: | ||||
| 						# Ensure table lookups are correct | ||||
| 						self._id_verify(path, uuid, lvm_id) | ||||
| 						# Verify the uuid is sane | ||||
| 						self._uuid_verify(path, uuid, lvm_id) | ||||
| 					else: | ||||
| 						# We have exhausted all lookups, let's create if we can | ||||
| 						if path_create: | ||||
| 							path = path_create() | ||||
| 							self._lookup_add(None, path, lvm_id, uuid) | ||||
|  | ||||
| 			# print('get_object_path_by_lvm_id(%s, %s, %s): return %s' % | ||||
| 			#	(uuid, lvm_id, str(path_create), path)) | ||||
| 			# print('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' % | ||||
| 			# 	   (uuid, lvm_id, str(path_create), str(gen_new), path)) | ||||
|  | ||||
| 			return path | ||||
|  | ||||
|   | ||||
| @@ -79,9 +79,7 @@ class PvState(State): | ||||
|  | ||||
| 		self.lv = self._lv_object_list(vg_name) | ||||
|  | ||||
| 		# It's possible to have a vg_name and no uuid with the main example | ||||
| 		# being when the vg_name == '[unknown]' | ||||
| 		if vg_uuid and vg_name: | ||||
| 		if vg_name: | ||||
| 			self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				vg_uuid, vg_name, vg_obj_path_generate) | ||||
| 		else: | ||||
|   | ||||
| @@ -13,7 +13,7 @@ from gi.repository import GLib | ||||
| from .job import Job | ||||
| from . import cfg | ||||
| import traceback | ||||
| from .utils import log_error, mt_async_call | ||||
| from .utils import log_error, mt_async_result | ||||
|  | ||||
|  | ||||
| class RequestEntry(object): | ||||
| @@ -116,9 +116,9 @@ class RequestEntry(object): | ||||
| 				if error_rc == 0: | ||||
| 					if self.cb: | ||||
| 						if self._return_tuple: | ||||
| 							mt_async_call(self.cb, (result, '/')) | ||||
| 							mt_async_result(self.cb, (result, '/')) | ||||
| 						else: | ||||
| 							mt_async_call(self.cb, result) | ||||
| 							mt_async_result(self.cb, result) | ||||
| 				else: | ||||
| 					if self.cb_error: | ||||
| 						if not error_exception: | ||||
| @@ -129,7 +129,7 @@ class RequestEntry(object): | ||||
| 							else: | ||||
| 								error_exception = Exception(error_msg) | ||||
|  | ||||
| 						mt_async_call(self.cb_error, error_exception) | ||||
| 						mt_async_result(self.cb_error, error_exception) | ||||
| 			else: | ||||
| 				# We have a job and it's complete, indicate that it's done. | ||||
| 				self._job.Complete = True | ||||
|   | ||||
| @@ -10,41 +10,10 @@ | ||||
| import pyudev | ||||
| import threading | ||||
| from . import cfg | ||||
| from .request import RequestEntry | ||||
| from . import utils | ||||
|  | ||||
| observer = None | ||||
| observer_lock = threading.RLock() | ||||
|  | ||||
| _udev_lock = threading.RLock() | ||||
| _udev_count = 0 | ||||
|  | ||||
|  | ||||
| def udev_add(): | ||||
| 	global _udev_count | ||||
| 	with _udev_lock: | ||||
| 		if _udev_count == 0: | ||||
| 			_udev_count += 1 | ||||
|  | ||||
| 			# Place this on the queue so any other operations will sequence | ||||
| 			# behind it | ||||
| 			r = RequestEntry( | ||||
| 				-1, _udev_event, (), None, None, False) | ||||
| 			cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| def udev_complete(): | ||||
| 	global _udev_count | ||||
| 	with _udev_lock: | ||||
| 		if _udev_count > 0: | ||||
| 			_udev_count -= 1 | ||||
|  | ||||
|  | ||||
| def _udev_event(): | ||||
| 	utils.log_debug("Processing udev event") | ||||
| 	udev_complete() | ||||
| 	cfg.load() | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def filter_event(action, device): | ||||
| @@ -68,7 +37,7 @@ def filter_event(action, device): | ||||
| 		refresh = True | ||||
|  | ||||
| 	if refresh: | ||||
| 		udev_add() | ||||
| 		cfg.event() | ||||
|  | ||||
|  | ||||
| def add(): | ||||
|   | ||||
| @@ -20,8 +20,7 @@ from lvmdbusd import cfg | ||||
| # noinspection PyUnresolvedReferences | ||||
| from gi.repository import GLib | ||||
| import threading | ||||
| import traceback | ||||
| import signal | ||||
|  | ||||
|  | ||||
| STDOUT_TTY = os.isatty(sys.stdout.fileno()) | ||||
|  | ||||
| @@ -282,47 +281,12 @@ def log_error(msg, *attributes): | ||||
| 	_common_log(msg, *attributes) | ||||
|  | ||||
|  | ||||
| def dump_threads_stackframe(): | ||||
| 	ident_to_name = {} | ||||
|  | ||||
| 	for thread_object in threading.enumerate(): | ||||
| 		ident_to_name[thread_object.ident] = thread_object | ||||
|  | ||||
| 	stacks = [] | ||||
| 	for thread_ident, frame in sys._current_frames().items(): | ||||
| 		stack = traceback.format_list(traceback.extract_stack(frame)) | ||||
|  | ||||
| 		# There is a possibility that a thread gets created after we have | ||||
| 		# enumerated all threads, so this lookup table may be incomplete, so | ||||
| 		# account for this | ||||
| 		if thread_ident in ident_to_name: | ||||
| 			thread_name = ident_to_name[thread_ident].name | ||||
| 		else: | ||||
| 			thread_name = "unknown" | ||||
|  | ||||
| 		stacks.append("Thread: %s" % (thread_name)) | ||||
| 		stacks.append("".join(stack)) | ||||
|  | ||||
| 	log_error("Dumping thread stack frames!\n" + "\n".join(stacks)) | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def handler(signum): | ||||
| 	try: | ||||
| 		if signum == signal.SIGUSR1: | ||||
| 			dump_threads_stackframe() | ||||
| 		else: | ||||
| 			cfg.run.value = 0 | ||||
| 			log_debug('Exiting daemon with signal %d' % signum) | ||||
| 			if cfg.loop is not None: | ||||
| 				cfg.loop.quit() | ||||
| 	except: | ||||
| 		st = traceback.format_exc() | ||||
| 		log_error("signal handler: exception (logged, not reported!) \n %s" % st) | ||||
|  | ||||
| 	# It's important we report that we handled the exception for the exception | ||||
| 	# handler to continue to work, especially for signal 10 (SIGUSR1) | ||||
| 	return True | ||||
| def handler(signum, frame): | ||||
| 	cfg.run.value = 0 | ||||
| 	log_debug('Signal handler called with signal %d' % signum) | ||||
| 	if cfg.loop is not None: | ||||
| 		cfg.loop.quit() | ||||
|  | ||||
|  | ||||
| def pv_obj_path_generate(): | ||||
| @@ -535,62 +499,27 @@ def validate_tag(interface, tag): | ||||
| 			% (tag, _ALLOWABLE_TAG_CH)) | ||||
|  | ||||
|  | ||||
| def add_no_notify(cmdline): | ||||
| 	""" | ||||
| 	Given a command line to execute we will see if `--config` is present, if it | ||||
| 	is we will add the global/notify_dbus=0 to it, otherwise we will append it | ||||
| 	to the end of the list. | ||||
| 	:param: cmdline: The command line to inspect | ||||
| 	:type: cmdline: list | ||||
| 	:return: cmdline with notify_dbus config option present | ||||
| 	:rtype: list | ||||
| 	""" | ||||
|  | ||||
| 	# Only after we have seen an external event will be disable lvm from sending | ||||
| 	# us one when we call lvm | ||||
| 	if cfg.got_external_event: | ||||
| 		if 'help' in cmdline: | ||||
| 			return cmdline | ||||
|  | ||||
| 		if '--config' in cmdline: | ||||
| 			for i, arg in enumerate(cmdline): | ||||
| 				if arg == '--config': | ||||
| 					if len(cmdline) <= i+1: | ||||
| 						raise dbus.exceptions.DBusException("Missing value for --config option.") | ||||
| 					cmdline[i+1] += " global/notify_dbus=0" | ||||
| 					break | ||||
| 		else: | ||||
| 			cmdline.extend(['--config', 'global/notify_dbus=0']) | ||||
| 	return cmdline | ||||
|  | ||||
|  | ||||
| # The methods below which start with mt_* are used to execute the desired code | ||||
| # on the the main thread of execution to alleviate any issues the dbus-python | ||||
| # library with regards to multi-threaded access.  Essentially, we are trying to | ||||
| # ensure all dbus library interaction is done from the same thread! | ||||
|  | ||||
|  | ||||
| def _async_handler(call_back, parameters): | ||||
| 	params_str = ", ".join(str(x) for x in parameters) | ||||
| 	log_debug('Main thread execution, callback = %s, parameters = (%s)' % | ||||
| 				(str(call_back), params_str)) | ||||
|  | ||||
| 	try: | ||||
| 		if parameters: | ||||
| 			call_back(*parameters) | ||||
| 		else: | ||||
| 			call_back() | ||||
| 	except: | ||||
| 		st = traceback.format_exc() | ||||
| 		log_error("mt_async_call: exception (logged, not reported!) \n %s" % st) | ||||
| def _async_result(call_back, results): | ||||
| 	log_debug('Results = %s' % str(results)) | ||||
| 	call_back(results) | ||||
|  | ||||
|  | ||||
| # Execute the function on the main thread with the provided parameters, do | ||||
| # not return *any* value or wait for the execution to complete! | ||||
| def mt_async_call(function_call_back, *parameters): | ||||
| 	GLib.idle_add(_async_handler, function_call_back, parameters) | ||||
| # Return result in main thread | ||||
| def mt_async_result(call_back, results): | ||||
| 	GLib.idle_add(_async_result, call_back, results) | ||||
|  | ||||
|  | ||||
| # Take the supplied function and run it on the main thread and not wait for | ||||
| # a result! | ||||
| def mt_run_no_wait(function, param): | ||||
| 	GLib.idle_add(function, param) | ||||
|  | ||||
| # Run the supplied function and arguments on the main thread and wait for them | ||||
| # to complete while allowing the ability to get the return value too. | ||||
| # | ||||
| @@ -610,7 +539,6 @@ class MThreadRunner(object): | ||||
| 	def __init__(self, function, *args): | ||||
| 		self.f = function | ||||
| 		self.rc = None | ||||
| 		self.exception = None | ||||
| 		self.args = args | ||||
| 		self.function_complete = False | ||||
| 		self.cond = threading.Condition(threading.Lock()) | ||||
| @@ -620,21 +548,13 @@ class MThreadRunner(object): | ||||
| 		with self.cond: | ||||
| 			if not self.function_complete: | ||||
| 				self.cond.wait() | ||||
| 		if self.exception: | ||||
| 			raise self.exception | ||||
| 		return self.rc | ||||
|  | ||||
| 	def _run(self): | ||||
| 		try: | ||||
| 			if self.args: | ||||
| 				self.rc = self.f(*self.args) | ||||
| 			else: | ||||
| 				self.rc = self.f() | ||||
| 		except BaseException as be: | ||||
| 			self.exception = be | ||||
| 			st = traceback.format_exc() | ||||
| 			log_error("MThreadRunner: exception \n %s" % st) | ||||
| 			log_error("Exception will be raised in calling thread!") | ||||
| 		if len(self.args): | ||||
| 			self.rc = self.f(*self.args) | ||||
| 		else: | ||||
| 			self.rc = self.f() | ||||
|  | ||||
|  | ||||
| def _remove_objects(dbus_objects_rm): | ||||
|   | ||||
| @@ -52,23 +52,18 @@ def load_vgs(vg_specific=None, object_path=None, refresh=False, | ||||
|  | ||||
| # noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal | ||||
| class VgState(State): | ||||
|  | ||||
| 	@property | ||||
| 	def internal_name(self): | ||||
| 		return self.Name | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return self.internal_name | ||||
| 		return self.Name | ||||
|  | ||||
| 	def identifiers(self): | ||||
| 		return (self.Uuid, self.internal_name) | ||||
| 		return (self.Uuid, self.Name) | ||||
|  | ||||
| 	def _lv_paths_build(self): | ||||
| 		rc = [] | ||||
| 		for lv in cfg.db.lvs_in_vg(self.Uuid): | ||||
| 			(lv_name, meta, lv_uuid) = lv | ||||
| 			full_name = "%s/%s" % (self.internal_name, lv_name) | ||||
| 			full_name = "%s/%s" % (self.Name, lv_name) | ||||
|  | ||||
| 			gen = utils.lv_object_path_method(lv_name, meta) | ||||
|  | ||||
| @@ -97,7 +92,7 @@ class VgState(State): | ||||
| 	def create_dbus_object(self, path): | ||||
| 		if not path: | ||||
| 			path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				self.Uuid, self.internal_name, vg_obj_path_generate) | ||||
| 				self.Uuid, self.Name, vg_obj_path_generate) | ||||
| 		return Vg(path, self) | ||||
|  | ||||
| 	# noinspection PyMethodMayBeStatic | ||||
| @@ -107,6 +102,7 @@ class VgState(State): | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| @utils.dbus_property(VG_INTERFACE, 'Uuid', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'Name', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'Fmt', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'SizeBytes', 't', 0) | ||||
| @utils.dbus_property(VG_INTERFACE, 'FreeBytes', 't', 0) | ||||
| @@ -139,7 +135,6 @@ class Vg(AutomatedProperties): | ||||
| 	_AllocNormal_meta = ('b', VG_INTERFACE) | ||||
| 	_AllocAnywhere_meta = ('b', VG_INTERFACE) | ||||
| 	_Clustered_meta = ('b', VG_INTERFACE) | ||||
| 	_Name_meta = ('s', VG_INTERFACE) | ||||
|  | ||||
| 	# noinspection PyUnusedLocal,PyPep8Naming | ||||
| 	def __init__(self, object_path, object_state): | ||||
| @@ -177,7 +172,7 @@ class Vg(AutomatedProperties): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.vg_rename( | ||||
| 			uuid, new_name, rename_options) | ||||
| 			vg_name, new_name, rename_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| @@ -221,7 +216,7 @@ class Vg(AutomatedProperties): | ||||
| 	# TODO: This should be broken into a number of different methods | ||||
| 	# instead of having one method that takes a hash for parameters.  Some of | ||||
| 	# the changes that vgchange does works on entire system, not just a | ||||
| 	# specific vg, thus that should be in the Manager interface. | ||||
| 	# specfic vg, thus that should be in the Manager interface. | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| @@ -734,12 +729,6 @@ class Vg(AutomatedProperties): | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@property | ||||
| 	def Name(self): | ||||
| 		if ':' in self.state.Name: | ||||
| 			return self.state.Name.split(':')[0] | ||||
| 		return self.state.Name | ||||
|  | ||||
| 	@property | ||||
| 	def Tags(self): | ||||
| 		return utils.parse_tags(self.state.tags) | ||||
|   | ||||
							
								
								
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| lvmetad | ||||
| lvmetactl | ||||
							
								
								
									
										65
									
								
								daemons/lvmetad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								daemons/lvmetad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| # | ||||
| # Copyright (C) 2011-2012 Red Hat, Inc. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| # This copyrighted material is made available to anyone wishing to use, | ||||
| # modify, copy, or redistribute it subject to the terms and conditions | ||||
| # of the GNU Lesser General Public License v.2.1. | ||||
| # | ||||
| # You should have received a copy of the GNU Lesser 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 | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| SOURCES = lvmetad-core.c | ||||
| SOURCES2 = testclient.c | ||||
|  | ||||
| TARGETS = lvmetad lvmetactl | ||||
|  | ||||
| .PHONY: install_lvmetad | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
| CFLOW_TARGET = lvmetad | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper | ||||
|  | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) | ||||
| CLDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
| CFLAGS += $(EXTRA_EXEC_CFLAGS) | ||||
|  | ||||
| lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS) | ||||
|  | ||||
| lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 	$(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LVMLIBS) | ||||
|  | ||||
| CLEAN_TARGETS += lvmetactl.o | ||||
|  | ||||
| # TODO: No idea. No idea how to test either. | ||||
| #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_lvmetad: lvmetad | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|  | ||||
| install_lvm2: install_lvmetad | ||||
|  | ||||
| install: install_lvm2 | ||||
							
								
								
									
										249
									
								
								daemons/lvmetad/lvmetactl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								daemons/lvmetad/lvmetactl.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone 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 "tool.h" | ||||
|  | ||||
| #include "lvmetad-client.h" | ||||
|  | ||||
| daemon_handle h; | ||||
|  | ||||
| static void print_reply(daemon_reply reply) | ||||
| { | ||||
| 	const char *a = daemon_reply_str(reply, "response", NULL); | ||||
| 	const char *b = daemon_reply_str(reply, "status", NULL); | ||||
| 	const char *c = daemon_reply_str(reply, "reason", NULL); | ||||
|  | ||||
| 	printf("response \"%s\" status \"%s\" reason \"%s\"\n", | ||||
| 	       a ? a : "", b ? b : "", c ? c : ""); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
| 	daemon_reply reply; | ||||
| 	char *cmd; | ||||
| 	char *uuid; | ||||
| 	char *name; | ||||
| 	int val; | ||||
| 	int ver; | ||||
|  | ||||
| 	if (argc < 2) { | ||||
| 		printf("lvmetactl dump\n"); | ||||
| 		printf("lvmetactl pv_list\n"); | ||||
| 		printf("lvmetactl vg_list\n"); | ||||
| 		printf("lvmetactl get_global_info\n"); | ||||
| 		printf("lvmetactl vg_lookup_name <name>\n"); | ||||
| 		printf("lvmetactl vg_lookup_uuid <uuid>\n"); | ||||
| 		printf("lvmetactl pv_lookup_uuid <uuid>\n"); | ||||
| 		printf("lvmetactl set_global_invalid 0|1\n"); | ||||
| 		printf("lvmetactl set_global_disable 0|1\n"); | ||||
| 		printf("lvmetactl set_vg_version <uuid> <name> <version>\n"); | ||||
| 		printf("lvmetactl vg_lock_type <uuid>\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	cmd = argv[1]; | ||||
|  | ||||
| 	h = lvmetad_open(NULL); | ||||
|  | ||||
| 	if (!strcmp(cmd, "dump")) { | ||||
| 		reply = daemon_send_simple(h, "dump", | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "pv_list")) { | ||||
| 		reply = daemon_send_simple(h, "pv_list", | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_list")) { | ||||
| 		reply = daemon_send_simple(h, "vg_list", | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "get_global_info")) { | ||||
| 		reply = daemon_send_simple(h, "get_global_info", | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "set_global_invalid")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("set_global_invalid 0|1\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		val = atoi(argv[2]); | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "set_global_info", | ||||
| 					   "global_invalid = " FMTd64, (int64_t) val, | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		print_reply(reply); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "set_global_disable")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("set_global_disable 0|1\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		val = atoi(argv[2]); | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "set_global_info", | ||||
| 					   "global_disable = " FMTd64, (int64_t) val, | ||||
| 					   "disable_reason = %s", LVMETAD_DISABLE_REASON_DIRECT, | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		print_reply(reply); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "set_vg_version")) { | ||||
| 		if (argc < 5) { | ||||
| 			printf("set_vg_version <uuid> <name> <ver>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
| 		name = argv[3]; | ||||
| 		ver = atoi(argv[4]); | ||||
|  | ||||
| 		if ((strlen(uuid) == 1) && (uuid[0] == '-')) | ||||
| 			uuid = NULL; | ||||
| 		if ((strlen(name) == 1) && (name[0] == '-')) | ||||
| 			name = NULL; | ||||
|  | ||||
| 		if (uuid && name) { | ||||
| 			reply = daemon_send_simple(h, "set_vg_info", | ||||
| 						   "uuid = %s", uuid, | ||||
| 						   "name = %s", name, | ||||
| 						   "version = " FMTd64, (int64_t) ver, | ||||
| 						   "token = %s", "skip", | ||||
| 						   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 						   "cmd = %s", "lvmetactl", | ||||
| 						   NULL); | ||||
| 		} else if (uuid) { | ||||
| 			reply = daemon_send_simple(h, "set_vg_info", | ||||
| 						   "uuid = %s", uuid, | ||||
| 						   "version = " FMTd64, (int64_t) ver, | ||||
| 						   "token = %s", "skip", | ||||
| 						   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 						   "cmd = %s", "lvmetactl", | ||||
| 						   NULL); | ||||
| 		} else if (name) { | ||||
| 			reply = daemon_send_simple(h, "set_vg_info", | ||||
| 						   "name = %s", name, | ||||
| 						   "version = " FMTd64, (int64_t) ver, | ||||
| 						   "token = %s", "skip", | ||||
| 						   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 						   "cmd = %s", "lvmetactl", | ||||
| 						   NULL); | ||||
| 		} else { | ||||
| 			printf("name or uuid required\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		print_reply(reply); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_lookup_name")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("vg_lookup_name <name>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		name = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "vg_lookup", | ||||
| 					   "name = %s", name, | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_lookup_uuid")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("vg_lookup_uuid <uuid>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "vg_lookup", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "vg_lock_type")) { | ||||
| 		struct dm_config_node *metadata; | ||||
| 		const char *lock_type; | ||||
|  | ||||
| 		if (argc < 3) { | ||||
| 			printf("vg_lock_type <uuid>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "vg_lookup", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		/* printf("%s\n", reply.buffer.mem); */ | ||||
|  | ||||
| 		metadata = dm_config_find_node(reply.cft->root, "metadata"); | ||||
| 		if (!metadata) { | ||||
| 			printf("no metadata\n"); | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		lock_type = dm_config_find_str(metadata, "metadata/lock_type", NULL); | ||||
| 		if (!lock_type) { | ||||
| 			printf("no lock_type\n"); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		printf("lock_type %s\n", lock_type); | ||||
|  | ||||
| 	} else if (!strcmp(cmd, "pv_lookup_uuid")) { | ||||
| 		if (argc < 3) { | ||||
| 			printf("pv_lookup_uuid <uuid>\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		uuid = argv[2]; | ||||
|  | ||||
| 		reply = daemon_send_simple(h, "pv_lookup", | ||||
| 					   "uuid = %s", uuid, | ||||
| 					   "token = %s", "skip", | ||||
| 					   "pid = " FMTd64, (int64_t)getpid(), | ||||
| 					   "cmd = %s", "lvmetactl", | ||||
| 					   NULL); | ||||
| 		printf("%s\n", reply.buffer.mem); | ||||
|  | ||||
| 	} else { | ||||
| 		printf("unknown command\n"); | ||||
| 		goto out_close; | ||||
| 	} | ||||
| out: | ||||
| 	daemon_reply_destroy(reply); | ||||
| out_close: | ||||
| 	daemon_close(h); | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										90
									
								
								daemons/lvmetad/lvmetad-client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								daemons/lvmetad/lvmetad-client.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-2012 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This copyrighted material is made available to anyone wishing to use, | ||||
|  * modify, copy, or redistribute it subject to the terms and conditions | ||||
|  * of the GNU Lesser General Public License v.2.1. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser 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 | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_LVMETAD_CLIENT_H | ||||
| #define _LVM_LVMETAD_CLIENT_H | ||||
|  | ||||
| #include "daemon-client.h" | ||||
|  | ||||
| #define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket" | ||||
|  | ||||
| #define LVMETAD_TOKEN_UPDATE_IN_PROGRESS "update in progress" | ||||
|  | ||||
| #define LVMETAD_DISABLE_REASON_DIRECT		"DIRECT" | ||||
| #define LVMETAD_DISABLE_REASON_LVM1		"LVM1" | ||||
| #define LVMETAD_DISABLE_REASON_DUPLICATES	"DUPLICATES" | ||||
| #define LVMETAD_DISABLE_REASON_VGRESTORE	"VGRESTORE" | ||||
|  | ||||
| struct volume_group; | ||||
|  | ||||
| /* Different types of replies we may get from lvmetad. */ | ||||
|  | ||||
| typedef struct { | ||||
| 	daemon_reply r; | ||||
| 	const char **uuids; /* NULL terminated array */ | ||||
| } lvmetad_uuidlist; | ||||
|  | ||||
| typedef struct { | ||||
| 	daemon_reply r; | ||||
| 	struct dm_config_tree *cft; | ||||
| } lvmetad_vg; | ||||
|  | ||||
| /* Get a list of VG UUIDs that match a given VG name. */ | ||||
| lvmetad_uuidlist lvmetad_lookup_vgname(daemon_handle h, const char *name); | ||||
|  | ||||
| /* Get the metadata of a single VG, identified by UUID. */ | ||||
| lvmetad_vg lvmetad_get_vg(daemon_handle h, const char *uuid); | ||||
|  | ||||
| /* | ||||
|  * Add and remove PVs on demand. Udev-driven systems will use this interface | ||||
|  * instead of scanning. | ||||
|  */ | ||||
| daemon_reply lvmetad_add_pv(daemon_handle h, const char *pv_uuid, const char *mda_content); | ||||
| daemon_reply lvmetad_remove_pv(daemon_handle h, const char *pv_uuid); | ||||
|  | ||||
| /* Trigger a full disk scan, throwing away all caches. XXX do we eventually want | ||||
|  * this? Probably not yet, anyway. | ||||
|  *     daemon_reply lvmetad_rescan(daemon_handle h); | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Update the version of metadata of a volume group. The VG has to be locked for | ||||
|  * writing for this, and the VG metadata here has to match whatever has been | ||||
|  * written to the disk (under this lock). This initially avoids the requirement | ||||
|  * for lvmetad to write to disk (in later revisions, lvmetad_supersede_vg may | ||||
|  * also do the writing, or we probably add another function to do that). | ||||
|  */ | ||||
| daemon_reply lvmetad_supersede_vg(daemon_handle h, struct volume_group *vg); | ||||
|  | ||||
| /* Wrappers to open/close connection */ | ||||
|  | ||||
| static inline daemon_handle lvmetad_open(const char *socket) | ||||
| { | ||||
| 	daemon_info lvmetad_info = { | ||||
| 		.path = "lvmetad", | ||||
| 		.socket = socket ?: LVMETAD_SOCKET, | ||||
| 		.protocol = "lvmetad", | ||||
| 		.protocol_version = 1, | ||||
| 		.autostart = 0 | ||||
| 	}; | ||||
|  | ||||
| 	return daemon_open(lvmetad_info); | ||||
| } | ||||
|  | ||||
| static inline void lvmetad_close(daemon_handle h) | ||||
| { | ||||
| 	return daemon_close(h); | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										3012
									
								
								daemons/lvmetad/lvmetad-core.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3012
									
								
								daemons/lvmetad/lvmetad-core.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										16
									
								
								daemons/lvmetad/test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								daemons/lvmetad/test.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| export LD_LIBRARY_PATH="$1" | ||||
|  | ||||
| test -n "$2" && { | ||||
|     rm -f /var/run/lvmetad.{socket,pid} | ||||
|     chmod +rx lvmetad | ||||
|     valgrind ./lvmetad -f & | ||||
|     PID=$! | ||||
|     sleep 1 | ||||
|     ./testclient | ||||
|     kill $PID | ||||
|     exit 0 | ||||
| } | ||||
|  | ||||
| sudo ./test.sh "$1" . | ||||
							
								
								
									
										147
									
								
								daemons/lvmetad/testclient.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								daemons/lvmetad/testclient.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-2014 Red Hat, Inc. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * This 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
| #include "tool.h" | ||||
|  | ||||
| #include "lvmetad-client.h" | ||||
| #include "label.h" | ||||
| #include "lvmcache.h" | ||||
| #include "metadata.h" | ||||
|  | ||||
| const char *uuid1 = "abcd-efgh"; | ||||
| const char *uuid2 = "bbcd-efgh"; | ||||
| const char *vgid = "yada-yada"; | ||||
| const char *uuid3 = "cbcd-efgh"; | ||||
|  | ||||
| const char *metadata2 = "{\n" | ||||
| 	"id = \"yada-yada\"\n" | ||||
| 	"seqno = 15\n" | ||||
| 	"status = [\"READ\", \"WRITE\"]\n" | ||||
| 	"flags = []\n" | ||||
| 	"extent_size = 8192\n" | ||||
| 	"physical_volumes {\n" | ||||
| 	"    pv0 {\n" | ||||
| 	"        id = \"abcd-efgh\"\n" | ||||
| 	"    }\n" | ||||
| 	"    pv1 {\n" | ||||
| 	"        id = \"bbcd-efgh\"\n" | ||||
| 	"    }\n" | ||||
| 	"    pv2 {\n" | ||||
| 	"        id = \"cbcd-efgh\"\n" | ||||
| 	"    }\n" | ||||
| 	"}\n" | ||||
| 	"}\n"; | ||||
|  | ||||
| void _handle_reply(daemon_reply reply) { | ||||
| 	const char *repl = daemon_reply_str(reply, "response", NULL); | ||||
| 	const char *status = daemon_reply_str(reply, "status", NULL); | ||||
| 	const char *vgid = daemon_reply_str(reply, "vgid", NULL); | ||||
|  | ||||
| 	fprintf(stderr, "[C] REPLY: %s\n", repl); | ||||
| 	if (!strcmp(repl, "failed")) | ||||
| 		fprintf(stderr, "[C] REASON: %s\n", daemon_reply_str(reply, "reason", "unknown")); | ||||
| 	if (vgid) | ||||
| 		fprintf(stderr, "[C] VGID: %s\n", vgid); | ||||
| 	if (status) | ||||
| 		fprintf(stderr, "[C] STATUS: %s\n", status); | ||||
| 	daemon_reply_destroy(reply); | ||||
| } | ||||
|  | ||||
| void _pv_add(daemon_handle h, const char *uuid, const char *metadata) | ||||
| { | ||||
| 	daemon_reply reply = daemon_send_simple(h, "pv_add", "uuid = %s", uuid, | ||||
| 						             "metadata = %b", metadata, | ||||
| 						             NULL); | ||||
| 	_handle_reply(reply); | ||||
| } | ||||
|  | ||||
| int scan(daemon_handle h, char *fn) { | ||||
| 	struct device *dev = dev_cache_get(fn, NULL); | ||||
|  | ||||
| 	struct label *label; | ||||
| 	if (!label_read(dev, &label, 0)) { | ||||
| 		fprintf(stderr, "[C] no label found on %s\n", fn); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	char uuid[64]; | ||||
| 	if (!id_write_format(dev->pvid, uuid, 64)) { | ||||
| 		fprintf(stderr, "[C] Failed to format PV UUID for %s", dev_name(dev)); | ||||
| 		return; | ||||
| 	} | ||||
| 	fprintf(stderr, "[C] found PV: %s\n", uuid); | ||||
| 	struct lvmcache_info *info = (struct lvmcache_info *) label->info; | ||||
| 	struct physical_volume pv = { 0, }; | ||||
|  | ||||
| 	if (!(info->fmt->ops->pv_read(info->fmt, dev_name(dev), &pv, 0))) { | ||||
| 		fprintf(stderr, "[C] Failed to read PV %s", dev_name(dev)); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	struct format_instance_ctx fic; | ||||
| 	struct format_instance *fid = info->fmt->ops->create_instance(info->fmt, &fic); | ||||
| 	struct metadata_area *mda; | ||||
| 	struct volume_group *vg = NULL; | ||||
| 	dm_list_iterate_items(mda, &info->mdas) { | ||||
| 		struct volume_group *this = mda->ops->vg_read(fid, "", mda); | ||||
| 		if (this && !vg || this->seqno > vg->seqno) | ||||
| 			vg = this; | ||||
| 	} | ||||
| 	if (vg) { | ||||
| 		char *buf = NULL; | ||||
| 		/* TODO. This is not entirely correct, since export_vg_to_buffer | ||||
| 		 * adds trailing garbage to the buffer. We may need to use | ||||
| 		 * export_vg_to_config_tree and format the buffer ourselves. It | ||||
| 		 * does, however, work for now, since the garbage is well | ||||
| 		 * formatted and has no conflicting keys with the rest of the | ||||
| 		 * request.  */ | ||||
| 		export_vg_to_buffer(vg, &buf); | ||||
| 		daemon_reply reply = | ||||
| 			daemon_send_simple(h, "pv_add", "uuid = %s", uuid, | ||||
| 					      "metadata = %b", strchr(buf, '{'), | ||||
| 					      NULL); | ||||
| 		_handle_reply(reply); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void _dump_vg(daemon_handle h, const char *uuid) | ||||
| { | ||||
| 	daemon_reply reply = daemon_send_simple(h, "vg_by_uuid", "uuid = %s", uuid, NULL); | ||||
| 	fprintf(stderr, "[C] reply buffer: %s\n", reply.buffer); | ||||
| 	daemon_reply_destroy(reply); | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
| 	daemon_handle h = lvmetad_open(); | ||||
| 	/* FIXME Missing error path */ | ||||
|  | ||||
| 	if (argc > 1) { | ||||
| 		int i; | ||||
| 		struct cmd_context *cmd = create_toolcontext(0, NULL, 0, 0, 1, 1); | ||||
| 		for (i = 1; i < argc; ++i) { | ||||
| 			const char *uuid = NULL; | ||||
| 			scan(h, argv[i]); | ||||
| 		} | ||||
| 		destroy_toolcontext(cmd); | ||||
| 		/* FIXME Missing lvmetad_close() */ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	_pv_add(h, uuid1, NULL); | ||||
| 	_pv_add(h, uuid2, metadata2); | ||||
| 	_dump_vg(h, vgid); | ||||
| 	_pv_add(h, uuid3, NULL); | ||||
|  | ||||
| 	daemon_close(h);	/* FIXME lvmetad_close? */ | ||||
| 	return 0; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user