mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			dev-dct-pv
			...
			dev-lvmguy
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | adcaf772f7 | 
							
								
								
									
										117
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| *.5 | ||||
| *.7 | ||||
| *.8 | ||||
| *.8_gen | ||||
| *.a | ||||
| *.d | ||||
| *.o | ||||
| @@ -25,123 +24,9 @@ make.tmpl | ||||
|  | ||||
| /autom4te.cache/ | ||||
| /autoscan.log | ||||
| /build/ | ||||
| /config.cache | ||||
| /config.log | ||||
| /config.status | ||||
| /configure.scan | ||||
| /cscope.* | ||||
| /html/ | ||||
| /python/ | ||||
| /reports/ | ||||
| /cscope.out | ||||
| /tags | ||||
| /tmp/ | ||||
|  | ||||
| coverity/coverity_model.xml | ||||
|  | ||||
| # gcov files: | ||||
| *.gcda | ||||
| *.gcno | ||||
|  | ||||
| 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. | ||||
							
								
								
									
										132
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								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,30 +52,24 @@ 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 | ||||
| device-mapper: tools.device-mapper daemons.device-mapper man.device-mapper | ||||
| device_mapper: device-mapper | ||||
|  | ||||
| ifeq ("@INTL@", "yes") | ||||
| lib.pofile: include.pofile | ||||
| @@ -76,6 +79,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 | ||||
| @@ -90,7 +97,7 @@ 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 | ||||
| @@ -117,11 +124,11 @@ rpm: dist | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES | ||||
| 	DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\ | ||||
| 	GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\ | ||||
| 	$(SED) -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \ | ||||
| 	sed -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \ | ||||
| 	    -e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \ | ||||
| 	    -e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \ | ||||
| 	    $(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc | ||||
| 	V=$(V) rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec | ||||
| 	rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec | ||||
|  | ||||
| generate: conf.generate man.generate | ||||
| 	$(MAKE) -C conf generate | ||||
| @@ -139,7 +146,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: | ||||
| @@ -152,36 +159,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 | ||||
|  | ||||
| help: | ||||
| 	@echo -e "\nAvailable targets:" | ||||
| 	@echo "  all			Default target." | ||||
| 	@echo "  all_man		Build all man pages with generators." | ||||
| 	@echo "  clean			Remove all compile files." | ||||
| 	@echo "  device-mapper		Device mapper part of lvm2." | ||||
| 	@echo "  dist			Generate distributable file." | ||||
| 	@echo "  distclean		Remove all build files." | ||||
| 	@echo "  generate		Generate man pages for sources." | ||||
| 	@echo "  help			Display callable targets." | ||||
| 	@echo "  install		Install all files." | ||||
| 	@echo "  install_all_man	Install all man pages." | ||||
| 	@echo "  install_cluster	Install cmirrord." | ||||
| 	@echo "  install_device-mapper	Install device mapper files." | ||||
| 	@echo "  install_initscripts	Install initialization scripts." | ||||
| 	@echo "  install_lvm2		Install lvm2 files." | ||||
| 	@echo "  install_systemd_units	Install systemd units." | ||||
| 	@echo "  lcov			Generate lcov output." | ||||
| 	@echo "  lcov-dated		Generate lcov with timedate suffix." | ||||
| 	@echo "  lcov-reset		Reset lcov counters" | ||||
| 	@echo "  man			Build man pages." | ||||
| 	@echo "  rpm			Build rpm." | ||||
| 	@echo "  run-unit-test		Run unit tests." | ||||
| 	@echo "  tags			Generate c/etags." | ||||
| 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) | ||||
| @@ -191,22 +188,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 | ||||
|   | ||||
							
								
								
									
										23
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README
									
									
									
									
									
								
							| @@ -1,7 +1,5 @@ | ||||
| This tree contains the LVM2 and device-mapper tools and libraries. | ||||
|  | ||||
| This is development branch, for stable 2.02 release see stable-2.02 branch. | ||||
|  | ||||
| For more information about LVM2 read the changelog in the WHATS_NEW file. | ||||
| Installation instructions are in INSTALL. | ||||
|  | ||||
| @@ -9,15 +7,11 @@ There is no warranty - see COPYING and COPYING.LIB. | ||||
|  | ||||
| Tarballs are available from: | ||||
|   ftp://sourceware.org/pub/lvm2/ | ||||
|   https://github.com/lvmteam/lvm2/releases | ||||
|   ftp://sources.redhat.com/pub/lvm2/ | ||||
|  | ||||
| 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 | ||||
|  | ||||
| Mailing list for general discussion related to LVM2: | ||||
|   linux-lvm@redhat.com | ||||
| @@ -35,17 +29,6 @@ and multipath-tools: | ||||
|   dm-devel@redhat.com | ||||
|   Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel | ||||
|  | ||||
| Website: | ||||
|   https://sourceware.org/lvm2/ | ||||
| The source code repository used until 7th June 2012 is accessible here: | ||||
|   http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=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 using CVS: | ||||
|  | ||||
|   cvs -d :pserver:cvs@sourceware.org:/cvs/lvm2 login cvs | ||||
|   cvs -d :pserver:cvs@sourceware.org:/cvs/lvm2 checkout LVM2 | ||||
|  | ||||
| The password is cvs. | ||||
|   | ||||
							
								
								
									
										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.177-git (2021-01-08) | ||||
| 1.02.143-git (2017-07-20) | ||||
|   | ||||
							
								
								
									
										416
									
								
								WHATS_NEW
									
									
									
									
									
								
							
							
						
						
									
										416
									
								
								WHATS_NEW
									
									
									
									
									
								
							| @@ -1,417 +1,5 @@ | ||||
| Version 2.03.12 -  | ||||
| =================================== | ||||
|   Add configure option --with/out-symvers for non-glibc builds. | ||||
|   Report error when the filesystem is missing on fsadm resized volume. | ||||
|   Handle better blockdev with --getsize64 support for fsadm. | ||||
|   Do not include editline/history.h when using editline library. | ||||
|   Support lvconvert -Zn with thin-pool conversion. | ||||
|   Support error and zero segtype for thin-pool data for testing. | ||||
|   Support mixed extension for striped, error and zero segtypes. | ||||
|   Support resize also for stacked virtual volumes. | ||||
|   Skip dm-zero devices just like with dm-error target. | ||||
|   Reduce ioctl() calls when checking target status. | ||||
|   Merge polling does not fail, when LV is found to be already merged. | ||||
|   Poll volumes with at least 100ms delays. | ||||
|   Do not flush dm cache when cached LV is going to be removed. | ||||
|   Support interruption while waiting on device close before deactivation. | ||||
|   Flush thin-pool messages before removing more thin volumes. | ||||
|   Improve hash function with less collisions and make it faster. | ||||
|   Reduce ioctl count when deactivating volumes. | ||||
|   Reduce number of metadata parsing. | ||||
|   Enhance performance of lvremove and vgremove commands. | ||||
|   Support interruption when taking archive and backup. | ||||
|   Accelerate large lvremoves. | ||||
|   Speedup search for cached device nodes. | ||||
|   Speedup command initialization. | ||||
|   Add devices file feature, off by default for now. | ||||
|   Support extension of writecached volumes. | ||||
|   Fix problem with unbound variable usage within fsadm. | ||||
|   Check for presence of VDO target before starting any conversion. | ||||
|   Support metatadata profiles with volume VDO pool conversions. | ||||
|   Support -Zn for conversion of already formated VDO pools. | ||||
|   Avoid removing LVs on error path of lvconvert during creation volumes. | ||||
|   Fix crashing lvdisplay when thin volume was waiting for merge. | ||||
|   Support option --errorwhenfull when converting volume to thin-pool. | ||||
|   Improve thin-performance profile support conversion to thin-pool. | ||||
|   Add workaround to avoid read of internal 'converted' devices. | ||||
|   Prohibit merging snapshot into the read-only thick snapshot origin. | ||||
|   Restore support for flipping rw/r permissions for thin snapshot origin. | ||||
|   Support resize of cached volumes. | ||||
|   Disable autoactivation with global/event_activation=0. | ||||
|   Check if lvcreate passes read_only_volume_list with tags and skips zeroing. | ||||
|   Allocation prints better error when metadata cannot fit on a single PV. | ||||
|   Pvmove can better resolve full thin-pool tree move. | ||||
|   Limit pool metadata spare to 16GiB. | ||||
|   Improves convertsion and allocation of pool metadata. | ||||
|   Support thin pool metadata 15.88GiB, adds 64MiB, thin_pool_crop_metadata=0. | ||||
|   Enhance lvdisplay to report raid availiable/partial. | ||||
|   Support online rename of VDO pools. | ||||
|   Imporove removal of pmspare when last pool is removed. | ||||
|   Fix problem with wiping of converted LVs. | ||||
|   Fix memleak in scanning  (2.03.11). | ||||
|   Fix corner case allocation for thin-pools. | ||||
|  | ||||
| Version 2.03.11 - 08th January 2021 | ||||
| =================================== | ||||
|   Fix pvck handling MDA at offset different from 4096. | ||||
|   Partial or degraded activation of writecache is not allowed. | ||||
|   Enhance error handling for fsadm and handle correct fsck result. | ||||
|   Dmeventd lvm plugin ignores higher reserved_stack lvm.conf values. | ||||
|   Support using BLKZEROOUT for clearing devices. | ||||
|   Support interruption when wipping LVs. | ||||
|   Support interruption for bcache waiting. | ||||
|   Fix bcache when device has too many failing writes. | ||||
|   Fix bcache waiting for IO completion with failing disks. | ||||
|   Configure use own python path name order to prefer using python3. | ||||
|   Add configure --enable-editline support as an alternative to readline. | ||||
|   Enhance reporting and error handling when creating thin volumes. | ||||
|   Enable vgsplit for VDO volumes. | ||||
|   Lvextend of vdo pool volumes ensure at least 1 new VDO slab is added. | ||||
|   Use revert_lv() on reload error path after vg_revert(). | ||||
|   Configure --with-integrity enabled. | ||||
|   Restore lost signal blocking while VG lock is held. | ||||
|   Improve estimation of needed extents when creating thin-pool. | ||||
|   Use extra 1% when resizing thin-pool metadata LV with --use-policy. | ||||
|   Enhance --use-policy percentage rounding. | ||||
|   Configure --with-vdo and --with-writecache as internal segments. | ||||
|   Improving VDO man page examples. | ||||
|   Allow pvmove of writecache origin. | ||||
|   Report integrity fields. | ||||
|   Integrity volumes defaults to journal mode. | ||||
|   Switch code base to use flexible array syntax. | ||||
|   Fix 64bit math when calculation cachevol size. | ||||
|   Preserve uint32_t for seqno handling. | ||||
|   Switch from mmap to plain read when loading regular files. | ||||
|   Update lvmvdo man page and better explain DISCARD usage. | ||||
|  | ||||
| Version 2.03.10 - 09th August 2020 | ||||
| ================================== | ||||
|   Add writecache and integrity support to lvmdbusd. | ||||
|   Generate unique cachevol name when default required from lvcreate. | ||||
|   Converting RAID1 volume to one with same number of legs now succeeds with a | ||||
|   warning. | ||||
|   Fix conversion to raid from striped lagging type. | ||||
|   Fix conversion to 'mirrored' mirror log with larger regionsize. | ||||
|   Zero pool metadata on allocation (disable with allocation/zero_metadata=0). | ||||
|   Failure in zeroing or wiping will fail command (bypass with -Zn, -Wn). | ||||
|   Add lvcreate of new cache or writecache lv with single command. | ||||
|   Fix running out of free buffers for async writing for larger writes. | ||||
|   Add integrity with raid capability. | ||||
|   Fix support for lvconvert --repair used by foreign apps (i.e. Docker). | ||||
|  | ||||
| Version 2.03.09 - 26th March 2020 | ||||
| Version 2.02.174 -  | ||||
| ================================= | ||||
|   Fix formating of vdopool (vdo_slab_size_mb was smaller by 2 bits). | ||||
|   Fix showing of a dm kernel error when uncaching a volume with cachevol. | ||||
|  | ||||
| Version 2.03.08 - 11th February 2020 | ||||
| ==================================== | ||||
|   Prevent problematic snapshots of writecache volumes. | ||||
|   Add error handling for failing allocation in _reserve_area(). | ||||
|   Fix memleak in syncing of internal cache. | ||||
|   Fix pvck dump_current_text memleak. | ||||
|   Fix lvmlockd result code on error path for _query_lock_lv(). | ||||
|   Update pvck man page and help output. | ||||
|   Reject invalid writecache high/low_watermark setting. | ||||
|   Report writecache status. | ||||
|   Accept more output lines from vdo_format. | ||||
|   Prohibit reshaping of stacked raid LVs. | ||||
|   Avoid running cache input arg validation when creating vdo pool. | ||||
|   Prevent raid reshaping of stacked volumes. | ||||
|   Added VDO lvmdbusd methods for enable/disable compression & dedupe. | ||||
|   Added VDO lvmdbusd method for converting LV to VDO pool. | ||||
|  | ||||
| Version 2.03.07 - 30th November 2019 | ||||
| ==================================== | ||||
|   Subcommand in vgck for repairing headers and metadata. | ||||
|   Ensure minimum required region size on striped RaidLV creation. | ||||
|   Fix resize of thin-pool with data and metadata of different segtype. | ||||
|   Improve mirror type leg splitting. | ||||
|   Improve error path handling in daemons on shutdown. | ||||
|   Fix activation order when removing merged snapshot. | ||||
|   Experimental VDO support for lvmdbusd. | ||||
|  | ||||
| Version 2.03.06 - 23rd October 2019 | ||||
| =================================== | ||||
|   Add _cpool suffix to cache-pool LV name when used by caching LV. | ||||
|   No longer store extra UUID for cmeta and cdata cachevol layer. | ||||
|   Enhance activation of cache devices with cachevols. | ||||
|   Add _cvol in list of protected suffixes and start use it with DM UUID. | ||||
|   Rename LV converted to cachevol to use _cvol suffix. | ||||
|   Use normal LVs for wiping of cachevols. | ||||
|   Reload cleanered cache DM only with cleaner policy. | ||||
|   Fix cmd return when zeroing of cachevol fails. | ||||
|   Extend lvs to show all VDO properties. | ||||
|   Preserve VDO write policy with vdopool. | ||||
|   Increase default vdo bio threads to 4. | ||||
|   Continue report when cache_status fails. | ||||
|   Add support for DM_DEVICE_GET_TARGET_VERSION into device_mapper. | ||||
|   Fix cmirrord usage of header files from device_mapper subdir. | ||||
|   Allow standalone activation of VDO pool just like for thin-pools. | ||||
|   Activate thin-pool layered volume as 'read-only' device. | ||||
|   Ignore crypto devices with UUID signature CRYPT-SUBDEV. | ||||
|   Enhance validation for thin and cache pool conversion and swapping. | ||||
|   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 | ||||
| ===================================== | ||||
|   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. | ||||
| @@ -1769,7 +1357,7 @@ Version 2.02.105 - 20th January 2014 | ||||
|   Allow lvmetad to reuse stale socket. | ||||
|   Only unlink lvmetad socket on error if created by the same process. | ||||
|   Append missing newline to lvmetad missing socket path error message. | ||||
|   Check for non-zero alignment in _text_pv_add_metadata_area() to not div by 0. | ||||
|   Check for non-zero aligment in _text_pv_add_metadata_area() to not div by 0. | ||||
|   Add allocation/use_blkid_wiping to lvm.conf to enable blkid wiping. | ||||
|   Enable blkid_wiping by default if the blkid library is present. | ||||
|   Add configure --disable-blkid_wiping to disable libblkid signature detection. | ||||
|   | ||||
							
								
								
									
										111
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							| @@ -1,112 +1,5 @@ | ||||
| Version 1.02.177 -  | ||||
| ==================================== | ||||
|   Add dm_tree_node_add_thin_pool_target_v1 with crop_metadata support. | ||||
|  | ||||
| Version 1.02.175 - 08th January 2021 | ||||
| ==================================== | ||||
|  | ||||
| Version 1.02.173 - 09th August 2020 | ||||
| =================================== | ||||
|   Add support for VDO in blkdeactivate script. | ||||
|  | ||||
| Version 1.02.171 - 26th March 2020 | ||||
| ================================== | ||||
|   Try to remove all created devices on dm preload tree error path. | ||||
|   Fix dm_list interators with gcc 10 optimization (-ftree-pta). | ||||
|   Dmeventd handles timer without looping on short intervals. | ||||
|  | ||||
| Version 1.02.169 - 11th February 2020 | ||||
| ===================================== | ||||
|   Enhance error messages for device creation. | ||||
|  | ||||
| Version 1.02.167 - 30th November 2019 | ||||
| ===================================== | ||||
|  | ||||
| Version 1.02.165 - 23rd October 2019 | ||||
| ==================================== | ||||
|   Add support for DM_DEVICE_GET_TARGET_VERSION. | ||||
|   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.143 -  | ||||
| ================================= | ||||
|  | ||||
| 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 | ||||
| ===================================== | ||||
|   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 | ||||
| @@ -555,7 +448,7 @@ Version 1.02.86 - 23rd June 2014 | ||||
|   Add DM_REPORT_FIELD_TYPE_STRING_LIST: separate string and string list fields. | ||||
|   Add dm_str_list to libdevmapper for string list type definition and its reuse. | ||||
|   Add dmsetup -S/--select to define selection criteria for dmsetup reports. | ||||
|   Add dm_report_init_with_selection to initialize report with selection criteria. | ||||
|   Add dm_report_init_with_selection to intialize report with selection criteria. | ||||
|   Add DM_REPORT_FIELD_TYPE_SIZE: separate number and size reporting fields. | ||||
|   Use RemoveOnStop for dm-event.socket systemd unit. | ||||
|   Document env var 'DM_DEFAULT_NAME_MANGLING_MODE' in dmsetup man page. | ||||
|   | ||||
| @@ -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]) | ||||
|   | ||||
							
								
								
									
										307
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										307
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # generated automatically by aclocal 1.16.2 -*- Autoconf -*- | ||||
| # generated automatically by aclocal 1.15 -*- Autoconf -*- | ||||
|  | ||||
| # Copyright (C) 1996-2020 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-2020 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, | ||||
| @@ -447,11 +318,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). | ||||
|   m4_define_default([_AM_PYTHON_INTERPRETER_LIST], | ||||
| [python python2 python3 dnl | ||||
|  python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 dnl | ||||
|  python3.2 python3.1 python3.0 dnl | ||||
|  python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 dnl | ||||
|  python2.0]) | ||||
| [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]) | ||||
|  | ||||
| @@ -496,14 +364,12 @@ AC_DEFUN([AM_PATH_PYTHON], | ||||
|     m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) | ||||
|   else | ||||
|  | ||||
|   dnl Query Python for its version number.  Although site.py simply uses | ||||
|   dnl sys.version[:3], printing that failed with Python 3.10, since the | ||||
|   dnl trailing zero was eliminated. So now we output just the major | ||||
|   dnl and minor version numbers, as numbers. Apparently the tertiary | ||||
|   dnl version is not of interest. | ||||
|   dnl Query Python for its version number.  Getting [:3] seems to be | ||||
|   dnl the best way to do this; it's what "site.py" does in the standard | ||||
|   dnl library. | ||||
|  | ||||
|   AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], | ||||
|     [am_cv_python_version=`$PYTHON -c "import sys; print('%u.%u' % sys.version_info[[:2]])"`]) | ||||
|     [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) | ||||
|   AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) | ||||
|  | ||||
|   dnl Use the values of $prefix and $exec_prefix for the corresponding | ||||
| @@ -653,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-2020 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, | ||||
| @@ -670,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,477 +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; | ||||
| 	unsigned hash; | ||||
| 	char key[]; | ||||
| }; | ||||
|  | ||||
| struct dm_hash_table { | ||||
| 	unsigned num_nodes; | ||||
| 	unsigned num_hint; | ||||
| 	unsigned mask_slots;    /* (slots - 1) -> used as hash mask */ | ||||
| 	unsigned collisions;    /* Collissions of hash keys */ | ||||
| 	unsigned search;        /* How many keys were searched */ | ||||
| 	unsigned found;         /* How many nodes were found */ | ||||
| 	unsigned same_hash;     /* Was there a colision with same masked hash and len ? */ | ||||
| 	struct dm_hash_node **slots; | ||||
| }; | ||||
|  | ||||
| #if 0 /* TO BE REMOVED */ | ||||
| static unsigned _hash(const void *key, unsigned len) | ||||
| { | ||||
| 	/* 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 | ||||
| 	}; | ||||
|  | ||||
| 	const uint8_t *str = key; | ||||
| 	unsigned h = 0, g; | ||||
| 	unsigned i; | ||||
|  | ||||
| 	for (i = 0; i < len; i++) { | ||||
| 		h <<= 4; | ||||
| 		h += _nums[*str++]; | ||||
| 		g = h & ((unsigned) 0xf << 16u); | ||||
| 		if (g) { | ||||
| 			h ^= g >> 16u; | ||||
| 			h ^= g >> 5u; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return h; | ||||
| } | ||||
|  | ||||
| /* In-kernel DM hashing, still lots of collisions */ | ||||
| static unsigned _hash_in_kernel(const char *key, unsigned len) | ||||
| { | ||||
| 	const unsigned char *str = (unsigned char *)key; | ||||
| 	const unsigned hash_mult = 2654435387U; | ||||
| 	unsigned hash = 0, i; | ||||
|  | ||||
| 	for (i = 0; i < len; ++i) | ||||
| 		hash = (hash + str[i]) * hash_mult; | ||||
|  | ||||
| 	return hash; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #undef get16bits | ||||
| #if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) | ||||
| #define get16bits(d) (*((const uint16_t *) (d))) | ||||
| #endif | ||||
|  | ||||
| #if !defined (get16bits) | ||||
| #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ | ||||
|                        +(uint32_t)(((const uint8_t *)(d))[0]) ) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Adapted Bob Jenkins hash to read by 2 bytes if possible. | ||||
|  * https://secure.wikimedia.org/wikipedia/en/wiki/Jenkins_hash_function | ||||
|  * | ||||
|  * Reduces amount of hash collisions | ||||
|  */ | ||||
| static unsigned _hash(const void *key, unsigned len) | ||||
| { | ||||
| 	const uint8_t *str = (uint8_t*) key; | ||||
| 	unsigned hash = 0, i; | ||||
| 	unsigned sz = len / 2; | ||||
|  | ||||
| 	for(i = 0; i < sz; ++i) { | ||||
| 		hash += get16bits(str + 2 * i); | ||||
| 		hash += (hash << 10); | ||||
| 		hash ^= (hash >> 6); | ||||
| 	} | ||||
|  | ||||
| 	if (len & 1) { | ||||
| 		hash += str[len - 1]; | ||||
| 		hash += (hash << 10); | ||||
| 		hash ^= (hash >> 6); | ||||
| 	} | ||||
|  | ||||
| 	hash += (hash << 3); | ||||
| 	hash ^= (hash >> 11); | ||||
| 	hash += (hash << 15); | ||||
|  | ||||
| 	return hash; | ||||
| } | ||||
|  | ||||
| static struct dm_hash_node *_create_node(const void *key, unsigned len) | ||||
| { | ||||
| 	struct dm_hash_node *n = malloc(sizeof(*n) + len); | ||||
|  | ||||
| 	if (n) { | ||||
| 		memcpy(n->key, key, len); | ||||
| 		n->keylen = len; | ||||
| 	} | ||||
|  | ||||
| 	return n; | ||||
| } | ||||
|  | ||||
| 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) { | ||||
| 		log_error("Failed to allocate memory for hash."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	hc->num_hint = size_hint; | ||||
|  | ||||
| 	/* round size hint up to a power of two */ | ||||
| 	while (new_size < size_hint) | ||||
| 		new_size = new_size << 1; | ||||
|  | ||||
| 	hc->mask_slots = new_size - 1; | ||||
| 	len = sizeof(*(hc->slots)) * new_size; | ||||
| 	if (!(hc->slots = zalloc(len))) { | ||||
| 		free(hc); | ||||
| 		log_error("Failed to allocate slots for hash."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return hc; | ||||
| } | ||||
|  | ||||
| static void _free_nodes(struct dm_hash_table *t) | ||||
| { | ||||
| 	struct dm_hash_node *c, *n; | ||||
| 	unsigned i; | ||||
|  | ||||
| #ifdef DEBUG | ||||
| 	log_debug("Free hash hint:%d slots:%d nodes:%d (s:%d f:%d c:%d h:%d)", | ||||
| 		  t->num_hint, t->mask_slots + 1, t->num_nodes, | ||||
| 		  t->search, t->found, t->collisions, t->same_hash); | ||||
| #endif | ||||
|  | ||||
| 	if (!t->num_nodes) | ||||
| 		return; | ||||
|  | ||||
| 	for (i = 0; i <= t->mask_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 **_findh(struct dm_hash_table *t, const void *key, | ||||
| 				    uint32_t len, unsigned hash) | ||||
| { | ||||
| 	struct dm_hash_node **c; | ||||
|  | ||||
| 	++t->search; | ||||
| 	for (c = &t->slots[hash & t->mask_slots]; *c; c = &((*c)->next)) { | ||||
| 		if ((*c)->keylen == len && (*c)->hash == hash) { | ||||
| 			if (!memcmp(key, (*c)->key, len)) { | ||||
| 				++t->found; | ||||
| 				break; | ||||
| 			} | ||||
| 			++t->same_hash; | ||||
| 		} | ||||
| 		++t->collisions; | ||||
| 	} | ||||
|  | ||||
| 	return c; | ||||
| } | ||||
|  | ||||
| static struct dm_hash_node **_find(struct dm_hash_table *t, const void *key, | ||||
| 				   uint32_t len) | ||||
| { | ||||
| 	return _findh(t, key, len, _hash(key, len)); | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	unsigned hash = _hash(key, len); | ||||
| 	struct dm_hash_node **c = _findh(t, key, len, hash); | ||||
|  | ||||
| 	if (*c) | ||||
| 		(*c)->data = data; | ||||
| 	else { | ||||
| 		struct dm_hash_node *n = _create_node(key, len); | ||||
|  | ||||
| 		if (!n) | ||||
| 			return 0; | ||||
|  | ||||
| 		n->data = data; | ||||
| 		n->hash = hash; | ||||
| 		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->mask_slots; | ||||
|  | ||||
| 	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->mask_slots; | ||||
|  | ||||
| 	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->mask_slots; | ||||
|  | ||||
| 	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->mask_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->mask_slots + 1)); | ||||
| 	t->num_nodes = t->collisions = t->search = t->same_hash = 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->mask_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) | ||||
| { | ||||
| 	return n->next ? n->next : _next_slot(t, (n->hash & t->mask_slots) + 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,211 +0,0 @@ | ||||
| #ifndef BASE_DATA_STRUCT_LIST_H | ||||
| #define BASE_DATA_STRUCT_LIST_H | ||||
|  | ||||
| #include "base/memory/container_of.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) \ | ||||
|     container_of(v, t, 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) - offsetof(t, 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,25 +0,0 @@ | ||||
| // Copyright (C) 2018 - 2020 Red Hat, Inc. All rights reserved. | ||||
| //  | ||||
| // This file is part of LVM2. | ||||
| // | ||||
| // This copyrighted material is made available to anyone wishing to use, | ||||
| // modify, copy, or redistribute it subject to the terms and conditions | ||||
| // of the GNU Lesser General Public License v.2.1. | ||||
| // | ||||
| // You should 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 | ||||
|  | ||||
| #include <stddef.h>  // offsetof | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #define container_of(v, t, head) \ | ||||
|     ((t *)((char *)(v) - offsetof(t, 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 | ||||
| 	LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withgeneralpreamble --withcomments --ignorelocal --withspaces > example.conf.in | ||||
| 	LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withlocalpreamble --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) $(profiledir) | ||||
| 	$(INSTALL_DATA) $(PROFILES) $(profiledir)/ | ||||
|  | ||||
| install_lvm2: install_conf install_localconf install_profiles | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -28,13 +28,13 @@ local { | ||||
| 	# main configuration file, e.g. lvm.conf. When used, it must be set to | ||||
| 	# a unique value among all hosts sharing access to the storage, | ||||
| 	# e.g. a host name. | ||||
| 	# | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# Set no system ID: | ||||
| 	# system_id = "" | ||||
| 	# Set the system_id to a specific name: | ||||
| 	# system_id = "host1" | ||||
| 	# | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# system_id = "" | ||||
|  | ||||
|   | ||||
| @@ -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
											
										
									
								
							| @@ -46,7 +46,6 @@ const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile | ||||
| 	return "STRING"; | ||||
| } | ||||
|  | ||||
| /* | ||||
| struct logical_volume *origin_from_cow(const struct logical_volume *lv) | ||||
| { | ||||
| 	if (lv) | ||||
| @@ -54,7 +53,6 @@ struct logical_volume *origin_from_cow(const struct logical_volume *lv) | ||||
|  | ||||
| 	__coverity_panic__(); | ||||
| } | ||||
| */ | ||||
|  | ||||
| /* simple_memccpy() from glibc */ | ||||
| void *memccpy(void *dest, const void *src, int c, size_t n) | ||||
|   | ||||
| @@ -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 | ||||
| @@ -40,8 +48,12 @@ ifeq ("@BUILD_LVMDBUSD@", "yes") | ||||
|   SUBDIRS += lvmdbusd | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_DMFILEMAPD@", "yes") | ||||
|   SUBDIRS += dmfilemapd | ||||
| endif | ||||
|  | ||||
| ifeq ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = cmirrord dmeventd lvmpolld lvmlockd lvmdbusd | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd | ||||
| endif | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|   | ||||
							
								
								
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| clvmd | ||||
							
								
								
									
										94
									
								
								daemons/clvmd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								daemons/clvmd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| # | ||||
| # 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 | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS) | ||||
| CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS) | ||||
|  | ||||
| INSTALL_TARGETS = \ | ||||
| 	install_clvmd | ||||
|  | ||||
| clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \ | ||||
| 	      -o clvmd $(OBJECTS) $(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("(%p) doing PRE command LOCK_VG '%s' at %x\n", client, lockname, lock_cmd); | ||||
|  | ||||
| 	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("(%p) Client thread cleanup\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("(%p) Cleanup: 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,5 +1,5 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2010 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -12,11 +12,16 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _LIBDM_KDEV_H | ||||
| #define _LIBDM_KDEV_H | ||||
| /*
 | ||||
|  * This file must be included first by every clvmd source file. | ||||
|  */ | ||||
| #ifndef _LVM_CLVMD_COMMON_H | ||||
| #define _LVM_CLVMD_COMMON_H | ||||
| 
 | ||||
| #define MAJOR(dev)      ((dev & 0xfff00) >> 8) | ||||
| #define MINOR(dev)      ((dev & 0xff) | ((dev >> 12) & 0xfff00)) | ||||
| #define MKDEV(ma,mi)    (((dev_t)mi & 0xff) | ((dev_t)ma << 8) | (((dev_t)mi & ~0xff) << 12)) | ||||
| #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; | ||||
| } | ||||
							
								
								
									
										687
									
								
								daemons/clvmd/clvmd-openais.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										687
									
								
								daemons/clvmd/clvmd-openais.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,687 @@ | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
|  | ||||
| /* 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; | ||||
| } | ||||
							
								
								
									
										2407
									
								
								daemons/clvmd/clvmd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2407
									
								
								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,27 +17,23 @@ top_builddir = @top_builddir@ | ||||
|  | ||||
| CPG_LIBS = @CPG_LIBS@ | ||||
| CPG_CFLAGS = @CPG_CFLAGS@ | ||||
| SACKPT_LIBS = @SACKPT_LIBS@ | ||||
| SACKPT_CFLAGS = @SACKPT_CFLAGS@ | ||||
|  | ||||
| SOURCES = clogd.c cluster.c compat.c functions.c link_mon.c local.c logging.c | ||||
|  | ||||
| TARGETS = cmirrord | ||||
|  | ||||
| CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES)) | ||||
| CFLOW_TARGET := $(TARGETS) | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LMLIBS += $(CPG_LIBS) | ||||
| CFLAGS += $(CPG_CFLAGS) $(EXTRA_EXEC_CFLAGS) | ||||
| LIBS += -ldevmapper | ||||
| LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS) | ||||
| CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS) | ||||
| LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) | ||||
|  | ||||
| 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_cluster: $(TARGETS) | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_PROGRAM) -D $< $(usrsbindir)/$(<F) | ||||
|  | ||||
| install: install_cluster | ||||
| install: $(TARGETS) | ||||
| 	$(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; | ||||
|  | ||||
| @@ -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/libdevmapper.h" | ||||
| #include "libdm/misc/dm-log-userspace.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> | ||||
| @@ -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; | ||||
| } | ||||
| @@ -658,10 +653,11 @@ static int clog_dtr(struct dm_ulog_request *rq) | ||||
| 	if (lc->disk_fd != -1 && close(lc->disk_fd)) | ||||
| 		LOG_ERROR("Failed to close disk log: %s", | ||||
| 			  strerror(errno)); | ||||
| 	free(lc->disk_buffer); | ||||
| 	free(lc->clean_bits); | ||||
| 	free(lc->sync_bits); | ||||
| 	free(lc); | ||||
| 	if (lc->disk_buffer) | ||||
| 		free(lc->disk_buffer); | ||||
| 	dm_free(lc->clean_bits); | ||||
| 	dm_free(lc->sync_bits); | ||||
| 	dm_free(lc); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -12,8 +12,7 @@ | ||||
| #ifndef _LVM_CLOG_FUNCTIONS_H | ||||
| #define _LVM_CLOG_FUNCTIONS_H | ||||
|  | ||||
| #include "libdm/libdevmapper.h" | ||||
| #include "libdm/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> | ||||
|   | ||||
| @@ -14,21 +14,11 @@ | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
| abs_srcdir = @abs_srcdir@ | ||||
|  | ||||
| SOURCES = libdevmapper-event.c | ||||
| SOURCES2 = dmeventd.c | ||||
|  | ||||
| TARGETS = dmeventd | ||||
| CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES) $(SOURCES2) \ | ||||
| 	plugins/lvm2/dmeventd_lvm.c \ | ||||
| 	plugins/mirror/dmeventd_mirror.c \ | ||||
| 	plugins/raid/dmeventd_raid.c \ | ||||
| 	plugins/snapshot/dmeventd_snapshot.c \ | ||||
| 	plugins/thin/dmeventd_thin.c \ | ||||
| 	plugins/vdo/dmeventd_vdo.c \ | ||||
| 	) | ||||
| CFLOW_TARGET := $(TARGETS) | ||||
|  | ||||
| .PHONY: install_lib_dynamic install_lib_static install_include \ | ||||
| 	install_pkgconfig install_dmeventd_dynamic install_dmeventd_static \ | ||||
| @@ -56,6 +46,7 @@ endif | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
| CFLOW_TARGET = dmeventd | ||||
|  | ||||
| EXPORTED_HEADER = $(srcdir)/libdevmapper-event.h | ||||
| EXPORTED_FN_PREFIX = dm_event | ||||
| @@ -66,45 +57,47 @@ all: device-mapper | ||||
| device-mapper: $(TARGETS) | ||||
|  | ||||
| CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS) | ||||
| LIBS += $(PTHREAD_LIBS) -L$(top_builddir)/libdm -ldevmapper | ||||
| LIBS += -ldevmapper $(PTHREAD_LIBS) | ||||
|  | ||||
| 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) -L. $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) dmeventd.o \ | ||||
| 		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) | ||||
|  | ||||
| dmeventd.static: $(LIB_STATIC) dmeventd.o | ||||
| 	@echo "    [CC] $@" | ||||
| 	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) dmeventd.o \ | ||||
| dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) dmeventd.o \ | ||||
| 		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS) | ||||
|  | ||||
| ifeq ("@PKGCONFIG@", "yes") | ||||
|   INSTALL_LIB_TARGETS += install_pkgconfig | ||||
| endif | ||||
|  | ||||
| ifneq ("$(CFLOW_CMD)", "") | ||||
| CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES)) | ||||
| -include $(top_builddir)/libdm/libdevmapper.cflow | ||||
| -include $(top_builddir)/lib/liblvm-internal.cflow | ||||
| -include $(top_builddir)/lib/liblvm2cmd.cflow | ||||
| -include $(top_builddir)/daemons/dmeventd/$(LIB_NAME).cflow | ||||
| -include $(top_builddir)/daemons/dmeventd/plugins/mirror/$(LIB_NAME)-lvm2mirror.cflow | ||||
| endif | ||||
|  | ||||
| install_include: $(srcdir)/libdevmapper-event.h | ||||
| 	@echo "    [INSTALL] $(<F)" | ||||
| 	$(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 */ | ||||
| @@ -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", | ||||
| @@ -752,9 +750,8 @@ static void _exit_timeout(void *unused __attribute__((unused))) | ||||
| static void *_timeout_thread(void *unused __attribute__((unused))) | ||||
| { | ||||
| 	struct thread_status *thread; | ||||
| 	struct timespec timeout, real_time; | ||||
| 	struct timespec timeout; | ||||
| 	time_t curr_time; | ||||
| 	int ret; | ||||
|  | ||||
| 	DEBUGLOG("Timeout thread starting."); | ||||
| 	pthread_cleanup_push(_exit_timeout, NULL); | ||||
| @@ -763,16 +760,7 @@ static void *_timeout_thread(void *unused __attribute__((unused))) | ||||
| 	while (!dm_list_empty(&_timeout_registry)) { | ||||
| 		timeout.tv_sec = 0; | ||||
| 		timeout.tv_nsec = 0; | ||||
| #ifndef HAVE_REALTIME | ||||
| 		curr_time = time(NULL); | ||||
| #else | ||||
| 		if (clock_gettime(CLOCK_REALTIME, &real_time)) { | ||||
| 			log_error("Failed to read clock_gettime()."); | ||||
| 			break; | ||||
| 		} | ||||
| 		/* 10ms back to the future */ | ||||
| 		curr_time = real_time.tv_sec + ((real_time.tv_nsec > (1000000000 - 10000000)) ? 1 : 0); | ||||
| #endif | ||||
|  | ||||
| 		dm_list_iterate_items_gen(thread, &_timeout_registry, timeout_list) { | ||||
| 			if (thread->next_time <= curr_time) { | ||||
| @@ -785,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(); | ||||
| 			} | ||||
| @@ -878,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) { | ||||
| @@ -1234,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, | ||||
| @@ -1374,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); | ||||
|  | ||||
| @@ -1494,34 +1478,37 @@ static int _client_read(struct dm_event_fifos *fifos, | ||||
| 		t.tv_usec = 0; | ||||
| 		ret = select(fifos->client + 1, &fds, NULL, NULL, &t); | ||||
|  | ||||
| 		if (!ret && bytes) | ||||
| 			continue; /* trying to finish read */ | ||||
| 		if (!ret && !bytes)	/* nothing to read */ | ||||
| 			return 0; | ||||
|  | ||||
| 		if (ret <= 0)	/* nothing to read */ | ||||
| 			goto bad; | ||||
| 		if (!ret)	/* trying to finish read */ | ||||
| 			continue; | ||||
|  | ||||
| 		if (ret < 0)	/* error */ | ||||
| 			return 0; | ||||
|  | ||||
| 		ret = read(fifos->client, buf + bytes, size - bytes); | ||||
| 		bytes += ret > 0 ? ret : 0; | ||||
| 		if (!msg->data && (bytes == 2 * sizeof(uint32_t))) { | ||||
| 		if (header && (bytes == 2 * sizeof(uint32_t))) { | ||||
| 			msg->cmd = ntohl(header[0]); | ||||
| 			size = msg->size = ntohl(header[1]); | ||||
| 			bytes = 0; | ||||
|  | ||||
| 			if (!(size = msg->size = ntohl(header[1]))) | ||||
| 				break; | ||||
|  | ||||
| 			if (!(buf = msg->data = malloc(msg->size))) | ||||
| 				goto bad; | ||||
| 			if (!size) | ||||
| 				break; /* No data -> error */ | ||||
| 			buf = msg->data = dm_malloc(msg->size); | ||||
| 			if (!buf) | ||||
| 				break; /* No mem -> error */ | ||||
| 			header = 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (bytes == size) | ||||
| 		return 1; | ||||
| 	if (bytes != size) { | ||||
| 		dm_free(msg->data); | ||||
| 		msg->data = NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| bad: | ||||
| 	free(msg->data); | ||||
| 	msg->data = NULL; | ||||
|  | ||||
| 	return 0; | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1536,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) { | ||||
| @@ -1566,7 +1553,7 @@ static int _client_write(struct dm_event_fifos *fifos, | ||||
| 	} | ||||
|  | ||||
| 	if (header != temp) | ||||
| 		free(header); | ||||
| 		dm_free(header); | ||||
|  | ||||
| 	return (bytes == size); | ||||
| } | ||||
| @@ -1628,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; | ||||
| @@ -1670,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)) | ||||
| @@ -1751,8 +1738,7 @@ static void _init_thread_signals(void) | ||||
| 	sigdelset(&my_sigset, SIGHUP); | ||||
| 	sigdelset(&my_sigset, SIGQUIT); | ||||
|  | ||||
| 	if (pthread_sigmask(SIG_BLOCK, &my_sigset, NULL)) | ||||
| 		log_sys_error("pthread_sigmask", "SIG_BLOCK"); | ||||
| 	pthread_sigmask(SIG_BLOCK, &my_sigset, NULL); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1764,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__ | ||||
| @@ -1982,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) { | ||||
| @@ -2028,8 +2014,8 @@ static int _reinstate_registrations(struct dm_event_fifos *fifos) | ||||
| static void _restart_dmeventd(void) | ||||
| { | ||||
| 	struct dm_event_fifos fifos = { | ||||
| 		.client = -1, | ||||
| 		.server = -1, | ||||
| 		.client = -1, | ||||
| 		/* FIXME Make these either configurable or depend directly on dmeventd_path */ | ||||
| 		.client_path = DM_EVENT_FIFO_CLIENT, | ||||
| 		.server_path = DM_EVENT_FIFO_SERVER | ||||
| @@ -2068,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; | ||||
| 		} | ||||
| @@ -2243,8 +2229,7 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| 	_init_thread_signals(); | ||||
|  | ||||
| 	if (pthread_mutex_init(&_global_mutex, NULL)) | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	pthread_mutex_init(&_global_mutex, NULL); | ||||
|  | ||||
| 	if (!_systemd_activation && !_open_fifos(&fifos)) | ||||
| 		exit(EXIT_FIFO_FAILURE); | ||||
| @@ -2263,8 +2248,6 @@ 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)); | ||||
| @@ -2281,14 +2264,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; | ||||
| @@ -237,16 +234,16 @@ static int _daemon_read(struct dm_event_fifos *fifos, | ||||
| 			ret = select(fifos->server + 1, &fds, NULL, NULL, &tval); | ||||
| 			if (ret < 0 && errno != EINTR) { | ||||
| 				log_error("Unable to read from event server."); | ||||
| 				goto bad; | ||||
| 				return 0; | ||||
| 			} | ||||
| 			if ((ret == 0) && (i > 4) && !bytes) { | ||||
| 				log_error("No input from event server."); | ||||
| 				goto bad; | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
| 		if (ret < 1) { | ||||
| 			log_error("Unable to read from event server."); | ||||
| 			goto bad; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		ret = read(fifos->server, buf + bytes, size); | ||||
| @@ -255,32 +252,25 @@ static int _daemon_read(struct dm_event_fifos *fifos, | ||||
| 				continue; | ||||
|  | ||||
| 			log_error("Unable to read from event server."); | ||||
| 			goto bad; | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		bytes += ret; | ||||
| 		if (!msg->data && (bytes == 2 * sizeof(uint32_t))) { | ||||
| 		if (header && (bytes == 2 * sizeof(uint32_t))) { | ||||
| 			msg->cmd = ntohl(header[0]); | ||||
| 			msg->size = ntohl(header[1]); | ||||
| 			buf = msg->data = dm_malloc(msg->size); | ||||
| 			size = msg->size; | ||||
| 			bytes = 0; | ||||
|  | ||||
| 			if (!(size = msg->size = ntohl(header[1]))) | ||||
| 				break; | ||||
|  | ||||
| 			if (!(buf = msg->data = malloc(msg->size))) { | ||||
| 				log_error("Unable to allocate message data."); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			header = 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (bytes == size) | ||||
| 		return 1; | ||||
|  | ||||
| bad: | ||||
| 	free(msg->data); | ||||
| 	msg->data = NULL; | ||||
|  | ||||
| 	return 0; | ||||
| 	if (bytes != size) { | ||||
| 		dm_free(msg->data); | ||||
| 		msg->data = NULL; | ||||
| 	} | ||||
| 	return bytes == size; | ||||
| } | ||||
|  | ||||
| /* Write message to daemon. */ | ||||
| @@ -380,13 +370,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)) { | ||||
| @@ -615,8 +605,8 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag | ||||
| { | ||||
| 	int ret; | ||||
| 	struct dm_event_fifos fifos = { | ||||
| 		.client = -1, | ||||
| 		.server = -1, | ||||
| 		.client = -1, | ||||
| 		/* FIXME Make these either configurable or depend directly on dmeventd_path */ | ||||
| 		.client_path = DM_EVENT_FIFO_CLIENT, | ||||
| 		.server_path = DM_EVENT_FIFO_SERVER | ||||
| @@ -629,7 +619,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) | ||||
| @@ -655,7 +645,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")) | ||||
| @@ -670,7 +659,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); | ||||
|  | ||||
| @@ -697,7 +686,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); | ||||
|  | ||||
| @@ -713,7 +702,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) | ||||
| @@ -733,11 +722,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; | ||||
| } | ||||
|  | ||||
| @@ -765,10 +754,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; | ||||
| 	} | ||||
|  | ||||
| @@ -779,7 +769,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); | ||||
| @@ -788,7 +778,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; | ||||
| 	} | ||||
| @@ -801,13 +791,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; | ||||
| 	} | ||||
| @@ -825,9 +815,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); | ||||
| @@ -992,12 +982,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") | ||||
|  | ||||
| @@ -71,7 +64,7 @@ int dmeventd_lvm2_init(void) | ||||
| 	if (!_lvm_handle) { | ||||
| 		lvm2_log_fn(_lvm2_print_log); | ||||
|  | ||||
| 		if (!(_lvm_handle = lvm2_init_threaded())) | ||||
| 		if (!(_lvm_handle = lvm2_init())) | ||||
| 			goto out; | ||||
|  | ||||
| 		/* | ||||
| @@ -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 | ||||
|  | ||||
| @@ -25,8 +25,13 @@ LIB_NAME = libdevmapper-event-lvm2mirror | ||||
| LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX) | ||||
| LIB_VERSION = $(LIB_VERSION_LVM) | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -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. */ | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| @@ -24,8 +24,13 @@ LIB_NAME = libdevmapper-event-lvm2raid | ||||
| LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX) | ||||
| LIB_VERSION = $(LIB_VERSION_LVM) | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -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/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) | ||||
| @@ -76,17 +76,14 @@ static int _process_raid_event(struct dso_state *state, char *params, const char | ||||
| 	} | ||||
|  | ||||
| 	if (dead) { | ||||
| 		/* | ||||
| 		 * Use the first event to run a repair ignoring any additional ones. | ||||
| 		 * | ||||
| 		 * We presume lvconvert to do pre-repair | ||||
| 		 * checks to avoid bloat in this plugin. | ||||
| 		 */ | ||||
| 		if (!state->warned && status->insync_regions < status->total_regions) { | ||||
| 			state->warned = 1; | ||||
| 			log_warn("WARNING: waiting for resynchronization to finish " | ||||
| 				 "before initiating repair on RAID device %s.", device); | ||||
| 			/* Fall through to allow lvconvert to run. */ | ||||
| 		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) | ||||
|   | ||||
| @@ -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; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| @@ -24,8 +24,13 @@ LIB_NAME = libdevmapper-event-lvm2thin | ||||
| LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX) | ||||
| LIB_VERSION = $(LIB_VERSION_LVM) | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| LIBS += -ldevmapper-event-lvm2 -ldevmapper | ||||
|  | ||||
| install_lvm2: install_dm_plugin | ||||
|  | ||||
| install: install_lvm2 | ||||
|   | ||||
| @@ -12,16 +12,16 @@ | ||||
|  * 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> | ||||
|  | ||||
| /* 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)) | ||||
| @@ -62,25 +62,27 @@ struct dso_state { | ||||
|  | ||||
| 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 */ | ||||
| 		env[1] = NULL; | ||||
| 		log_debug("Error event processing."); | ||||
| 	} | ||||
|  | ||||
| @@ -95,7 +97,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); | ||||
| @@ -286,7 +288,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; | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| process_event | ||||
| register_device | ||||
| unregister_device | ||||
| @@ -1,412 +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. | ||||
|  * Although during compilation we can see dm_vdo_status_parse() | ||||
|  * in runtime we are linked agains systems libdm 'older' library | ||||
|  * which does not provide this symbol and plugin fails to load | ||||
|  */ | ||||
| #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; | ||||
| } | ||||
| @@ -1,2 +1 @@ | ||||
| dmsetup | ||||
| dmfilemapd | ||||
							
								
								
									
										66
									
								
								daemons/dmfilemapd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								daemons/dmfilemapd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| # | ||||
| # Copyright (C) 2016 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 | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| SOURCES = dmfilemapd.c | ||||
|  | ||||
| TARGETS = dmfilemapd | ||||
|  | ||||
| .PHONY: install_dmfilemapd install_dmfilemapd_static | ||||
|  | ||||
| INSTALL_DMFILEMAPD_TARGETS = install_dmfilemapd_dynamic | ||||
|  | ||||
| CLEAN_TARGETS = dmfilemapd.static | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
| CFLOW_TARGET = dmfilemapd | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| all: device-mapper | ||||
| device-mapper: $(TARGETS) | ||||
|  | ||||
| CFLAGS_dmfilemapd.o += $(EXTRA_EXEC_CFLAGS) | ||||
| LIBS += -ldevmapper | ||||
|  | ||||
| dmfilemapd: $(LIB_SHARED) dmfilemapd.o | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \ | ||||
| 		-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS) | ||||
|  | ||||
| dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o $(interfacebuilddir)/libdevmapper.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L$(interfacebuilddir) \ | ||||
| 		-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS) $(STATIC_LIBS) | ||||
|  | ||||
| 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/dmfilemapd/$(LIB_NAME).cflow | ||||
| endif | ||||
|  | ||||
| install_dmfilemapd_dynamic: dmfilemapd | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|  | ||||
| install_dmfilemapd_static: dmfilemapd.static | ||||
| 	$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F) | ||||
|  | ||||
| install_dmfilemapd: $(INSTALL_DMFILEMAPD_TARGETS) | ||||
|  | ||||
| install: install_dmfilemapd | ||||
|  | ||||
| install_device-mapper: install_dmfilemapd | ||||
| @@ -14,8 +14,11 @@ | ||||
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
| 
 | ||||
| #include "util.h" | ||||
| #include "libdm/misc/dm-logging.h" | ||||
| #include "tool.h" | ||||
| 
 | ||||
| #include "dm-logging.h" | ||||
| 
 | ||||
| #include "defaults.h" | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| @@ -26,15 +29,13 @@ | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| #ifdef __linux__ | ||||
| #  include "libdm/misc/kdev_t.h" | ||||
| #  include "kdev_t.h" | ||||
| #else | ||||
| #  define MAJOR(x) major((x)) | ||||
| #  define MINOR(x) minor((x)) | ||||
| #  define MKDEV(x,y) makedev((x),(y)) | ||||
| #endif | ||||
| 
 | ||||
| #define DEFAULT_PROC_DIR "/proc" | ||||
| 
 | ||||
| /* limit to two updates/sec */ | ||||
| #define FILEMAPD_WAIT_USECS 500000 | ||||
| 
 | ||||
| @@ -180,14 +181,14 @@ static int _is_open_in_pid(pid_t pid, const char *path) | ||||
| 		link_buf[len] = '\0'; | ||||
| 		if (!strcmp(deleted_path, link_buf)) { | ||||
| 			if (closedir(pid_d)) | ||||
| 				log_sys_debug("closedir", path_buf); | ||||
| 				log_sys_error("closedir", path_buf); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| bad: | ||||
| 	if (closedir(pid_d)) | ||||
| 		log_sys_debug("closedir", path_buf); | ||||
| 		log_sys_error("closedir", path_buf); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @@ -230,13 +231,13 @@ static int _is_open(const char *path) | ||||
| 			continue; | ||||
| 		if (_is_open_in_pid(pid, path)) { | ||||
| 			if (closedir(proc_d)) | ||||
| 				log_sys_debug("closedir", DEFAULT_PROC_DIR); | ||||
| 				log_sys_error("closedir", DEFAULT_PROC_DIR); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (closedir(proc_d)) | ||||
| 		log_sys_debug("closedir", DEFAULT_PROC_DIR); | ||||
| 		log_sys_error("closedir", DEFAULT_PROC_DIR); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @@ -310,7 +311,7 @@ static int _parse_args(int argc, char **argv, struct filemap_monitor *fm) | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	fm->path = strdup(argv[0]); | ||||
| 	fm->path = dm_strdup(argv[0]); | ||||
| 	if (!fm->path) { | ||||
| 		_early_log("Could not allocate memory for path argument."); | ||||
| 		return 0; | ||||
| @@ -537,8 +538,8 @@ static void _filemap_monitor_destroy(struct filemap_monitor *fm) | ||||
| 		_filemap_monitor_end_notify(fm); | ||||
| 		_filemap_monitor_close_fd(fm); | ||||
| 	} | ||||
| 	free((void *) fm->program_id); | ||||
| 	free(fm->path); | ||||
| 	dm_free((void *) fm->program_id); | ||||
| 	dm_free(fm->path); | ||||
| } | ||||
| 
 | ||||
| static int _filemap_monitor_check_same_file(int fd1, int fd2) | ||||
| @@ -628,10 +629,10 @@ check_unlinked: | ||||
| 
 | ||||
| static int _daemonise(struct filemap_monitor *fm) | ||||
| { | ||||
| 	pid_t pid = 0; | ||||
| 	pid_t pid = 0, sid; | ||||
| 	int fd; | ||||
| 
 | ||||
| 	if (!setsid()) { | ||||
| 	if (!(sid = setsid())) { | ||||
| 		_early_log("setsid failed."); | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -698,7 +699,7 @@ static int _update_regions(struct dm_stats *dms, struct filemap_monitor *fm) | ||||
| 			 fm->group_id, regions[0]); | ||||
| 		fm->group_id = regions[0]; | ||||
| 	} | ||||
| 	free(regions); | ||||
| 	dm_free(regions); | ||||
| 	fm->nr_regions = nr_regions; | ||||
| 	return 1; | ||||
| } | ||||
| @@ -740,7 +741,7 @@ static int _dmfilemapd(struct filemap_monitor *fm) | ||||
| 	 */ | ||||
| 	program_id = dm_stats_get_region_program_id(dms, fm->group_id); | ||||
| 	if (program_id) | ||||
| 		fm->program_id = strdup(program_id); | ||||
| 		fm->program_id = dm_strdup(program_id); | ||||
| 	else | ||||
| 		fm->program_id = NULL; | ||||
| 	dm_stats_set_program_id(dms, 1, program_id); | ||||
| @@ -801,7 +802,7 @@ bad: | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static const char * const _mode_names[] = { | ||||
| static const char * _mode_names[] = { | ||||
| 	"inode", | ||||
| 	"path" | ||||
| }; | ||||
| @@ -816,7 +817,7 @@ int main(int argc, char **argv) | ||||
| 	memset(&fm, 0, sizeof(fm)); | ||||
| 
 | ||||
| 	if (!_parse_args(argc, argv, &fm)) { | ||||
| 		free(fm.path); | ||||
| 		dm_free(fm.path); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -826,10 +827,8 @@ int main(int argc, char **argv) | ||||
| 		 "mode=%s, path=%s", fm.fd, fm.group_id, | ||||
| 		 _mode_names[fm.mode], fm.path); | ||||
| 
 | ||||
| 	if (!_foreground && !_daemonise(&fm)) { | ||||
| 		free(fm.path); | ||||
| 	if (!_foreground && !_daemonise(&fm)) | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return _dmfilemapd(&fm); | ||||
| } | ||||
							
								
								
									
										3
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1 @@ | ||||
| path.py | ||||
| lvmdbusd | ||||
| lvmdb.py | ||||
| lvm_shell_proxy.py | ||||
|   | ||||
| @@ -15,8 +15,7 @@ srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| lvmdbuspydir = $(python3dir)/lvmdbusd | ||||
| lvmdbusdir = $(DESTDIR)$(lvmdbuspydir) | ||||
| lvmdbusdir = $(python3dir)/lvmdbusd | ||||
|  | ||||
| LVMDBUS_SRCDIR_FILES = \ | ||||
| 	automatedproperties.py \ | ||||
| @@ -24,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 \ | ||||
| @@ -35,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: $(LVMDBUSD) | ||||
| 	@echo "    [INSTALL] $<" | ||||
| 	$(Q) $(INSTALL_DIR) $(sbindir) | ||||
| 	$(Q) $(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir) | ||||
| 	$(Q) $(INSTALL_DIR) $(lvmdbusdir) | ||||
| 	$(Q) (cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(lvmdbusdir)) | ||||
| 	$(Q) $(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(lvmdbusdir) | ||||
| 	$(Q) PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbuspydir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES) | ||||
| 	$(Q) $(CHMOD) 755 $(lvmdbusdir)/__pycache__ | ||||
| 	$(Q) $(CHMOD) 444 $(lvmdbusdir)/__pycache__/*.py[co] | ||||
| install_lvmdbusd: | ||||
| 	$(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) | ||||
|   | ||||
| @@ -155,7 +155,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: | ||||
|   | ||||
| @@ -158,6 +158,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() | ||||
|   | ||||
| @@ -47,11 +47,9 @@ BUS_NAME = os.getenv('LVM_DBUS_NAME', 'com.redhat.lvmdbus1') | ||||
| BASE_INTERFACE = 'com.redhat.lvmdbus1' | ||||
| PV_INTERFACE = BASE_INTERFACE + '.Pv' | ||||
| VG_INTERFACE = BASE_INTERFACE + '.Vg' | ||||
| VG_VDO_INTERFACE = BASE_INTERFACE + '.VgVdo' | ||||
| LV_INTERFACE = BASE_INTERFACE + '.Lv' | ||||
| LV_COMMON_INTERFACE = BASE_INTERFACE + '.LvCommon' | ||||
| THIN_POOL_INTERFACE = BASE_INTERFACE + '.ThinPool' | ||||
| VDO_POOL_INTERFACE = BASE_INTERFACE + '.VdoPool' | ||||
| CACHE_POOL_INTERFACE = BASE_INTERFACE + '.CachePool' | ||||
| LV_CACHED = BASE_INTERFACE + '.CachedLv' | ||||
| SNAPSHOT_INTERFACE = BASE_INTERFACE + '.Snapshot' | ||||
| @@ -63,7 +61,6 @@ PV_OBJ_PATH = BASE_OBJ_PATH + '/Pv' | ||||
| VG_OBJ_PATH = BASE_OBJ_PATH + '/Vg' | ||||
| LV_OBJ_PATH = BASE_OBJ_PATH + '/Lv' | ||||
| THIN_POOL_PATH = BASE_OBJ_PATH + "/ThinPool" | ||||
| VDO_POOL_PATH = BASE_OBJ_PATH + "/VdoPool" | ||||
| CACHE_POOL_PATH = BASE_OBJ_PATH + "/CachePool" | ||||
| HIDDEN_LV_PATH = BASE_OBJ_PATH + "/HiddenLv" | ||||
| MANAGER_OBJ_PATH = BASE_OBJ_PATH + '/Manager' | ||||
| @@ -74,7 +71,6 @@ pv_id = itertools.count() | ||||
| vg_id = itertools.count() | ||||
| lv_id = itertools.count() | ||||
| thin_id = itertools.count() | ||||
| vdo_id = itertools.count() | ||||
| cache_pool_id = itertools.count() | ||||
| job_id = itertools.count() | ||||
| hidden_lv = itertools.count() | ||||
| @@ -83,9 +79,6 @@ hidden_lv = itertools.count() | ||||
| load = None | ||||
| event = None | ||||
|  | ||||
| # Boolean to denote if lvm supports VDO integration | ||||
| vdo_support = False | ||||
|  | ||||
| # Global cached state | ||||
| db = None | ||||
|  | ||||
| @@ -94,13 +87,3 @@ 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() | ||||
|   | ||||
| @@ -67,7 +67,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") | ||||
|  | ||||
| @@ -217,10 +217,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 +263,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,7 +280,7 @@ 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(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	pv_dest_ranges(cmd, pv_dests) | ||||
| 	return call(cmd) | ||||
| @@ -295,7 +292,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,9 +303,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(['--thin', '--size', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend(['--yes']) | ||||
| 	return cmd | ||||
| @@ -323,10 +320,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,13 +336,13 @@ 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']) | ||||
| 	return call(cmd) | ||||
| @@ -366,8 +363,8 @@ 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(['--mirrors', str(num_copies)]) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	return call(cmd) | ||||
|  | ||||
| @@ -388,24 +385,6 @@ def vg_create_thin_pool(md_full_name, data_full_name, create_options): | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_create_vdo_pool_lv_and_lv(vg_name, pool_name, lv_name, data_size, | ||||
| 									virtual_size, create_options): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['-y', '--type', 'vdo', '-n', lv_name, | ||||
| 				'-L', '%dB' % data_size, '-V', '%dB' % virtual_size, | ||||
| 				"%s/%s" % (vg_name, pool_name)]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_create_vdo_pool(pool_full_name, lv_name, virtual_size, create_options): | ||||
| 	cmd = ['lvconvert'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--type', 'vdo-pool', '-n', lv_name, '--force', '-y', | ||||
| 				'-V', '%dB' % virtual_size, pool_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_remove(lv_path, remove_options): | ||||
| 	cmd = ['lvremove'] | ||||
| 	cmd.extend(options_to_cli_args(remove_options)) | ||||
| @@ -439,7 +418,7 @@ 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(['--virtualsize', str(size_bytes) + 'B', '-T']) | ||||
| 	cmd.extend(['--name', name, lv_full_name, '--yes']) | ||||
| 	return call(cmd) | ||||
|  | ||||
| @@ -453,15 +432,6 @@ def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options): | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_writecache_lv(cache_lv_full_name, lv_full_name, cache_options): | ||||
| 	# lvconvert --type writecache --cachevol VG/CacheLV VG/OriginLV | ||||
| 	cmd = ['lvconvert'] | ||||
| 	cmd.extend(options_to_cli_args(cache_options)) | ||||
| 	cmd.extend(['-y', '--type', 'writecache', '--cachevol', | ||||
| 				cache_lv_full_name, lv_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_detach_cache(lv_full_name, detach_options, destroy_cache): | ||||
| 	cmd = ['lvconvert'] | ||||
| 	if destroy_cache: | ||||
| @@ -477,28 +447,6 @@ def lv_detach_cache(lv_full_name, detach_options, destroy_cache): | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_vdo_compression(lv_path, enable, comp_options): | ||||
| 	cmd = ['lvchange', '--compression'] | ||||
| 	if enable: | ||||
| 		cmd.append('y') | ||||
| 	else: | ||||
| 		cmd.append('n') | ||||
| 	cmd.extend(options_to_cli_args(comp_options)) | ||||
| 	cmd.append(lv_path) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_vdo_deduplication(lv_path, enable, dedup_options): | ||||
| 	cmd = ['lvchange', '--deduplication'] | ||||
| 	if enable: | ||||
| 		cmd.append('y') | ||||
| 	else: | ||||
| 		cmd.append('n') | ||||
| 	cmd.extend(options_to_cli_args(dedup_options)) | ||||
| 	cmd.append(lv_path) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def supports_json(): | ||||
| 	cmd = ['help'] | ||||
| 	rc, out, err = call(cmd) | ||||
| @@ -511,16 +459,6 @@ def supports_json(): | ||||
| 	return False | ||||
|  | ||||
|  | ||||
| def supports_vdo(): | ||||
| 	cmd = ['segtypes'] | ||||
| 	rc, out, err = call(cmd) | ||||
| 	if rc == 0: | ||||
| 		if "vdo" in out: | ||||
| 			log_debug("We have VDO support") | ||||
| 			return True | ||||
| 	return False | ||||
|  | ||||
|  | ||||
| def lvm_full_report_json(): | ||||
| 	pv_columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free', | ||||
| 					'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free', | ||||
| @@ -548,22 +486,6 @@ def lvm_full_report_json(): | ||||
|  | ||||
| 	lv_seg_columns = ['seg_pe_ranges', 'segtype', 'lv_uuid'] | ||||
|  | ||||
| 	if cfg.vdo_support: | ||||
| 		lv_columns.extend( | ||||
| 			['vdo_operating_mode', 'vdo_compression_state', 'vdo_index_state', | ||||
| 				'vdo_used_size', 'vdo_saving_percent'] | ||||
| 		) | ||||
|  | ||||
| 		lv_seg_columns.extend( | ||||
| 			['vdo_compression', 'vdo_deduplication', | ||||
| 				'vdo_use_metadata_hints', 'vdo_minimum_io_size', | ||||
| 				'vdo_block_map_cache_size', 'vdo_block_map_era_length', | ||||
| 				'vdo_use_sparse_index', 'vdo_index_memory_size', | ||||
| 				'vdo_slab_size', 'vdo_ack_threads', 'vdo_bio_threads', | ||||
| 				'vdo_bio_rotation', 'vdo_cpu_threads', 'vdo_hash_zone_threads', | ||||
| 				'vdo_logical_threads', 'vdo_physical_threads', | ||||
| 				'vdo_max_discard', 'vdo_write_policy', 'vdo_header_size']) | ||||
|  | ||||
| 	cmd = _dc('fullreport', [ | ||||
| 		'-a',		# Need hidden too | ||||
| 		'--configreport', 'pv', '-o', ','.join(pv_columns), | ||||
| @@ -575,8 +497,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 | ||||
| @@ -634,7 +555,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(['--yes', '--setphysicalvolumesize', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend([device]) | ||||
| 	return call(cmd) | ||||
| @@ -730,12 +651,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): | ||||
| @@ -777,7 +698,6 @@ def activate_deactivate(op, name, activate, control_flags, options): | ||||
| 		op += 'n' | ||||
|  | ||||
| 	cmd.append(op) | ||||
| 	cmd.append("-y") | ||||
| 	cmd.append(name) | ||||
| 	return call(cmd) | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,6 @@ from . import cfg | ||||
| from .utils import MThreadRunner, log_debug, log_error | ||||
| import threading | ||||
| import queue | ||||
| import time | ||||
| import traceback | ||||
|  | ||||
|  | ||||
| @@ -29,26 +28,11 @@ def _main_thread_load(refresh=True, emit_signal=True): | ||||
| 		refresh=refresh, | ||||
| 		emit_signal=emit_signal, | ||||
| 		cache_refresh=False)[1] | ||||
|  | ||||
| 	lv_changes = load_lvs( | ||||
| 	num_total_changes += load_lvs( | ||||
| 		refresh=refresh, | ||||
| 		emit_signal=emit_signal, | ||||
| 		cache_refresh=False)[1] | ||||
|  | ||||
| 	num_total_changes += lv_changes | ||||
|  | ||||
| 	# When the LVs change it can cause another change in the VGs which is | ||||
| 	# missed if we don't scan through the VGs again.  We could achieve this | ||||
| 	# the other way and re-scan the LVs, but in general there are more LVs than | ||||
| 	# VGs, thus this should be more efficient.  This happens when a LV interface | ||||
| 	# changes causing the dbus object representing it to be removed and | ||||
| 	# recreated. | ||||
| 	if refresh and lv_changes > 0: | ||||
| 		num_total_changes += load_vgs( | ||||
| 			refresh=refresh, | ||||
| 			emit_signal=emit_signal, | ||||
| 			cache_refresh=False)[1] | ||||
|  | ||||
| 	return num_total_changes | ||||
|  | ||||
|  | ||||
| @@ -98,8 +82,6 @@ class StateUpdate(object): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def update_thread(obj): | ||||
| 		exception_count = 0 | ||||
|  | ||||
| 		queued_requests = [] | ||||
| 		while cfg.run.value != 0: | ||||
| 			# noinspection PyBroadException | ||||
| @@ -154,26 +136,12 @@ class StateUpdate(object): | ||||
| 				# 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() | ||||
| @@ -184,8 +152,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,22 +10,20 @@ | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| from .utils import vg_obj_path_generate, log_error, _handle_execute | ||||
| from .utils import vg_obj_path_generate | ||||
| import dbus | ||||
| from . import cmdhandler | ||||
| from . import cfg | ||||
| from .cfg import LV_INTERFACE, THIN_POOL_INTERFACE, SNAPSHOT_INTERFACE, \ | ||||
| 	LV_COMMON_INTERFACE, CACHE_POOL_INTERFACE, LV_CACHED, VDO_POOL_INTERFACE | ||||
| 	LV_COMMON_INTERFACE, CACHE_POOL_INTERFACE, LV_CACHED | ||||
| from .request import RequestEntry | ||||
| from .utils import n, n32, d | ||||
| from .utils import n, n32 | ||||
| from .loader import common | ||||
| from .state import State | ||||
| 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 | ||||
| @@ -74,66 +72,23 @@ def lvs_state_retrieve(selection, cache_refresh=True): | ||||
| 	lvs = sorted(cfg.db.fetch_lvs(selection), key=get_key) | ||||
|  | ||||
| 	for l in lvs: | ||||
| 		if cfg.vdo_support: | ||||
| 			rc.append(LvStateVdo( | ||||
| 				l['lv_uuid'], l['lv_name'], | ||||
| 				l['lv_path'], n(l['lv_size']), | ||||
| 				l['vg_name'], | ||||
| 				l['vg_uuid'], l['pool_lv_uuid'], | ||||
| 				l['pool_lv'], l['origin_uuid'], l['origin'], | ||||
| 				n32(l['data_percent']), l['lv_attr'], | ||||
| 				l['lv_tags'], l['lv_active'], l['data_lv'], | ||||
| 				l['metadata_lv'], l['segtype'], l['lv_role'], | ||||
| 				l['lv_layout'], | ||||
| 				n32(l['snap_percent']), | ||||
| 				n32(l['metadata_percent']), | ||||
| 				n32(l['copy_percent']), | ||||
| 				n32(l['sync_percent']), | ||||
| 				n(l['lv_metadata_size']), | ||||
| 				l['move_pv'], | ||||
| 				l['move_pv_uuid'], | ||||
| 				l['vdo_operating_mode'], | ||||
| 				l['vdo_compression_state'], | ||||
| 				l['vdo_index_state'], | ||||
| 				n(l['vdo_used_size']), | ||||
| 				d(l['vdo_saving_percent']), | ||||
| 				l['vdo_compression'], | ||||
| 				l['vdo_deduplication'], | ||||
| 				l['vdo_use_metadata_hints'], | ||||
| 				n32(l['vdo_minimum_io_size']), | ||||
| 				n(l['vdo_block_map_cache_size']), | ||||
| 				n32(l['vdo_block_map_era_length']), | ||||
| 				l['vdo_use_sparse_index'], | ||||
| 				n(l['vdo_index_memory_size']), | ||||
| 				n(l['vdo_slab_size']), | ||||
| 				n32(l['vdo_ack_threads']), | ||||
| 				n32(l['vdo_bio_threads']), | ||||
| 				n32(l['vdo_bio_rotation']), | ||||
| 				n32(l['vdo_cpu_threads']), | ||||
| 				n32(l['vdo_hash_zone_threads']), | ||||
| 				n32(l['vdo_logical_threads']), | ||||
| 				n32(l['vdo_physical_threads']), | ||||
| 				n32(l['vdo_max_discard']), | ||||
| 				l['vdo_write_policy'], | ||||
| 				n32(l['vdo_header_size']))) | ||||
| 		else: | ||||
| 			rc.append(LvState( | ||||
| 				l['lv_uuid'], l['lv_name'], | ||||
| 				l['lv_path'], n(l['lv_size']), | ||||
| 				l['vg_name'], | ||||
| 				l['vg_uuid'], l['pool_lv_uuid'], | ||||
| 				l['pool_lv'], l['origin_uuid'], l['origin'], | ||||
| 				n32(l['data_percent']), l['lv_attr'], | ||||
| 				l['lv_tags'], l['lv_active'], l['data_lv'], | ||||
| 				l['metadata_lv'], l['segtype'], l['lv_role'], | ||||
| 				l['lv_layout'], | ||||
| 				n32(l['snap_percent']), | ||||
| 				n32(l['metadata_percent']), | ||||
| 				n32(l['copy_percent']), | ||||
| 				n32(l['sync_percent']), | ||||
| 				n(l['lv_metadata_size']), | ||||
| 				l['move_pv'], | ||||
| 				l['move_pv_uuid'])) | ||||
| 		rc.append(LvState( | ||||
| 			l['lv_uuid'], l['lv_name'], | ||||
| 			l['lv_path'], n(l['lv_size']), | ||||
| 			l['vg_name'], | ||||
| 			l['vg_uuid'], l['pool_lv_uuid'], | ||||
| 			l['pool_lv'], l['origin_uuid'], l['origin'], | ||||
| 			n32(l['data_percent']), l['lv_attr'], | ||||
| 			l['lv_tags'], l['lv_active'], l['data_lv'], | ||||
| 			l['metadata_lv'], l['segtype'], l['lv_role'], | ||||
| 			l['lv_layout'], | ||||
| 			n32(l['snap_percent']), | ||||
| 			n32(l['metadata_percent']), | ||||
| 			n32(l['copy_percent']), | ||||
| 			n32(l['sync_percent']), | ||||
| 			n(l['lv_metadata_size']), | ||||
| 			l['move_pv'], | ||||
| 			l['move_pv_uuid'])) | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| @@ -237,8 +192,6 @@ class LvState(State): | ||||
| 	def _object_type_create(self): | ||||
| 		if self.Attr[0] == 't': | ||||
| 			return LvThinPool | ||||
| 		elif self.Attr[0] == 'd': | ||||
| 			return LvVdoPool | ||||
| 		elif self.Attr[0] == 'C': | ||||
| 			if 'pool' in self.layout: | ||||
| 				return LvCachePool | ||||
| @@ -265,34 +218,6 @@ class LvState(State): | ||||
| 		return (klass, path_method) | ||||
|  | ||||
|  | ||||
| class LvStateVdo(LvState): | ||||
|  | ||||
| 	def __init__(self, Uuid, Name, Path, SizeBytes, | ||||
| 					vg_name, vg_uuid, pool_lv_uuid, PoolLv, | ||||
| 					origin_uuid, OriginLv, DataPercent, Attr, Tags, active, | ||||
| 					data_lv, metadata_lv, segtypes, role, layout, SnapPercent, | ||||
| 					MetaDataPercent, CopyPercent, SyncPercent, | ||||
| 					MetaDataSizeBytes, move_pv, move_pv_uuid, | ||||
| 					vdo_operating_mode, vdo_compression_state, vdo_index_state, | ||||
| 					vdo_used_size,vdo_saving_percent,vdo_compression, | ||||
| 					vdo_deduplication,vdo_use_metadata_hints, | ||||
| 					vdo_minimum_io_size,vdo_block_map_cache_size, | ||||
| 					vdo_block_map_era_length,vdo_use_sparse_index, | ||||
| 					vdo_index_memory_size,vdo_slab_size,vdo_ack_threads, | ||||
| 					vdo_bio_threads,vdo_bio_rotation,vdo_cpu_threads, | ||||
| 					vdo_hash_zone_threads,vdo_logical_threads, | ||||
| 					vdo_physical_threads,vdo_max_discard, | ||||
| 					vdo_write_policy,vdo_header_size): | ||||
| 		super(LvStateVdo, self).__init__(Uuid, Name, Path, SizeBytes, | ||||
| 					vg_name, vg_uuid, pool_lv_uuid, PoolLv, | ||||
| 					origin_uuid, OriginLv, DataPercent, Attr, Tags, active, | ||||
| 					data_lv, metadata_lv, segtypes, role, layout, SnapPercent, | ||||
| 					MetaDataPercent, CopyPercent, SyncPercent, | ||||
| 					MetaDataSizeBytes, move_pv, move_pv_uuid) | ||||
|  | ||||
| 		utils.init_class_from_arguments(self, "vdo_", snake_to_pascal=True) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Uuid', 's') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's') | ||||
| @@ -307,6 +232,7 @@ class LvStateVdo(LvState): | ||||
| @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') | ||||
| @@ -348,7 +274,13 @@ class LvCommon(AutomatedProperties): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def handle_execute(rc, out, err): | ||||
| 		_handle_execute(rc, out, err, LV_INTERFACE) | ||||
| 		if rc == 0: | ||||
| 			cfg.load() | ||||
| 		else: | ||||
| 			# Need to work on error handling, need consistent | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def validate_dbus_object(lv_uuid, lv_name): | ||||
| @@ -360,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', | ||||
| @@ -388,16 +304,17 @@ class LvCommon(AutomatedProperties): | ||||
| 					'l': 'mirror log device', 'c': 'under conversion', | ||||
| 					'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data', | ||||
| 					'e': 'raid or pool metadata or pool metadata spare', | ||||
| 					'd': 'vdo pool', 'D': 'vdo pool data', 'g': 'integrity', | ||||
| 					'-': '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): | ||||
| @@ -406,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): | ||||
| @@ -414,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): | ||||
| @@ -443,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 +430,8 @@ class Lv(LvCommon): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		# Remove the LV, if successful then remove from the model | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_remove(lv_name, remove_options)) | ||||
| 		rc, out, err = cmdhandler.lv_remove(lv_name, remove_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -544,8 +451,9 @@ class Lv(LvCommon): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		# Rename the logical volume | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_rename(lv_name, new_name, | ||||
| 												rename_options)) | ||||
| 		rc, out, err = cmdhandler.lv_rename(lv_name, new_name, | ||||
| 											rename_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -590,15 +498,17 @@ 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 | ||||
|  | ||||
| 		LvCommon.handle_execute(*cmdhandler.vg_lv_snapshot( | ||||
| 			lv_name, snapshot_options,name, optional_size)) | ||||
| 		rc, out, err = cmdhandler.vg_lv_snapshot( | ||||
| 			lv_name, snapshot_options, name, optional_size) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		full_name = "%s/%s" % (dbo.vg_name_lookup(), name) | ||||
| 		return cfg.om.get_object_path_by_lvm_id(full_name) | ||||
|  | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='stia{sv}', | ||||
| @@ -634,8 +544,9 @@ class Lv(LvCommon): | ||||
| 				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 		size_change = new_size_bytes - dbo.SizeBytes | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_resize( | ||||
| 			dbo.lvm_id, size_change,pv_dests, resize_options)) | ||||
| 		rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change, | ||||
| 											pv_dests, resize_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return "/" | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -670,8 +581,9 @@ class Lv(LvCommon): | ||||
| 								options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(uuid, lv_name) | ||||
| 		LvCommon.handle_execute(*cmdhandler.activate_deactivate( | ||||
| 			'lvchange', lv_name, activate, control_flags, options)) | ||||
| 		rc, out, err = cmdhandler.activate_deactivate( | ||||
| 			'lvchange', lv_name, activate, control_flags, options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -705,8 +617,9 @@ class Lv(LvCommon): | ||||
| 	def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(uuid, lv_name) | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_tag( | ||||
| 			lv_name, tags_add, tags_del, tag_options)) | ||||
| 		rc, out, err = cmdhandler.lv_tag( | ||||
| 			lv_name, tags_add, tags_del, tag_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -743,152 +656,6 @@ class Lv(LvCommon): | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _writecache_lv(lv_uuid, lv_name, lv_object_path, cache_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
|  | ||||
| 		# Make sure we have dbus object representing lv to cache | ||||
| 		lv_to_cache = cfg.om.get_object_by_path(lv_object_path) | ||||
|  | ||||
| 		if lv_to_cache: | ||||
| 			fcn = lv_to_cache.lv_full_name() | ||||
| 			rc, out, err = cmdhandler.lv_writecache_lv( | ||||
| 				dbo.lv_full_name(), fcn, cache_options) | ||||
| 			if rc == 0: | ||||
| 				# When we cache an LV, the cache pool and the lv that is getting | ||||
| 				# cached need to be removed from the object manager and | ||||
| 				# re-created as their interfaces have changed! | ||||
| 				mt_remove_dbus_objects((dbo, lv_to_cache)) | ||||
| 				cfg.load() | ||||
|  | ||||
| 				lv_converted = cfg.om.get_object_path_by_lvm_id(fcn) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, 'LV to cache with object path %s not present!' % | ||||
| 				lv_object_path) | ||||
| 		return lv_converted | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='oia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def WriteCacheLv(self, lv_object, tmo, cache_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._writecache_lv, | ||||
| 			(self.Uuid, self.lvm_id, lv_object, | ||||
| 			cache_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'OperatingMode', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'CompressionState', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'IndexState', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'UsedSize', 't') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'SavingPercent', 'd') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'Compression', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'Deduplication', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'UseMetadataHints', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'MinimumIoSize', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'BlockMapCacheSize', "t") | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'BlockMapEraLength', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'UseSparseIndex', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'IndexMemorySize', 't') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'SlabSize', 't') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'AckThreads', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'BioThreads', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'BioRotation', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'CpuThreads', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'HashZoneThreads', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'LogicalThreads', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'PhysicalThreads', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'MaxDiscard', 'u') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'WritePolicy', 's') | ||||
| @utils.dbus_property(VDO_POOL_INTERFACE, 'HeaderSize', 'u') | ||||
| class LvVdoPool(Lv): | ||||
| 	_DataLv_meta = ("o", VDO_POOL_INTERFACE) | ||||
|  | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(LvVdoPool, self).__init__(object_path, object_state) | ||||
| 		self.set_interface(VDO_POOL_INTERFACE) | ||||
| 		self._data_lv, _ = self._get_data_meta() | ||||
|  | ||||
| 	@property | ||||
| 	def DataLv(self): | ||||
| 		return dbus.ObjectPath(self._data_lv) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _enable_disable_compression(pool_uuid, pool_name, enable, comp_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(pool_uuid, pool_name) | ||||
| 		# Rename the logical volume | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_vdo_compression( | ||||
| 			pool_name, enable, comp_options)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VDO_POOL_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def EnableCompression(self, tmo, comp_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvVdoPool._enable_disable_compression, | ||||
| 			(self.Uuid, self.lvm_id, True, comp_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 	dbus_interface=VDO_POOL_INTERFACE, | ||||
| 	in_signature='ia{sv}', | ||||
| 	out_signature='o', | ||||
| 	async_callbacks=('cb', 'cbe')) | ||||
| 	def DisableCompression(self, tmo, comp_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvVdoPool._enable_disable_compression, | ||||
| 			(self.Uuid, self.lvm_id, False, comp_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _enable_disable_deduplication(pool_uuid, pool_name, enable, dedup_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(pool_uuid, pool_name) | ||||
| 		# Rename the logical volume | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_vdo_deduplication( | ||||
| 			pool_name, enable, dedup_options)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VDO_POOL_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def EnableDeduplication(self, tmo, dedup_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvVdoPool._enable_disable_deduplication, | ||||
| 			(self.Uuid, self.lvm_id, True, dedup_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 	dbus_interface=VDO_POOL_INTERFACE, | ||||
| 	in_signature='ia{sv}', | ||||
| 	out_signature='o', | ||||
| 	async_callbacks=('cb', 'cbe')) | ||||
| 	def DisableDeduplication(self, tmo, dedup_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvVdoPool._enable_disable_deduplication, | ||||
| 			(self.Uuid, self.lvm_id, False, dedup_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class LvThinPool(Lv): | ||||
| @@ -912,8 +679,10 @@ class LvThinPool(Lv): | ||||
| 	def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		LvCommon.handle_execute(*cmdhandler.lv_lv_create( | ||||
| 			lv_name, create_options, name, size_bytes)) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.lv_lv_create( | ||||
| 			lv_name, create_options, name, size_bytes) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		full_name = "%s/%s" % (dbo.vg_name_lookup(), name) | ||||
| 		return cfg.om.get_object_path_by_lvm_id(full_name) | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								daemons/lvmdbusd/lvm_shell_proxy.py.in → daemons/lvmdbusd/lvm_shell_proxy.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										7
									
								
								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. | ||||
| # | ||||
| @@ -220,10 +220,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() | ||||
							
								
								
									
										46
									
								
								daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										46
									
								
								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. | ||||
| # | ||||
| @@ -20,7 +20,7 @@ from lvmdbusd.utils import log_debug, log_error | ||||
| 
 | ||||
| 
 | ||||
| class DataStore(object): | ||||
| 	def __init__(self, usejson=True, vdo_support=False): | ||||
| 	def __init__(self, usejson=True): | ||||
| 		self.pvs = {} | ||||
| 		self.vgs = {} | ||||
| 		self.lvs = {} | ||||
| @@ -43,8 +43,6 @@ class DataStore(object): | ||||
| 		else: | ||||
| 			self.json = usejson | ||||
| 
 | ||||
| 		self.vdo_support = vdo_support | ||||
| 
 | ||||
| 	@staticmethod | ||||
| 	def _insert_record(table, key, record, allowed_multiple): | ||||
| 		if key in table: | ||||
| @@ -143,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 | ||||
| @@ -173,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 | ||||
| @@ -243,7 +223,8 @@ class DataStore(object): | ||||
| 
 | ||||
| 		return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup) | ||||
| 
 | ||||
| 	def _parse_lvs_json(self, _all): | ||||
| 	@staticmethod | ||||
| 	def _parse_lvs_json(_all): | ||||
| 
 | ||||
| 		c_lvs = OrderedDict() | ||||
| 		c_lv_full_lookup = {} | ||||
| @@ -263,13 +244,8 @@ class DataStore(object): | ||||
| 				if 'seg' in r: | ||||
| 					for s in r['seg']: | ||||
| 						r = c_lvs[s['lv_uuid']] | ||||
| 						r.setdefault('seg_pe_ranges', []).\ | ||||
| 							append(s['seg_pe_ranges']) | ||||
| 						r.setdefault('seg_pe_ranges', []).append(s['seg_pe_ranges']) | ||||
| 						r.setdefault('segtype', []).append(s['segtype']) | ||||
| 						if self.vdo_support: | ||||
| 							for seg_key, seg_val in s.items(): | ||||
| 								if seg_key.startswith("vdo_"): | ||||
| 									r[seg_key] = seg_val | ||||
| 
 | ||||
| 		return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup) | ||||
| 
 | ||||
| @@ -545,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. | ||||
| # | ||||
| @@ -29,7 +29,7 @@ from .utils import log_debug, log_error | ||||
| import argparse | ||||
| import os | ||||
| import sys | ||||
| from .cmdhandler import LvmFlightRecorder, supports_vdo | ||||
| from .cmdhandler import LvmFlightRecorder | ||||
| from .request import RequestEntry | ||||
|  | ||||
|  | ||||
| @@ -44,10 +44,10 @@ def process_request(): | ||||
| 		try: | ||||
| 			req = cfg.worker_q.get(True, 5) | ||||
| 			log_debug( | ||||
| 				"Method start: %s with args %s (callback = %s)" % | ||||
| 				(str(req.method), str(req.arguments), str(req.cb))) | ||||
| 				"Running method: %s with args %s" % | ||||
| 				(str(req.method), str(req.arguments))) | ||||
| 			req.run_cmd() | ||||
| 			log_debug("Method complete: %s" % str(req.method)) | ||||
| 			log_debug("Method complete ") | ||||
| 		except queue.Empty: | ||||
| 			pass | ||||
| 		except Exception: | ||||
| @@ -63,24 +63,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 | ||||
| @@ -127,18 +109,15 @@ def main(): | ||||
| 		log_error("You cannot specify --lvmshell and --nojson") | ||||
| 		sys.exit(1) | ||||
|  | ||||
| 	# We will dynamically add interfaces which support vdo if it | ||||
| 	# exists. | ||||
| 	cfg.vdo_support = supports_vdo() | ||||
|  | ||||
| 	if cfg.vdo_support and not cfg.args.use_json: | ||||
| 		log_error("You cannot specify --nojson when lvm has VDO support") | ||||
| 		sys.exit(1) | ||||
|  | ||||
| 	# 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() | ||||
| @@ -155,12 +134,11 @@ def main(): | ||||
| 	cfg.om = Lvm(BASE_OBJ_PATH) | ||||
| 	cfg.om.register_object(Manager(MANAGER_OBJ_PATH)) | ||||
|  | ||||
| 	cfg.db = lvmdb.DataStore(cfg.args.use_json, cfg.vdo_support) | ||||
| 	cfg.db = lvmdb.DataStore(cfg.args.use_json) | ||||
|  | ||||
| 	# 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 | ||||
| @@ -198,7 +176,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 | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class Manager(AutomatedProperties): | ||||
|  | ||||
| 	@property | ||||
| 	def Version(self): | ||||
| 		return dbus.String('1.1.0') | ||||
| 		return dbus.String('1.0.0') | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def handle_execute(rc, out, err): | ||||
| @@ -107,10 +107,10 @@ class Manager(AutomatedProperties): | ||||
| 		rc = cfg.load(log=False) | ||||
|  | ||||
| 		if rc != 0: | ||||
| 			utils.log_debug('Manager.Refresh - exit %d %d' % (rc, lc), | ||||
| 			utils.log_debug('Manager.Refresh - exit %d' % (rc), | ||||
| 							'bg_black', 'fg_light_red') | ||||
| 		else: | ||||
| 			utils.log_debug('Manager.Refresh - exit %d %d' % (rc, lc)) | ||||
| 			utils.log_debug('Manager.Refresh - exit %d' % (rc)) | ||||
| 		return rc + lc | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -164,8 +164,6 @@ 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) | ||||
|   | ||||
| @@ -189,8 +189,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) | ||||
|  | ||||
| @@ -240,19 +240,39 @@ class ObjectManager(AutomatedProperties): | ||||
| 				return lookup_rc | ||||
| 			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 +339,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 | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import dbus | ||||
| from .cfg import PV_INTERFACE | ||||
| from . import cmdhandler | ||||
| from .utils import vg_obj_path_generate, n, pv_obj_path_generate, \ | ||||
| 	lv_object_path_method, _handle_execute | ||||
| 	lv_object_path_method | ||||
| from .loader import common | ||||
| from .request import RequestEntry | ||||
| from .state import State | ||||
| @@ -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: | ||||
| @@ -138,12 +136,19 @@ class Pv(AutomatedProperties): | ||||
| 		# Remove the PV, if successful then remove from the model | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Pv.validate_dbus_object(pv_uuid, pv_name) | ||||
| 		Pv.handle_execute(*cmdhandler.pv_remove(pv_name, remove_options)) | ||||
| 		rc, out, err = cmdhandler.pv_remove(pv_name, remove_options) | ||||
| 		Pv.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def handle_execute(rc, out, err): | ||||
| 		return _handle_execute(rc, out, err, PV_INTERFACE) | ||||
| 		if rc == 0: | ||||
| 			cfg.load() | ||||
| 		else: | ||||
| 			# Need to work on error handling, need consistent | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				PV_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def validate_dbus_object(pv_uuid, pv_name): | ||||
| @@ -171,8 +176,10 @@ class Pv(AutomatedProperties): | ||||
| 	def _resize(pv_uuid, pv_name, new_size_bytes, resize_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Pv.validate_dbus_object(pv_uuid, pv_name) | ||||
| 		Pv.handle_execute(*cmdhandler.pv_resize(pv_name, new_size_bytes, | ||||
| 												resize_options)) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes, | ||||
| 												resize_options) | ||||
| 		Pv.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -191,8 +198,9 @@ class Pv(AutomatedProperties): | ||||
| 	def _allocation_enabled(pv_uuid, pv_name, yes_no, allocation_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Pv.validate_dbus_object(pv_uuid, pv_name) | ||||
| 		Pv.handle_execute(*cmdhandler.pv_allocatable(pv_name, yes_no, | ||||
| 														allocation_options)) | ||||
| 		rc, out, err = cmdhandler.pv_allocatable( | ||||
| 			pv_name, yes_no, allocation_options) | ||||
| 		Pv.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -20,21 +20,11 @@ from lvmdbusd import cfg | ||||
| # noinspection PyUnresolvedReferences | ||||
| from gi.repository import GLib | ||||
| import threading | ||||
| import traceback | ||||
| import signal | ||||
|  | ||||
|  | ||||
| STDOUT_TTY = os.isatty(sys.stdout.fileno()) | ||||
|  | ||||
|  | ||||
| def _handle_execute(rc, out, err, interface): | ||||
| 	if rc == 0: | ||||
| 		cfg.load() | ||||
| 	else: | ||||
| 		# Need to work on error handling, need consistent | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface, 'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
|  | ||||
| def rtype(dbus_type): | ||||
| 	""" | ||||
| 	Decorator making sure that the decorated function returns a value of | ||||
| @@ -66,20 +56,8 @@ def n32(v): | ||||
| 	return int(float(v)) | ||||
|  | ||||
|  | ||||
| @rtype(dbus.Double) | ||||
| def d(v): | ||||
| 	if not v: | ||||
| 		return 0.0 | ||||
| 	return float(v) | ||||
|  | ||||
|  | ||||
| def _snake_to_pascal(s): | ||||
| 	return ''.join(x.title() for x in s.split('_')) | ||||
|  | ||||
|  | ||||
| # noinspection PyProtectedMember | ||||
| def init_class_from_arguments( | ||||
| 		obj_instance, begin_suffix=None, snake_to_pascal=False): | ||||
| def init_class_from_arguments(obj_instance): | ||||
| 	for k, v in list(sys._getframe(1).f_locals.items()): | ||||
| 		if k != 'self': | ||||
| 			nt = k | ||||
| @@ -90,17 +68,8 @@ def init_class_from_arguments( | ||||
| 			cur = getattr(obj_instance, nt, v) | ||||
|  | ||||
| 			# print 'Init class %s = %s' % (nt, str(v)) | ||||
| 			if not (cur and len(str(cur)) and (v is None or len(str(v))) == 0)\ | ||||
| 					and (begin_suffix is None or nt.startswith(begin_suffix)): | ||||
|  | ||||
| 				if begin_suffix and nt.startswith(begin_suffix): | ||||
| 					name = nt[len(begin_suffix):] | ||||
| 					if snake_to_pascal: | ||||
| 						name = _snake_to_pascal(name) | ||||
|  | ||||
| 					setattr(obj_instance, name, v) | ||||
| 				else: | ||||
| 					setattr(obj_instance, nt, v) | ||||
| 			if not (cur and len(str(cur)) and (v is None or len(str(v))) == 0): | ||||
| 				setattr(obj_instance, nt, v) | ||||
|  | ||||
|  | ||||
| def get_properties(f): | ||||
| @@ -312,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(): | ||||
| @@ -368,8 +302,6 @@ def lv_object_path_method(name, meta): | ||||
| 		return _hidden_lv_obj_path_generate | ||||
| 	elif meta[0][0] == 't': | ||||
| 		return _thin_pool_obj_path_generate | ||||
| 	elif meta[0][0] == 'd': | ||||
| 		return _vdo_pool_object_path_generate | ||||
| 	elif meta[0][0] == 'C' and 'pool' in meta[1]: | ||||
| 		return _cache_pool_obj_path_generate | ||||
|  | ||||
| @@ -387,10 +319,6 @@ def _thin_pool_obj_path_generate(): | ||||
| 	return cfg.THIN_POOL_PATH + "/%d" % next(cfg.thin_id) | ||||
|  | ||||
|  | ||||
| def _vdo_pool_object_path_generate(): | ||||
| 	return cfg.VDO_POOL_PATH + "/%d" % next(cfg.vdo_id) | ||||
|  | ||||
|  | ||||
| def _cache_pool_obj_path_generate(): | ||||
| 	return cfg.CACHE_POOL_PATH + "/%d" % next(cfg.cache_pool_id) | ||||
|  | ||||
| @@ -482,7 +410,7 @@ _ALLOWABLE_CH_SET = set(_ALLOWABLE_CH) | ||||
| _ALLOWABLE_VG_LV_CH = string.ascii_letters + string.digits + '.-_+' | ||||
| _ALLOWABLE_VG_LV_CH_SET = set(_ALLOWABLE_VG_LV_CH) | ||||
| _LV_NAME_RESERVED = ("_cdata", "_cmeta", "_corig", "_mimage", "_mlog", | ||||
| 	"_pmspare", "_rimage", "_rmeta", "_tdata", "_tmeta", "_vorigin", "_vdata") | ||||
| 	"_pmspare", "_rimage", "_rmeta", "_tdata", "_tmeta", "_vorigin") | ||||
|  | ||||
| # Tags can have the characters, based on the code | ||||
| # a-zA-Z0-9._-+/=!:&# | ||||
| @@ -606,27 +534,21 @@ def add_no_notify(cmdline): | ||||
| # 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. | ||||
| # | ||||
| @@ -646,7 +568,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()) | ||||
| @@ -656,21 +577,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): | ||||
|   | ||||
| @@ -10,11 +10,10 @@ | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| from .utils import pv_obj_path_generate, vg_obj_path_generate, n, \ | ||||
| 	_handle_execute | ||||
| from .utils import pv_obj_path_generate, vg_obj_path_generate, n | ||||
| import dbus | ||||
| from . import cfg | ||||
| from .cfg import VG_INTERFACE, VG_VDO_INTERFACE | ||||
| from .cfg import VG_INTERFACE | ||||
| from . import cmdhandler | ||||
| from .request import RequestEntry | ||||
| from .loader import common | ||||
| @@ -47,29 +46,24 @@ def vgs_state_retrieve(selection, cache_refresh=True): | ||||
|  | ||||
| def load_vgs(vg_specific=None, object_path=None, refresh=False, | ||||
| 		emit_signal=False, cache_refresh=True): | ||||
| 	return common(vgs_state_retrieve, (Vg, VgVdo, ), vg_specific, object_path, refresh, | ||||
| 	return common(vgs_state_retrieve, (Vg,), vg_specific, object_path, refresh, | ||||
| 					emit_signal, cache_refresh) | ||||
|  | ||||
|  | ||||
| # 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) | ||||
|  | ||||
| @@ -98,12 +92,8 @@ 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) | ||||
|  | ||||
| 		if cfg.vdo_support: | ||||
| 			return VgVdo(path, self) | ||||
| 		else: | ||||
| 			return Vg(path, self) | ||||
| 				self.Uuid, self.Name, vg_obj_path_generate) | ||||
| 		return Vg(path, self) | ||||
|  | ||||
| 	# noinspection PyMethodMayBeStatic | ||||
| 	def creation_signature(self): | ||||
| @@ -112,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) | ||||
| @@ -144,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): | ||||
| @@ -159,7 +149,13 @@ class Vg(AutomatedProperties): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def handle_execute(rc, out, err): | ||||
| 		return _handle_execute(rc, out, err, VG_INTERFACE) | ||||
| 		if rc == 0: | ||||
| 			cfg.load() | ||||
| 		else: | ||||
| 			# Need to work on error handling, need consistent | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def validate_dbus_object(vg_uuid, vg_name): | ||||
| @@ -175,8 +171,9 @@ class Vg(AutomatedProperties): | ||||
| 	def _rename(uuid, vg_name, new_name, rename_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_rename( | ||||
| 			uuid, new_name, rename_options)) | ||||
| 		rc, out, err = cmdhandler.vg_rename( | ||||
| 			vg_name, new_name, rename_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -195,7 +192,8 @@ class Vg(AutomatedProperties): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		# Remove the VG, if successful then remove from the model | ||||
| 		Vg.handle_execute(*cmdhandler.vg_remove(vg_name, remove_options)) | ||||
| 		rc, out, err = cmdhandler.vg_remove(vg_name, remove_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -211,13 +209,14 @@ class Vg(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _change(uuid, vg_name, change_options): | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_change(change_options, vg_name)) | ||||
| 		rc, out, err = cmdhandler.vg_change(change_options, vg_name) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	# 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}', | ||||
| @@ -247,8 +246,9 @@ class Vg(AutomatedProperties): | ||||
| 						VG_INTERFACE, | ||||
| 						'PV Object path not found = %s!' % pv_op) | ||||
|  | ||||
| 		Vg.handle_execute(*cmdhandler.vg_reduce( | ||||
| 			vg_name, missing, pv_devices, reduce_options)) | ||||
| 		rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices, | ||||
| 											reduce_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -278,8 +278,9 @@ class Vg(AutomatedProperties): | ||||
| 					VG_INTERFACE, 'PV Object path not found = %s!' % i) | ||||
|  | ||||
| 		if len(extend_devices): | ||||
| 			Vg.handle_execute(*cmdhandler.vg_extend( | ||||
| 				vg_name, extend_devices, extend_options)) | ||||
| 			rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices, | ||||
| 												extend_options) | ||||
| 			Vg.handle_execute(rc, out, err) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, 'No pv_object_paths provided!') | ||||
| @@ -333,8 +334,10 @@ class Vg(AutomatedProperties): | ||||
|  | ||||
| 				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 		Vg.handle_execute(*cmdhandler.vg_lv_create( | ||||
| 			vg_name, create_options, name, size_bytes, pv_dests)) | ||||
| 		rc, out, err = cmdhandler.vg_lv_create( | ||||
| 			vg_name, create_options, name, size_bytes, pv_dests) | ||||
|  | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -372,8 +375,11 @@ class Vg(AutomatedProperties): | ||||
| 			thin_pool, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_lv_create_linear( | ||||
| 			vg_name, create_options, name, size_bytes, thin_pool)) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_linear( | ||||
| 			vg_name, create_options, name, size_bytes, thin_pool) | ||||
|  | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -395,9 +401,10 @@ class Vg(AutomatedProperties): | ||||
| 			stripe_size_kb, thin_pool, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_lv_create_striped( | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_striped( | ||||
| 			vg_name, create_options, name, size_bytes, | ||||
| 			num_stripes, stripe_size_kb, thin_pool)) | ||||
| 			num_stripes, stripe_size_kb, thin_pool) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -422,8 +429,9 @@ class Vg(AutomatedProperties): | ||||
| 			num_copies, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_lv_create_mirror( | ||||
| 			vg_name, create_options, name, size_bytes, num_copies)) | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_mirror( | ||||
| 			vg_name, create_options, name, size_bytes, num_copies) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -446,9 +454,10 @@ class Vg(AutomatedProperties): | ||||
| 						num_stripes, stripe_size_kb, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_lv_create_raid( | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_raid( | ||||
| 			vg_name, create_options, name, raid_type, size_bytes, | ||||
| 			num_stripes, stripe_size_kb)) | ||||
| 			num_stripes, stripe_size_kb) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -546,8 +555,9 @@ class Vg(AutomatedProperties): | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, 'PV object path = %s not found' % p) | ||||
|  | ||||
| 		Vg.handle_execute(*cmdhandler.pv_tag( | ||||
| 			pv_devices, tags_add, tags_del, tag_options)) | ||||
| 		rc, out, err = cmdhandler.pv_tag( | ||||
| 			pv_devices, tags_add, tags_del, tag_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -588,8 +598,9 @@ class Vg(AutomatedProperties): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
|  | ||||
| 		Vg.handle_execute(*cmdhandler.vg_tag( | ||||
| 			vg_name, tags_add, tags_del, tag_options)) | ||||
| 		rc, out, err = cmdhandler.vg_tag( | ||||
| 			vg_name, tags_add, tags_del, tag_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -628,7 +639,8 @@ class Vg(AutomatedProperties): | ||||
| 	def _vg_change_set(uuid, vg_name, method, value, options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*method(vg_name, value, options)) | ||||
| 		rc, out, err = method(vg_name, value, options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -688,8 +700,9 @@ class Vg(AutomatedProperties): | ||||
| 								options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.activate_deactivate( | ||||
| 			'vgchange', vg_name, activate, control_flags, options)) | ||||
| 		rc, out, err = cmdhandler.activate_deactivate( | ||||
| 			'vgchange', vg_name, activate, control_flags, options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -716,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) | ||||
| @@ -777,71 +784,3 @@ class Vg(AutomatedProperties): | ||||
| 	@property | ||||
| 	def Clustered(self): | ||||
| 		return self._attribute(5, 'c') | ||||
|  | ||||
|  | ||||
| class VgVdo(Vg): | ||||
|  | ||||
| 	# noinspection PyUnusedLocal,PyPep8Naming | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(VgVdo, self).__init__(object_path, vgs_state_retrieve) | ||||
| 		self.set_interface(VG_VDO_INTERFACE) | ||||
| 		self._object_path = object_path | ||||
| 		self.state = object_state | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lv_vdo_pool_create_with_lv(uuid, vg_name, pool_name, lv_name, | ||||
| 									data_size, virtual_size, create_options): | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		Vg.handle_execute(*cmdhandler.vg_create_vdo_pool_lv_and_lv( | ||||
| 			vg_name, pool_name, lv_name, data_size, virtual_size, | ||||
| 			create_options)) | ||||
| 		return Vg.fetch_new_lv(vg_name, pool_name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_VDO_INTERFACE, | ||||
| 		in_signature='ssttia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def CreateVdoPoolandLv(self, pool_name, lv_name, data_size, virtual_size, | ||||
| 							tmo, create_options, cb, cbe): | ||||
| 		utils.validate_lv_name(VG_VDO_INTERFACE, self.Name, pool_name) | ||||
| 		utils.validate_lv_name(VG_VDO_INTERFACE, self.Name, lv_name) | ||||
|  | ||||
| 		r = RequestEntry(tmo, VgVdo._lv_vdo_pool_create_with_lv, | ||||
| 							(self.state.Uuid, self.state.lvm_id, | ||||
| 							pool_name, lv_name, round_size(data_size), | ||||
| 							round_size(virtual_size), | ||||
| 							create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _vdo_pool_create(uuid, vg_name, pool_lv, name, virtual_size, create_options): | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
|  | ||||
| 		# Retrieve the full name of the pool lv | ||||
| 		pool = cfg.om.get_object_by_path(pool_lv) | ||||
| 		if not pool: | ||||
| 			msg = 'LV with object path %s not present!' % \ | ||||
| 					(pool_lv) | ||||
| 			raise dbus.exceptions.DBusException(VG_VDO_INTERFACE, msg) | ||||
|  | ||||
| 		Vg.handle_execute(*cmdhandler.vg_create_vdo_pool( | ||||
| 			pool.lv_full_name(), name, virtual_size, | ||||
| 			create_options)) | ||||
| 		return Vg.fetch_new_lv(vg_name, pool.Name) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_VDO_INTERFACE, | ||||
| 		in_signature='ostia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def CreateVdoPool(self, pool_lv, name, virtual_size, | ||||
| 						tmo, create_options, cb, cbe): | ||||
| 		utils.validate_lv_name(VG_VDO_INTERFACE, self.Name, name) | ||||
|  | ||||
| 		r = RequestEntry(tmo, VgVdo._vdo_pool_create, | ||||
| 							(self.state.Uuid, self.state.lvm_id, | ||||
| 							pool_lv, name, | ||||
| 							round_size(virtual_size), | ||||
| 							create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|   | ||||
							
								
								
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| lvmetad | ||||
| lvmetactl | ||||
							
								
								
									
										62
									
								
								daemons/lvmetad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								daemons/lvmetad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| # | ||||
| # 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 = lvmetactl.c | ||||
|  | ||||
| TARGETS = lvmetad lvmetactl | ||||
|  | ||||
| .PHONY: install_lvmetad | ||||
|  | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
| CFLOW_TARGET = lvmetad | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| CFLAGS_lvmetactl.o += $(EXTRA_EXEC_CFLAGS) | ||||
| CFLAGS_lvmetad-core.o += $(EXTRA_EXEC_CFLAGS) | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) | ||||
| LIBS += $(RT_LIBS) $(DAEMON_LIBS) -ldevmapper $(PTHREAD_LIBS) | ||||
|  | ||||
| lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) -ldaemonserver $(LIBS) | ||||
|  | ||||
| lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 	$(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LIBS) | ||||
|  | ||||
| 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; | ||||
| } | ||||
							
								
								
									
										91
									
								
								daemons/lvmetad/lvmetad-client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								daemons/lvmetad/lvmetad-client.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| /* | ||||
|  * 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" | ||||
| #define LVMETAD_DISABLE_REASON_REPAIR		"REPAIR" | ||||
|  | ||||
| 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 | ||||
							
								
								
									
										3021
									
								
								daemons/lvmetad/lvmetad-core.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3021
									
								
								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