mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-11-03 08:23:48 +03:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			dev-dct-pv
			...
			dev-dct-cm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8e85c41dc7 | 
							
								
								
									
										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.
 | 
			
		||||
							
								
								
									
										152
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								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.
 | 
			
		||||
#
 | 
			
		||||
@@ -18,7 +18,7 @@ top_builddir = @top_builddir@
 | 
			
		||||
abs_top_builddir = @abs_top_builddir@
 | 
			
		||||
abs_top_srcdir = @abs_top_srcdir@
 | 
			
		||||
 | 
			
		||||
SUBDIRS = libdm conf daemons include lib libdaemon man scripts tools
 | 
			
		||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools
 | 
			
		||||
 | 
			
		||||
ifeq ("@UDEV_RULES@", "yes")
 | 
			
		||||
  SUBDIRS += udev
 | 
			
		||||
@@ -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,24 +52,22 @@ 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
 | 
			
		||||
 | 
			
		||||
lib: libdaemon $(BASE_TARGET) $(DEVICE_MAPPER_TARGET)
 | 
			
		||||
libdm: include
 | 
			
		||||
libdaemon: include
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
@@ -70,25 +77,28 @@ 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
 | 
			
		||||
cflow: include.cflow
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
CSCOPE_DIRS = base daemons device_mapper include lib libdaemon scripts tools libdm test
 | 
			
		||||
ifneq ("@CSCOPE_CMD@", "")
 | 
			
		||||
cscope.out:
 | 
			
		||||
	@CSCOPE_CMD@ -b -R $(patsubst %,-s%,$(addprefix $(srcdir)/,$(CSCOPE_DIRS)))
 | 
			
		||||
	@CSCOPE_CMD@ -b -R -s$(top_srcdir)
 | 
			
		||||
all: cscope.out
 | 
			
		||||
endif
 | 
			
		||||
DISTCLEAN_TARGETS += cscope.out
 | 
			
		||||
CLEAN_DIRS += autom4te.cache
 | 
			
		||||
 | 
			
		||||
check check_system check_cluster check_local check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock: test
 | 
			
		||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all
 | 
			
		||||
	$(MAKE) -C test $(@)
 | 
			
		||||
 | 
			
		||||
conf.generate man.generate: tools
 | 
			
		||||
conf.generate: tools
 | 
			
		||||
 | 
			
		||||
# how to use parenthesis in makefiles
 | 
			
		||||
leftparen:=(
 | 
			
		||||
@@ -112,15 +122,14 @@ 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
 | 
			
		||||
generate: conf.generate
 | 
			
		||||
	$(MAKE) -C conf generate
 | 
			
		||||
	$(MAKE) -C man generate
 | 
			
		||||
 | 
			
		||||
all_man:
 | 
			
		||||
	$(MAKE) -C man all_man
 | 
			
		||||
@@ -134,7 +143,7 @@ install_system_dirs:
 | 
			
		||||
	$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
 | 
			
		||||
	$(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
 | 
			
		||||
 | 
			
		||||
install_initscripts:
 | 
			
		||||
install_initscripts: 
 | 
			
		||||
	$(MAKE) -C scripts install_initscripts
 | 
			
		||||
 | 
			
		||||
install_systemd_generators:
 | 
			
		||||
@@ -147,36 +156,26 @@ install_systemd_units:
 | 
			
		||||
install_all_man:
 | 
			
		||||
	$(MAKE) -C man install_all_man
 | 
			
		||||
 | 
			
		||||
ifeq ("@PYTHON_BINDINGS@", "yes")
 | 
			
		||||
install_python_bindings:
 | 
			
		||||
	$(MAKE) -C liblvm/python install_python_bindings
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
install_tmpfiles_configuration:
 | 
			
		||||
	$(MAKE) -C scripts install_tmpfiles_configuration
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
@@ -186,26 +185,59 @@ 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 $(addprefix $(top_srcdir)/,$(CSCOPE_DIRS)) -type f -name '*.[ch]' -newer tags 2>/dev/null | head -1)" || $(RM) tags
 | 
			
		||||
	test -f tags || find $(addprefix $(top_srcdir)/,$(CSCOPE_DIRS)) -maxdepth 5 -type f -name '*.[ch]' -exec ctags -a '{}' +
 | 
			
		||||
	test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags 2>/dev/null | head -1)" || $(RM) tags
 | 
			
		||||
	test -f tags || find $(top_srcdir) -maxdepth 5 -type f -name '*.[ch]' -exec ctags -a '{}' +
 | 
			
		||||
 | 
			
		||||
CLEAN_TARGETS += tags
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								README
									
									
									
									
									
								
							@@ -1,23 +1,16 @@
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  http://git.fedorahosted.org/git/lvm2.git
 | 
			
		||||
  git clone git://git.fedorahosted.org/git/lvm2.git
 | 
			
		||||
 | 
			
		||||
Mailing list for general discussion related to LVM2:
 | 
			
		||||
  linux-lvm@redhat.com
 | 
			
		||||
@@ -35,17 +28,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.181-git (2021-08-11)
 | 
			
		||||
1.02.136-git (2016-09-26)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										661
									
								
								WHATS_NEW
									
									
									
									
									
								
							
							
						
						
									
										661
									
								
								WHATS_NEW
									
									
									
									
									
								
							@@ -1,662 +1,5 @@
 | 
			
		||||
Version 2.03.14 - 
 | 
			
		||||
==================================
 | 
			
		||||
  Print info message with too many or too large archived files.
 | 
			
		||||
  Reduce metadata readings during scanning phase.
 | 
			
		||||
  Optimize computation of crc32 check sum with multiple PVs.
 | 
			
		||||
  Enhance recover path on cache creation failure.
 | 
			
		||||
  Filter out unsupported MQ/SMQ cache policy setting.
 | 
			
		||||
  Fix memleak in mpath filter.
 | 
			
		||||
  Support newer location for VDO statistics.
 | 
			
		||||
  Add support for VDO async-unsage write policy.
 | 
			
		||||
  Improve lvm_import_vdo script.
 | 
			
		||||
  Support VDO LV with lvcreate -ky.
 | 
			
		||||
  Fix lvconvert for VDO LV bigger then 2T.
 | 
			
		||||
  Create VDO LVs automatically without zeroing.
 | 
			
		||||
  Rename vdoimport to lvm_import_vdo.
 | 
			
		||||
 | 
			
		||||
Version 2.03.13 - 11th August 2021
 | 
			
		||||
==================================
 | 
			
		||||
  Changes in udev support:
 | 
			
		||||
  - obtain_device_list_from_udev defaults to 0.
 | 
			
		||||
  - see devices/external_device_info_source,
 | 
			
		||||
    devices/obtain_device_list_from_udev, and devices/multipath_wwids_file help
 | 
			
		||||
    in lvm.conf
 | 
			
		||||
  Fix devices file handling of loop with deleted backing file.
 | 
			
		||||
  Fix devices file handling of scsi_debug WWIDs.
 | 
			
		||||
  Fix many static analysis issues.
 | 
			
		||||
  Support --poolmetadataspare with vgsplit and vgmerge.
 | 
			
		||||
  Fix detection of active components of external origin volume.
 | 
			
		||||
  Add vdoimport tool to support conversion of VDO volumes.
 | 
			
		||||
  Support configurable allocation/vdo_pool_header_size.
 | 
			
		||||
  Fix handling of lvconvert --type vdo-pool --virtualsize.
 | 
			
		||||
  Simplified handling of archive() and backup() internal calls.
 | 
			
		||||
  Add 'idm' locking type for IDM lock manager.
 | 
			
		||||
  Fix load of kvdo target when it is not present in memory (2.03.12).
 | 
			
		||||
 | 
			
		||||
Version 2.03.12 - 07th May 2021
 | 
			
		||||
===============================
 | 
			
		||||
  Allow attaching cache to thin data volume.
 | 
			
		||||
  Fix memleak when generating list of outdated pvs.
 | 
			
		||||
  Better hyphenation usage in man pages.
 | 
			
		||||
  Replace use of deprecated security_context_t with char*.
 | 
			
		||||
  Configure supports AIO_LIBS and AIO_CFLAGS.
 | 
			
		||||
  Improve build process for static builds.
 | 
			
		||||
  New --setautoactivation option to modify LV or VG auto activation.
 | 
			
		||||
  New metadata based autoactivation property for LVs and VGs.
 | 
			
		||||
  Improve signal handling with lvmpolld.
 | 
			
		||||
  Signal handler can interrupt command also for SIGTERM.
 | 
			
		||||
  Lvreduce --yes support.
 | 
			
		||||
  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 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.
 | 
			
		||||
  New lvmlockctl_kill_command configuration option.
 | 
			
		||||
  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.
 | 
			
		||||
  Fix IMSM MD RAID detection on 4k devices.
 | 
			
		||||
  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 conversion and allocation of pool metadata.
 | 
			
		||||
  Support thin pool metadata 15.88GiB, adds 64MiB, thin_pool_crop_metadata=0.
 | 
			
		||||
  Enhance lvdisplay to report raid available/partial.
 | 
			
		||||
  Support online rename of VDO pools.
 | 
			
		||||
  Improve 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
 | 
			
		||||
=================================
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
Version 2.02.167 - 
 | 
			
		||||
======================================
 | 
			
		||||
  Prevent raid1 split with trackchanges in a shared VG.
 | 
			
		||||
  Avoid double unlocking of client & lockspace mutexes in lvmlockd.
 | 
			
		||||
  Fix leaking of file descriptor for non-blocking filebased locking.
 | 
			
		||||
  Fix check for 2nd mda at end of disk fits if using pvcreate --restorefile.
 | 
			
		||||
  Use maximum metadataarea size that fits with pvcreate --restorefile.
 | 
			
		||||
  Always clear cached bootloaderarea when wiping label e.g. in pvcreate.
 | 
			
		||||
  Disallow --bootloaderareasize with pvcreate --restorefile.
 | 
			
		||||
  Fix lvmlockd check for running lock managers during lock adoption.
 | 
			
		||||
  Add --withgeneralpreamble and --withlocalpreamble to lvmconfig.
 | 
			
		||||
  Improve makefiles' linking.
 | 
			
		||||
  Fix some paths in generated makefiles to respected configured settings.
 | 
			
		||||
  Add warning when creating thin-pool with zeroing and chunk size >= 512KiB.
 | 
			
		||||
  Introduce exit code 4 EINIT_FAILED to replace -1 when initialisation fails.
 | 
			
		||||
  Add synchronization points with udev during reshape of raid LVs.
 | 
			
		||||
 | 
			
		||||
Version 2.02.173 - 20th July 2017
 | 
			
		||||
=================================
 | 
			
		||||
  Add synchronization points with udev during conversion of raid LVs.
 | 
			
		||||
  Improve --size args validation and report more detailed error message.
 | 
			
		||||
  Initialize debugging mutex before any debug message in clvmd.
 | 
			
		||||
  Log error instead of warn when noticing connection problem with lvmetad.
 | 
			
		||||
  Fix memory leak in lvmetad when working with duplicates.
 | 
			
		||||
  Remove restrictions on reshaping open and clustered raid devices.
 | 
			
		||||
  Add incompatible data_offset to raid metadata to fix reshape activation.
 | 
			
		||||
  Accept 'lvm -h' and 'lvm --help' as well as 'lvm help' for help.
 | 
			
		||||
  Suppress error message from accept() on clean lvmetad shutdown.
 | 
			
		||||
  Tidy clvmd client list processing and fix segfaults.
 | 
			
		||||
  Protect clvmd debug log messages with mutex and add client id.
 | 
			
		||||
  Fix shellcheck reported issues for script files.
 | 
			
		||||
 | 
			
		||||
Version 2.02.172 - 28th June 2017
 | 
			
		||||
=================================
 | 
			
		||||
  Add missing NULL to argv array when spliting cmdline arguments.
 | 
			
		||||
  Add display_percent helper function for printing percent values.
 | 
			
		||||
  lvconvert --repair handles failing raid legs (present but marked 'D'ead).
 | 
			
		||||
  Do not lvdisplay --maps unset settings of cache pool.
 | 
			
		||||
  Fix lvdisplay --maps for cache pool without policy settings.
 | 
			
		||||
  Support aborting of flushing cache LV.
 | 
			
		||||
  Reenable conversion of data and metadata thin-pool volumes to raid.
 | 
			
		||||
  Improve raid status reporting with lvs.
 | 
			
		||||
  No longer necessary to '--force' a repair for RAID1.
 | 
			
		||||
  Linear to RAID1 upconverts now use "recover" sync action, not "resync".
 | 
			
		||||
  Improve lvcreate --cachepool arg validation.
 | 
			
		||||
  Limit maximum size of thin-pool for specific chunk size.
 | 
			
		||||
  Print a warning about in-use PVs with no VG using them.
 | 
			
		||||
  Disable automatic clearing of PVs that look like in-use orphans.
 | 
			
		||||
  Cache format2 flag is now using segment name type field.
 | 
			
		||||
  Support storing status flags via segtype name field.
 | 
			
		||||
  Stop using '--yes' mode when fsadm runs without terminal.
 | 
			
		||||
  Extend validation of filesystems resized by fsadm.
 | 
			
		||||
  Enhance lvconvert automatic settings of possible (raid) LV types.
 | 
			
		||||
  Allow lvchange to change properties on a thin pool data sub LV.
 | 
			
		||||
  Fix lvcreate extent percentage calculation for mirrors.
 | 
			
		||||
  Don't reinstate still-missing devices when correcting inconsistent metadata.
 | 
			
		||||
  Properly handle subshell return codes in fsadm.
 | 
			
		||||
  Disallow cachepool creation with policy cleaner and mode writeback.
 | 
			
		||||
 | 
			
		||||
Version 2.02.171 - 3rd May 2017
 | 
			
		||||
===============================
 | 
			
		||||
  Fix memory warnings by using mempools for command definition processing.
 | 
			
		||||
  Fix running commands from a script file.
 | 
			
		||||
  Add pvcreate prompt when device size doesn't match setphysicalvolumesize.
 | 
			
		||||
  lvconvert - preserve region size on raid1 image count changes
 | 
			
		||||
  Adjust pvresize/pvcreate messages and prompt if underlying dev size differs.
 | 
			
		||||
  raid - sanely handle insufficient space on takeover.
 | 
			
		||||
  Fix configure --enable-notify-dbus status message.
 | 
			
		||||
  Change configure option name prefix from --enable-lockd to --enable-lvmlockd.
 | 
			
		||||
  lvcreate - raise mirror/raid default regionsize to 2MiB
 | 
			
		||||
  Add missing configurable prefix to configuration file installation directory.
 | 
			
		||||
 | 
			
		||||
Version 2.02.170 - 13th April 2017
 | 
			
		||||
==================================
 | 
			
		||||
  Introduce global/fsadm_executable to make fsadm path configurable.
 | 
			
		||||
  Look for limited thin pool metadata size when using 16G metadata.
 | 
			
		||||
  Add lvconvert pool creation rule disallowing options with poolmetadata.
 | 
			
		||||
  Fix lvconvert when the same LV is incorrectly reused in options.
 | 
			
		||||
  Fix lvconvert VG name validation in option values.
 | 
			
		||||
  Fix missing lvmlockd LV locks in lvchange and lvconvert.
 | 
			
		||||
  Fix dmeventd setup for lvchange --poll.
 | 
			
		||||
  Fix use of --poll and --monitor with lvchange and vgchange.
 | 
			
		||||
  Disallow lvconvert of hidden LV to a pool.
 | 
			
		||||
  Ignore --partial option when not used for activation.
 | 
			
		||||
  Allow --activationmode option with lvchange --refresh.
 | 
			
		||||
  Better message on lvconvert --regionsize
 | 
			
		||||
  Allow valid lvconvert --regionsize change
 | 
			
		||||
  Add raid10 alias raid10_near
 | 
			
		||||
  Handle insufficient PVs on lvconvert takeover
 | 
			
		||||
  Fix SIGINT blocking to prevent corrupted metadata
 | 
			
		||||
  Fix systemd unit existence check for lvmconf --services --startstopservices.
 | 
			
		||||
  Check and use PATH_MAX buffers when creating vgrename device paths.
 | 
			
		||||
 | 
			
		||||
Version 2.02.169 - 28th March 2017
 | 
			
		||||
==================================
 | 
			
		||||
  Automatically decide whether '-' in a man page is a hyphen or a minus sign.
 | 
			
		||||
  Add build-time configuration command line to 'lvm version' output.
 | 
			
		||||
  Handle known table line parameter order change in specific raid target vsns.
 | 
			
		||||
  Conditionally reject raid convert to striped/raid0* after reshape.
 | 
			
		||||
  Ensure raid6 upconversion restrictions.
 | 
			
		||||
  Adjust mirror & raid dmeventd plugins for new lvconvert --repair behaviour.
 | 
			
		||||
  Disable lvmetad when lvconvert --repair is run.
 | 
			
		||||
  Remove obsolete lvmchange binary - convert to built-in command.
 | 
			
		||||
  Show more information for cached volumes in lvdisplay [-m].
 | 
			
		||||
  Add option for lvcreate/lvconvert --cachemetadataformat auto|1|2.
 | 
			
		||||
  Support cache segment with configurable metadata format.
 | 
			
		||||
  Add allocation/cache_metadata_format profilable settings.
 | 
			
		||||
  Use function cache_set_params() for both lvcreate and lvconvert.
 | 
			
		||||
  Skip rounding on cache chunk size boudary when create cache LV.
 | 
			
		||||
  Improve cache_set_params support for chunk_size selection.
 | 
			
		||||
  Fix metadata profile allocation/cache_[mode|policy] setting.
 | 
			
		||||
  Fix missing support for using allocation/cache_pool_chunk_size setting.
 | 
			
		||||
  Upstream git moved to https://sourceware.org/git/?p=lvm2
 | 
			
		||||
  Support conversion of raid type, stripesize and number of disks
 | 
			
		||||
  Reject writemostly/writebehind in lvchange during resynchronization.
 | 
			
		||||
  Deactivate active origin first before removal for improved workflow.
 | 
			
		||||
  Fix regression of accepting both --type and -m with lvresize. (2.02.158)
 | 
			
		||||
  Add lvconvert --swapmetadata, new specific way to swap pool metadata LVs.
 | 
			
		||||
  Add lvconvert --startpoll, new specific way to start polling conversions.
 | 
			
		||||
  Add lvconvert --mergethin, new specific way to merge thin snapshots.
 | 
			
		||||
  Add lvconvert --mergemirrors, new specific way to merge split mirrors.
 | 
			
		||||
  Add lvconvert --mergesnapshot, new specific way to combine cow LVs.
 | 
			
		||||
  Split up lvconvert code based on command definitions.
 | 
			
		||||
  Split up lvchange code based on command definitions.
 | 
			
		||||
  Generate help output and man pages from command definitions.
 | 
			
		||||
  Verify all command line items against command definition.
 | 
			
		||||
  Match every command run to one command definition.
 | 
			
		||||
  Specify every allowed command definition/syntax in command-lines.in.
 | 
			
		||||
  Add extra memory page when limiting pthread stack size in clvmd.
 | 
			
		||||
  Support striped/raid0* <-> raid10_near conversions.
 | 
			
		||||
  Support shrinking of RaidLVs.
 | 
			
		||||
  Support region size changes on existing RaidLVs.
 | 
			
		||||
  Avoid parallel usage of cpg_mcast_joined() in clvmd with corosync.
 | 
			
		||||
  Support raid6_{ls,rs,la,ra}_6 segment types and conversions from/to it.
 | 
			
		||||
  Support raid6_n_6 segment type and conversions from/to it.
 | 
			
		||||
  Support raid5_n segment type and conversions from/to it.
 | 
			
		||||
  Support new internal command _dmeventd_thin_command.
 | 
			
		||||
  Introduce new dmeventd/thin_command configurable setting.
 | 
			
		||||
  Use new default units 'r' for displaying sizes.
 | 
			
		||||
  Also unmount mount point on top of MD device if using blkdeactivate -u.
 | 
			
		||||
  Restore check preventing resize of cache type volumes (2.02.158).
 | 
			
		||||
  Add missing udev sync when flushing dirty cache content.
 | 
			
		||||
  vgchange -p accepts only uint32 numbers.
 | 
			
		||||
  Report thin LV date for merged LV when the merge is in progress.
 | 
			
		||||
  Detect if snapshot merge really started before polling for progress.
 | 
			
		||||
  Checking LV for merging origin requires also it has merged snapshot.
 | 
			
		||||
  Extend validation of metadata processing.
 | 
			
		||||
  Enable usage of cached volumes as snapshot origin LV.
 | 
			
		||||
  Fix displayed lv name when splitting snapshot (2.02.146).
 | 
			
		||||
  Warn about command not making metadata backup just once per command.
 | 
			
		||||
  Enable usage of cached volume as thin volume's external origin.
 | 
			
		||||
  Support cache volume activation with -real layer.
 | 
			
		||||
  Improve search of lock-holder for external origin and thin-pool.
 | 
			
		||||
  Support status checking of cache volume used in layer.
 | 
			
		||||
  Avoid shifting by one number of blocks when clearing dirty cache volume.
 | 
			
		||||
  Extend metadata validation of external origin LV use count.
 | 
			
		||||
  Fix dm table when the last user of active external origin is removed.
 | 
			
		||||
  Improve reported lvs status for active external origin volume.
 | 
			
		||||
  Fix table load for splitted RAID LV and require explicit activation.
 | 
			
		||||
  Always active splitted RAID LV exclusively locally.
 | 
			
		||||
  Do not use LV RAID status bit for segment status.
 | 
			
		||||
  Check segtype directly instead of checking RAID in segment status.
 | 
			
		||||
  Reusing exiting code for raid image removal.
 | 
			
		||||
  Fix pvmove leaving -pvmove0 error device in clustered VG.
 | 
			
		||||
  Avoid adding extra '_' at end of raid extracted images or metadata.
 | 
			
		||||
  Optimize another _rmeta clearing code.
 | 
			
		||||
  Fix deactivation of raid orphan devices for clustered VG.
 | 
			
		||||
  Fix lvconvert raid1 to mirror table reload order.
 | 
			
		||||
  Add internal function for separate mirror log preparation.
 | 
			
		||||
  Fix segfault in lvmetad from missing NULL in daemon_reply_simple.
 | 
			
		||||
  Simplify internal _info_run() and use _setup_task_run() for mknod.
 | 
			
		||||
  Better API for internal function _setup_task_run.
 | 
			
		||||
  Avoid using lv_has_target_type() call within lv_info_with_seg_status.
 | 
			
		||||
  Simplify internal lv_info_with_seg_status API.
 | 
			
		||||
  Decide which status is needed in one place for lv_info_with_seg_status.
 | 
			
		||||
  Fix matching of LV segment when checking for it info status.
 | 
			
		||||
  Report log_warn when status cannot be parsed.
 | 
			
		||||
  Test segment type before accessing segment members when checking status.
 | 
			
		||||
  Implement compatible target function for stripe segment.
 | 
			
		||||
  Use status info to report merge failed and snapshot invalid lvs fields.
 | 
			
		||||
 | 
			
		||||
Version 2.02.168 - 30th November 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Display correct sync_percent on large RaidLVs
 | 
			
		||||
  lvmdbusd --blackboxsize <n> added, used to override default size of 16
 | 
			
		||||
  Allow a transiently failed RaidLV to be refreshed
 | 
			
		||||
  Use lv_update_and_reload() inside mirror code where it applies.
 | 
			
		||||
  Preserve mirrored status for temporary layered mirrors.
 | 
			
		||||
  Use transient raid check before repairing raid volume.
 | 
			
		||||
  Implement transient status check for raid volumes.
 | 
			
		||||
  Only log msg as debug if lvm2-lvmdbusd unit missing for D-Bus notification.
 | 
			
		||||
  Avoid duplicated underscore in name of extracted LV image.
 | 
			
		||||
  Missing stripe filler now could be also 'zero'.
 | 
			
		||||
  lvconvert --repair accepts --interval and --background option.
 | 
			
		||||
  More efficiently prepare _rmeta devices when creating a new raid LV.
 | 
			
		||||
 | 
			
		||||
Version 2.02.167 - 5th November 2016
 | 
			
		||||
====================================
 | 
			
		||||
  Use log_error in regex and sysfs filter to describe reason of failure.
 | 
			
		||||
  Fix blkdeactivate to deactivate dev stack if dev on top already unmounted.
 | 
			
		||||
  Prevent non-synced raid1 repair unless --force
 | 
			
		||||
  Prevent raid4 creation/conversion on non-supporting kernels
 | 
			
		||||
  Add direct striped -> raid4 conversion
 | 
			
		||||
  Fix raid4 parity image pair position on conversions from striped/raid0*
 | 
			
		||||
  Fix a few unconverted return code values for some lvconvert error path.
 | 
			
		||||
  Disable lvconvert of thin pool to raid while active.
 | 
			
		||||
  Disable systemd service start rate limiting for lvm2-pvscan@.service.
 | 
			
		||||
@@ -1815,7 +1158,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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										190
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										190
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							@@ -1,191 +1,5 @@
 | 
			
		||||
Version 1.02.181 - 
 | 
			
		||||
===================================
 | 
			
		||||
  Add IMA support with 'dmsetup measure' command.
 | 
			
		||||
  Add defines DM_NAME_LIST_FLAG_HAS_UUID, DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID.
 | 
			
		||||
  Enhance tracking of activated devices when preloading dm tree.
 | 
			
		||||
  Fix bug in construction of cache table line (regression from 1.02.159).
 | 
			
		||||
 | 
			
		||||
Version 1.02.179 - 11th August 2021
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
Version 1.02.177 - 07th May 2021
 | 
			
		||||
================================
 | 
			
		||||
  Configure proceeds without libaio to allow build of device-mapper only.
 | 
			
		||||
  Fix symbol versioning build with -O2 -flto.
 | 
			
		||||
  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.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
 | 
			
		||||
Version 1.02.136 - 
 | 
			
		||||
======================================
 | 
			
		||||
  Restore umask when creation of node fails.
 | 
			
		||||
  Add --concise to dmsetup create for many devices with tables in one command.
 | 
			
		||||
  Accept minor number without major in library when it knows dm major number.
 | 
			
		||||
  Introduce single-line concise table output format: dmsetup table --concise
 | 
			
		||||
 | 
			
		||||
Version 1.02.142 - 20th July 2017
 | 
			
		||||
=================================
 | 
			
		||||
  Create /dev/disk/by-part{uuid,label} and gpt-auto-root symlinks with udev.
 | 
			
		||||
 | 
			
		||||
Version 1.02.141 - 28th June 2017
 | 
			
		||||
=================================
 | 
			
		||||
  Fix reusing of dm_task structure for status reading (used by dmeventd).
 | 
			
		||||
  Add dm_percent_to_round_float for adjusted percentage rounding.
 | 
			
		||||
  Reset array with dead rimage devices once raid gets in sync.
 | 
			
		||||
  Drop unneeded --config option from raid dmeventd plugin.
 | 
			
		||||
  dm_get_status_raid() handle better some incosistent md statuses.
 | 
			
		||||
  Accept truncated files in calls to dm_stats_update_regions_from_fd().
 | 
			
		||||
  Restore Warning by 5% increment when thin-pool is over 80% (1.02.138).
 | 
			
		||||
 | 
			
		||||
Version 1.02.140 - 3rd May 2017
 | 
			
		||||
===============================
 | 
			
		||||
  Add missing configure --enable-dmfilemapd status message and fix --disable.
 | 
			
		||||
 | 
			
		||||
Version 1.02.139 - 13th April 2017
 | 
			
		||||
==================================
 | 
			
		||||
  Fix assignment in _target_version() when dm task can't run.
 | 
			
		||||
  Flush stdout on each iteration when using --count or --interval.
 | 
			
		||||
  Show detailed error message when execvp fails while starting dmfilemapd.
 | 
			
		||||
  Fix segmentation fault when dmfilemapd is run with no arguments.
 | 
			
		||||
  Numerous minor dmfilemapd fixes from coverity.
 | 
			
		||||
 | 
			
		||||
Version 1.02.138 - 28th March 2017
 | 
			
		||||
==================================
 | 
			
		||||
  Support additional raid5/6 configurations.
 | 
			
		||||
  Provide dm_tree_node_add_cache_target@base compatible symbol.
 | 
			
		||||
  Support DM_CACHE_FEATURE_METADATA2, new cache metadata format 2.
 | 
			
		||||
  Improve code to handle mode mask for cache nodes.
 | 
			
		||||
  Cache status check for passthrough also require trailing space.
 | 
			
		||||
  Add extra memory page when limiting pthread stack size in dmeventd.
 | 
			
		||||
  Avoids immediate resume when preloaded device is smaller.
 | 
			
		||||
  Do not suppress kernel key description in dmsetup table output for dm-crypt.
 | 
			
		||||
  Support configurable command executed from dmeventd thin plugin.
 | 
			
		||||
  Support new R|r human readable units output format.
 | 
			
		||||
  Thin dmeventd plugin reacts faster on lvextend failure path with umount.
 | 
			
		||||
  Add dm_stats_bind_from_fd() to bind a stats handle from a file descriptor.
 | 
			
		||||
  Do not try call callback when reverting activation on error path.
 | 
			
		||||
  Fix file mapping for extents with physically adjacent extents in dmstats.
 | 
			
		||||
  Validation vsnprintf result in runtime translate of dm_log (1.02.136).
 | 
			
		||||
  Separate filemap extent allocation from region table in dmstats.
 | 
			
		||||
  Fix segmentation fault when filemap region creation fails in dmstats.
 | 
			
		||||
  Fix performance of region cleanup for failed filemap creation in dmstats.
 | 
			
		||||
  Fix very slow region deletion with many regions in dmstats.
 | 
			
		||||
 | 
			
		||||
Version 1.02.137 - 30th November 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Document raid status values.
 | 
			
		||||
  Always exit dmsetup with success when asked to display help/version.
 | 
			
		||||
 | 
			
		||||
Version 1.02.136 - 5th November 2016
 | 
			
		||||
====================================
 | 
			
		||||
  Log failure of raid device with log_error level.
 | 
			
		||||
  Use dm_log_with_errno and translate runtime to dm_log only when needed.
 | 
			
		||||
  Make log messages from dm and lvm library different from dmeventd.
 | 
			
		||||
  Notice and Info messages are again logged from dmeventd and its plugins.
 | 
			
		||||
  Dmeventd now also respects DM_ABORT_ON_INTERNAL_ERRORS as libdm based tool.
 | 
			
		||||
  Report as non default dm logging also when logging with errno was changed.
 | 
			
		||||
  Use log_level() macro to consistently decode message log level in dmeventd.
 | 
			
		||||
  Still produce output when dmsetup dependency tree building finds dev missing.
 | 
			
		||||
  Check and report pthread_sigmask() failure in dmeventd.
 | 
			
		||||
  Check mem alloc fail in _canonicalize_field_ids().
 | 
			
		||||
  Use unsigned math when checking more then 31 legs of raid.
 | 
			
		||||
@@ -567,7 +381,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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										171
									
								
								acinclude.m4
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								acinclude.m4
									
									
									
									
									
								
							@@ -61,174 +61,3 @@ AC_DEFUN([AC_TRY_LDFLAGS],
 | 
			
		||||
        ifelse([$4], [], [:], [$4])
 | 
			
		||||
    fi
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#      http://www.gnu.org/software/autoconf-archive/ax_gcc_builtin.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
#
 | 
			
		||||
#   AX_GCC_BUILTIN(BUILTIN)
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
#
 | 
			
		||||
#   This macro checks if the compiler supports one of GCC's built-in
 | 
			
		||||
#   functions; many other compilers also provide those same built-ins.
 | 
			
		||||
#
 | 
			
		||||
#   The BUILTIN parameter is the name of the built-in function.
 | 
			
		||||
#
 | 
			
		||||
#   If BUILTIN is supported define HAVE_<BUILTIN>. Keep in mind that since
 | 
			
		||||
#   builtins usually start with two underscores they will be copied over
 | 
			
		||||
#   into the HAVE_<BUILTIN> definition (e.g. HAVE___BUILTIN_EXPECT for
 | 
			
		||||
#   __builtin_expect()).
 | 
			
		||||
#
 | 
			
		||||
#   The macro caches its result in the ax_cv_have_<BUILTIN> variable (e.g.
 | 
			
		||||
#   ax_cv_have___builtin_expect).
 | 
			
		||||
#
 | 
			
		||||
#   The macro currently supports the following built-in functions:
 | 
			
		||||
#
 | 
			
		||||
#    __builtin_assume_aligned
 | 
			
		||||
#    __builtin_bswap16
 | 
			
		||||
#    __builtin_bswap32
 | 
			
		||||
#    __builtin_bswap64
 | 
			
		||||
#    __builtin_choose_expr
 | 
			
		||||
#    __builtin___clear_cache
 | 
			
		||||
#    __builtin_clrsb
 | 
			
		||||
#    __builtin_clrsbl
 | 
			
		||||
#    __builtin_clrsbll
 | 
			
		||||
#    __builtin_clz
 | 
			
		||||
#    __builtin_clzl
 | 
			
		||||
#    __builtin_clzll
 | 
			
		||||
#    __builtin_complex
 | 
			
		||||
#    __builtin_constant_p
 | 
			
		||||
#    __builtin_ctz
 | 
			
		||||
#    __builtin_ctzl
 | 
			
		||||
#    __builtin_ctzll
 | 
			
		||||
#    __builtin_expect
 | 
			
		||||
#    __builtin_ffs
 | 
			
		||||
#    __builtin_ffsl
 | 
			
		||||
#    __builtin_ffsll
 | 
			
		||||
#    __builtin_fpclassify
 | 
			
		||||
#    __builtin_huge_val
 | 
			
		||||
#    __builtin_huge_valf
 | 
			
		||||
#    __builtin_huge_vall
 | 
			
		||||
#    __builtin_inf
 | 
			
		||||
#    __builtin_infd128
 | 
			
		||||
#    __builtin_infd32
 | 
			
		||||
#    __builtin_infd64
 | 
			
		||||
#    __builtin_inff
 | 
			
		||||
#    __builtin_infl
 | 
			
		||||
#    __builtin_isinf_sign
 | 
			
		||||
#    __builtin_nan
 | 
			
		||||
#    __builtin_nand128
 | 
			
		||||
#    __builtin_nand32
 | 
			
		||||
#    __builtin_nand64
 | 
			
		||||
#    __builtin_nanf
 | 
			
		||||
#    __builtin_nanl
 | 
			
		||||
#    __builtin_nans
 | 
			
		||||
#    __builtin_nansf
 | 
			
		||||
#    __builtin_nansl
 | 
			
		||||
#    __builtin_object_size
 | 
			
		||||
#    __builtin_parity
 | 
			
		||||
#    __builtin_parityl
 | 
			
		||||
#    __builtin_parityll
 | 
			
		||||
#    __builtin_popcount
 | 
			
		||||
#    __builtin_popcountl
 | 
			
		||||
#    __builtin_popcountll
 | 
			
		||||
#    __builtin_powi
 | 
			
		||||
#    __builtin_powif
 | 
			
		||||
#    __builtin_powil
 | 
			
		||||
#    __builtin_prefetch
 | 
			
		||||
#    __builtin_trap
 | 
			
		||||
#    __builtin_types_compatible_p
 | 
			
		||||
#    __builtin_unreachable
 | 
			
		||||
#
 | 
			
		||||
#   Unsuppored built-ins will be tested with an empty parameter set and the
 | 
			
		||||
#   result of the check might be wrong or meaningless so use with care.
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
 | 
			
		||||
#
 | 
			
		||||
#   Copying and distribution of this file, with or without modification, are
 | 
			
		||||
#   permitted in any medium without royalty provided the copyright notice
 | 
			
		||||
#   and this notice are preserved.  This file is offered as-is, without any
 | 
			
		||||
#   warranty.
 | 
			
		||||
 | 
			
		||||
serial 3
 | 
			
		||||
 | 
			
		||||
AC_DEFUN([AX_GCC_BUILTIN], [
 | 
			
		||||
    AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1])
 | 
			
		||||
 | 
			
		||||
    AC_CACHE_CHECK([for $1], [ac_var], [
 | 
			
		||||
        AC_LINK_IFELSE([AC_LANG_PROGRAM([], [
 | 
			
		||||
            m4_case([$1],
 | 
			
		||||
                [__builtin_assume_aligned], [$1("", 0)],
 | 
			
		||||
                [__builtin_bswap16], [$1(0)],
 | 
			
		||||
                [__builtin_bswap32], [$1(0)],
 | 
			
		||||
                [__builtin_bswap64], [$1(0)],
 | 
			
		||||
                [__builtin_choose_expr], [$1(0, 0, 0)],
 | 
			
		||||
                [__builtin___clear_cache], [$1("", "")],
 | 
			
		||||
                [__builtin_clrsb], [$1(0)],
 | 
			
		||||
                [__builtin_clrsbl], [$1(0)],
 | 
			
		||||
                [__builtin_clrsbll], [$1(0)],
 | 
			
		||||
                [__builtin_clz], [$1(0)],
 | 
			
		||||
                [__builtin_clzl], [$1(0)],
 | 
			
		||||
                [__builtin_clzll], [$1(0)],
 | 
			
		||||
                [__builtin_complex], [$1(0.0, 0.0)],
 | 
			
		||||
                [__builtin_constant_p], [$1(0)],
 | 
			
		||||
                [__builtin_ctz], [$1(0)],
 | 
			
		||||
                [__builtin_ctzl], [$1(0)],
 | 
			
		||||
                [__builtin_ctzll], [$1(0)],
 | 
			
		||||
                [__builtin_expect], [$1(0, 0)],
 | 
			
		||||
                [__builtin_ffs], [$1(0)],
 | 
			
		||||
                [__builtin_ffsl], [$1(0)],
 | 
			
		||||
                [__builtin_ffsll], [$1(0)],
 | 
			
		||||
                [__builtin_fpclassify], [$1(0, 1, 2, 3, 4, 0.0)],
 | 
			
		||||
                [__builtin_huge_val], [$1()],
 | 
			
		||||
                [__builtin_huge_valf], [$1()],
 | 
			
		||||
                [__builtin_huge_vall], [$1()],
 | 
			
		||||
                [__builtin_inf], [$1()],
 | 
			
		||||
                [__builtin_infd128], [$1()],
 | 
			
		||||
                [__builtin_infd32], [$1()],
 | 
			
		||||
                [__builtin_infd64], [$1()],
 | 
			
		||||
                [__builtin_inff], [$1()],
 | 
			
		||||
                [__builtin_infl], [$1()],
 | 
			
		||||
                [__builtin_isinf_sign], [$1(0.0)],
 | 
			
		||||
                [__builtin_nan], [$1("")],
 | 
			
		||||
                [__builtin_nand128], [$1("")],
 | 
			
		||||
                [__builtin_nand32], [$1("")],
 | 
			
		||||
                [__builtin_nand64], [$1("")],
 | 
			
		||||
                [__builtin_nanf], [$1("")],
 | 
			
		||||
                [__builtin_nanl], [$1("")],
 | 
			
		||||
                [__builtin_nans], [$1("")],
 | 
			
		||||
                [__builtin_nansf], [$1("")],
 | 
			
		||||
                [__builtin_nansl], [$1("")],
 | 
			
		||||
                [__builtin_object_size], [$1("", 0)],
 | 
			
		||||
                [__builtin_parity], [$1(0)],
 | 
			
		||||
                [__builtin_parityl], [$1(0)],
 | 
			
		||||
                [__builtin_parityll], [$1(0)],
 | 
			
		||||
                [__builtin_popcount], [$1(0)],
 | 
			
		||||
                [__builtin_popcountl], [$1(0)],
 | 
			
		||||
                [__builtin_popcountll], [$1(0)],
 | 
			
		||||
                [__builtin_powi], [$1(0, 0)],
 | 
			
		||||
                [__builtin_powif], [$1(0, 0)],
 | 
			
		||||
                [__builtin_powil], [$1(0, 0)],
 | 
			
		||||
                [__builtin_prefetch], [$1("")],
 | 
			
		||||
                [__builtin_trap], [$1()],
 | 
			
		||||
                [__builtin_types_compatible_p], [$1(int, int)],
 | 
			
		||||
                [__builtin_unreachable], [$1()],
 | 
			
		||||
                [m4_warn([syntax], [Unsupported built-in $1, the test may fail])
 | 
			
		||||
                 $1()]
 | 
			
		||||
            )
 | 
			
		||||
            ])],
 | 
			
		||||
            [AS_VAR_SET([ac_var], [yes])],
 | 
			
		||||
            [AS_VAR_SET([ac_var], [no])])
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
    AS_IF([test yes = AS_VAR_GET([ac_var])],
 | 
			
		||||
        [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
 | 
			
		||||
            [Define to 1 if the system has the `$1' built-in function])], [])
 | 
			
		||||
 | 
			
		||||
    AS_VAR_POPDEF([ac_var])
 | 
			
		||||
])
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										306
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										306
									
								
								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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	(cat $(top_srcdir)/conf/example.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --ignorelocal --withspaces) > example.conf.in
 | 
			
		||||
	(cat $(top_srcdir)/conf/lvmlocal.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --withspaces local) > lvmlocal.conf.in
 | 
			
		||||
 | 
			
		||||
install_conf: $(CONFSRC)
 | 
			
		||||
	@if [ ! -e $(confdir)/$(CONFDEST) ]; then \
 | 
			
		||||
@@ -49,9 +48,8 @@ install_localconf: $(CONFLOCAL)
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
install_profiles: $(PROFILES)
 | 
			
		||||
	@echo "    [INSTALL] $<"
 | 
			
		||||
	$(Q) $(INSTALL_DIR) $(profiledir)
 | 
			
		||||
	$(Q) $(INSTALL_DATA) $(PROFILES) $(profiledir)/
 | 
			
		||||
	$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_PROFILE_DIR)
 | 
			
		||||
	$(INSTALL_DATA) $(PROFILES) $(DESTDIR)$(DEFAULT_PROFILE_DIR)/
 | 
			
		||||
 | 
			
		||||
install_lvm2: install_conf install_localconf install_profiles
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,6 @@ allocation {
 | 
			
		||||
	cache_mode = "writethrough"
 | 
			
		||||
	cache_policy = "smq"
 | 
			
		||||
	cache_settings {
 | 
			
		||||
	        # currently no settings for "smq" policy
 | 
			
		||||
	        # currently no settins for "smq" policy
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								conf/example.conf.base
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								conf/example.conf.base
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
# This is an example configuration file for the LVM2 system.
 | 
			
		||||
# It contains the default settings that would be used if there was no
 | 
			
		||||
# @DEFAULT_SYS_DIR@/lvm.conf file.
 | 
			
		||||
#
 | 
			
		||||
# Refer to 'man lvm.conf' for further information including the file layout.
 | 
			
		||||
#
 | 
			
		||||
# Refer to 'man lvm.conf' for information about how settings configured in
 | 
			
		||||
# this file are combined with built-in values and command line options to
 | 
			
		||||
# arrive at the final values used by LVM.
 | 
			
		||||
#
 | 
			
		||||
# Refer to 'man lvmconfig' for information about displaying the built-in
 | 
			
		||||
# and configured values used by LVM.
 | 
			
		||||
#
 | 
			
		||||
# If a default value is set in this file (not commented out), then a
 | 
			
		||||
# new version of LVM using this file will continue using that value,
 | 
			
		||||
# even if the new version of LVM changes the built-in default value.
 | 
			
		||||
#
 | 
			
		||||
# To put this file in a different directory and override @DEFAULT_SYS_DIR@ set
 | 
			
		||||
# the environment variable LVM_SYSTEM_DIR before running the tools.
 | 
			
		||||
#
 | 
			
		||||
# N.B. Take care that each setting only appears once if uncommenting
 | 
			
		||||
# example settings in this file.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1135
									
								
								conf/example.conf.in
									
									
									
									
									
								
							
							
						
						
									
										1135
									
								
								conf/example.conf.in
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										19
									
								
								conf/lvmlocal.conf.base
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								conf/lvmlocal.conf.base
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
# This is a local configuration file template for the LVM2 system
 | 
			
		||||
# which should be installed as @DEFAULT_SYS_DIR@/lvmlocal.conf .
 | 
			
		||||
#
 | 
			
		||||
# Refer to 'man lvm.conf' for information about the file layout.
 | 
			
		||||
#
 | 
			
		||||
# To put this file in a different directory and override
 | 
			
		||||
# @DEFAULT_SYS_DIR@ set the environment variable LVM_SYSTEM_DIR before
 | 
			
		||||
# running the tools.
 | 
			
		||||
#
 | 
			
		||||
# The lvmlocal.conf file is normally expected to contain only the
 | 
			
		||||
# "local" section which contains settings that should not be shared or
 | 
			
		||||
# repeated among different hosts.  (But if other sections are present,
 | 
			
		||||
# they *will* get processed.  Settings in this file override equivalent
 | 
			
		||||
# ones in lvm.conf and are in turn overridden by ones in any enabled
 | 
			
		||||
# lvm_<tag>.conf files.)
 | 
			
		||||
#
 | 
			
		||||
# Please take care that each setting only appears once if uncommenting
 | 
			
		||||
# example settings in this file and never copy this file between hosts.
 | 
			
		||||
 | 
			
		||||
@@ -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
											
										
									
								
							@@ -41,21 +41,6 @@ struct lv_segment *last_seg(const struct logical_volume *lv)
 | 
			
		||||
	return ((struct lv_segment **)lv)[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
 | 
			
		||||
{
 | 
			
		||||
	return "STRING";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
struct logical_volume *origin_from_cow(const struct logical_volume *lv)
 | 
			
		||||
{
 | 
			
		||||
	if (lv)
 | 
			
		||||
		return lv;
 | 
			
		||||
 | 
			
		||||
	__coverity_panic__();
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* simple_memccpy() from glibc */
 | 
			
		||||
void *memccpy(void *dest, const void *src, int c, size_t n)
 | 
			
		||||
{
 | 
			
		||||
@@ -86,17 +71,6 @@ void model_FD_ZERO(void *fdset)
 | 
			
		||||
		((long*)fdset)[i] = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Resent Coverity reports quite weird errors... */
 | 
			
		||||
int *__errno_location(void)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
const unsigned short **__ctype_b_loc (void)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Added extra pointer check to not need these models,
 | 
			
		||||
 * for now just keep then in file
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,11 @@ srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
.PHONY: dmeventd cmirrord lvmpolld lvmlockd
 | 
			
		||||
.PHONY: dmeventd clvmd cmirrord lvmetad lvmpolld lvmlockd
 | 
			
		||||
 | 
			
		||||
ifneq ("@CLVMD@", "none")
 | 
			
		||||
  SUBDIRS += clvmd
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_CMIRRORD@", "yes")
 | 
			
		||||
  SUBDIRS += cmirrord
 | 
			
		||||
@@ -28,20 +32,24 @@ daemons.cflow: dmeventd.cflow
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_LVMETAD@", "yes")
 | 
			
		||||
  SUBDIRS += lvmetad
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_LVMPOLLD@", "yes")
 | 
			
		||||
  SUBDIRS += lvmpolld
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_LVMLOCKD@", "yes")
 | 
			
		||||
  SUBDIRS += lvmlockd
 | 
			
		||||
  SUBDIRS += lvmlockd 
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_LVMDBUSD@", "yes")
 | 
			
		||||
  SUBDIRS += lvmdbusd
 | 
			
		||||
  SUBDIRS += lvmdbusd 
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(MAKECMDGOALS),distclean)
 | 
			
		||||
  SUBDIRS = cmirrord dmeventd lvmpolld lvmlockd lvmdbusd
 | 
			
		||||
  SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
clvmd
 | 
			
		||||
							
								
								
									
										103
									
								
								daemons/clvmd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								daemons/clvmd/Makefile.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of LVM2.
 | 
			
		||||
#
 | 
			
		||||
# This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
# modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
# of the GNU General Public License v.2.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
CMAN_LIBS = @CMAN_LIBS@
 | 
			
		||||
CMAN_CFLAGS = @CMAN_CFLAGS@
 | 
			
		||||
CMAP_LIBS = @CMAP_LIBS@
 | 
			
		||||
CMAP_CFLAGS = @CMAP_CFLAGS@
 | 
			
		||||
CONFDB_LIBS = @CONFDB_LIBS@
 | 
			
		||||
CONFDB_CFLAGS = @CONFDB_CFLAGS@
 | 
			
		||||
CPG_LIBS = @CPG_LIBS@
 | 
			
		||||
CPG_CFLAGS = @CPG_CFLAGS@
 | 
			
		||||
DLM_LIBS = @DLM_LIBS@
 | 
			
		||||
DLM_CFLAGS = @DLM_CFLAGS@
 | 
			
		||||
QUORUM_LIBS = @QUORUM_LIBS@
 | 
			
		||||
QUORUM_CFLAGS = @QUORUM_CFLAGS@
 | 
			
		||||
SALCK_LIBS = @SALCK_LIBS@
 | 
			
		||||
SALCK_CFLAGS = @SALCK_CFLAGS@
 | 
			
		||||
 | 
			
		||||
SOURCES = \
 | 
			
		||||
	clvmd-command.c  \
 | 
			
		||||
	clvmd.c          \
 | 
			
		||||
	lvm-functions.c  \
 | 
			
		||||
	refresh_clvmd.c
 | 
			
		||||
 | 
			
		||||
ifneq (,$(findstring cman,, "@CLVMD@,"))
 | 
			
		||||
	SOURCES += clvmd-cman.c
 | 
			
		||||
	LMLIBS += $(CMAN_LIBS) $(CONFDB_LIBS) $(DLM_LIBS)
 | 
			
		||||
	CFLAGS += $(CMAN_CFLAGS) $(CONFDB_CFLAGS) $(DLM_CFLAGS)
 | 
			
		||||
	DEFS += -DUSE_CMAN
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(findstring openais,, "@CLVMD@,"))
 | 
			
		||||
	SOURCES += clvmd-openais.c
 | 
			
		||||
	LMLIBS += $(CONFDB_LIBS) $(CPG_LIBS) $(SALCK_LIBS)
 | 
			
		||||
	CFLAGS += $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(SALCK_CFLAGS)
 | 
			
		||||
	DEFS += -DUSE_OPENAIS
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(findstring corosync,, "@CLVMD@,"))
 | 
			
		||||
	SOURCES += clvmd-corosync.c
 | 
			
		||||
	LMLIBS += $(CMAP_LIBS) $(CONFDB_LIBS) $(CPG_LIBS) $(DLM_LIBS) $(QUORUM_LIBS)
 | 
			
		||||
	CFLAGS += $(CMAP_CFLAGS) $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(DLM_CFLAGS) $(QUORUM_CFLAGS)
 | 
			
		||||
	DEFS += -DUSE_COROSYNC
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifneq (,$(findstring singlenode,, "@CLVMD@,"))
 | 
			
		||||
	SOURCES += clvmd-singlenode.c
 | 
			
		||||
	DEFS += -DUSE_SINGLENODE
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(MAKECMDGOALS),distclean)
 | 
			
		||||
	SOURCES += clvmd-cman.c
 | 
			
		||||
	SOURCES += clvmd-openais.c
 | 
			
		||||
	SOURCES += clvmd-corosync.c
 | 
			
		||||
	SOURCES += clvmd-singlenode.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
TARGETS = \
 | 
			
		||||
	clvmd
 | 
			
		||||
 | 
			
		||||
LVMLIBS = $(LVMINTERNAL_LIBS)
 | 
			
		||||
 | 
			
		||||
ifeq ("@DMEVENTD@", "yes")
 | 
			
		||||
	LVMLIBS += -ldevmapper-event
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
LVMLIBS += -ldevmapper
 | 
			
		||||
LIBS += $(PTHREAD_LIBS)
 | 
			
		||||
 | 
			
		||||
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS)
 | 
			
		||||
 | 
			
		||||
INSTALL_TARGETS = \
 | 
			
		||||
	install_clvmd
 | 
			
		||||
 | 
			
		||||
clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -o clvmd $(OBJECTS) \
 | 
			
		||||
		$(LVMLIBS) $(LMLIBS) $(LIBS)
 | 
			
		||||
 | 
			
		||||
.PHONY: install_clvmd
 | 
			
		||||
 | 
			
		||||
install_clvmd: $(TARGETS)
 | 
			
		||||
	$(INSTALL_PROGRAM) -D clvmd $(usrsbindir)/clvmd
 | 
			
		||||
 | 
			
		||||
install: $(INSTALL_TARGETS)
 | 
			
		||||
 | 
			
		||||
install_cluster: $(INSTALL_TARGETS)
 | 
			
		||||
							
								
								
									
										85
									
								
								daemons/clvmd/clvm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								daemons/clvmd/clvm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Definitions for CLVMD server and clients */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The protocol spoken over the cluster and across the local socket.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _CLVM_H
 | 
			
		||||
#define _CLVM_H
 | 
			
		||||
 | 
			
		||||
#include "configure.h"
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
struct clvm_header {
 | 
			
		||||
	uint8_t  cmd;	        /* See below */
 | 
			
		||||
	uint8_t  flags;	        /* See below */
 | 
			
		||||
	uint16_t xid;	        /* Transaction ID */
 | 
			
		||||
	uint32_t clientid;	/* Only used in Daemon->Daemon comms */
 | 
			
		||||
	int32_t  status;	/* For replies, whether request succeeded */
 | 
			
		||||
	uint32_t arglen;	/* Length of argument below.
 | 
			
		||||
				   If >1500 then it will be passed
 | 
			
		||||
				   around the cluster in the system LV */
 | 
			
		||||
	char node[1];		/* Actually a NUL-terminated string, node name.
 | 
			
		||||
				   If this is empty then the command is
 | 
			
		||||
				   forwarded to all cluster nodes unless
 | 
			
		||||
				   FLAG_LOCAL or FLAG_REMOTE is also set. */
 | 
			
		||||
	char args[1];		/* Arguments for the command follow the
 | 
			
		||||
				   node name, This member is only
 | 
			
		||||
				   valid if the node name is empty */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* Flags */
 | 
			
		||||
#define CLVMD_FLAG_LOCAL	1	/* Only do this on the local node */
 | 
			
		||||
#define CLVMD_FLAG_SYSTEMLV	2	/* Data in system LV under my node name */
 | 
			
		||||
#define CLVMD_FLAG_NODEERRS	4	/* Reply has errors in node-specific portion */
 | 
			
		||||
#define CLVMD_FLAG_REMOTE	8	/* Do this on all nodes except for the local node */
 | 
			
		||||
 | 
			
		||||
/* Name of the local socket to communicate between lvm and clvmd */
 | 
			
		||||
#define CLVMD_SOCKNAME		DEFAULT_RUN_DIR "/clvmd.sock"
 | 
			
		||||
 | 
			
		||||
/* Internal commands & replies */
 | 
			
		||||
#define CLVMD_CMD_REPLY    1
 | 
			
		||||
#define CLVMD_CMD_VERSION  2	/* Send version around cluster when we start */
 | 
			
		||||
#define CLVMD_CMD_GOAWAY   3	/* Die if received this - we are running
 | 
			
		||||
				   an incompatible version */
 | 
			
		||||
#define CLVMD_CMD_TEST     4	/* Just for mucking about */
 | 
			
		||||
 | 
			
		||||
#define CLVMD_CMD_LOCK              30
 | 
			
		||||
#define CLVMD_CMD_UNLOCK            31
 | 
			
		||||
 | 
			
		||||
/* Lock/Unlock commands */
 | 
			
		||||
#define CLVMD_CMD_LOCK_LV           50
 | 
			
		||||
#define CLVMD_CMD_LOCK_VG           51
 | 
			
		||||
#define CLVMD_CMD_LOCK_QUERY	    52
 | 
			
		||||
 | 
			
		||||
/* Misc functions */
 | 
			
		||||
#define CLVMD_CMD_REFRESH	    40
 | 
			
		||||
#define CLVMD_CMD_GET_CLUSTERNAME   41
 | 
			
		||||
#define CLVMD_CMD_SET_DEBUG	    42
 | 
			
		||||
#define CLVMD_CMD_VG_BACKUP	    43
 | 
			
		||||
#define CLVMD_CMD_RESTART	    44
 | 
			
		||||
#define CLVMD_CMD_SYNC_NAMES	    45
 | 
			
		||||
 | 
			
		||||
/* Used internally by some callers, but not part of the protocol.*/
 | 
			
		||||
#ifndef NODE_ALL
 | 
			
		||||
#  define NODE_ALL	"*"
 | 
			
		||||
#  define NODE_LOCAL	"."
 | 
			
		||||
#  define NODE_REMOTE	"^"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										505
									
								
								daemons/clvmd/clvmd-cman.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								daemons/clvmd/clvmd-cman.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,505 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * CMAN communication layer for clvmd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#include "clvmd-comms.h"
 | 
			
		||||
#include "clvm.h"
 | 
			
		||||
#include "clvmd.h"
 | 
			
		||||
#include "lvm-functions.h"
 | 
			
		||||
 | 
			
		||||
#include <libdlm.h>
 | 
			
		||||
 | 
			
		||||
#include <syslog.h>
 | 
			
		||||
 | 
			
		||||
#define LOCKSPACE_NAME "clvmd"
 | 
			
		||||
 | 
			
		||||
struct clvmd_node
 | 
			
		||||
{
 | 
			
		||||
	struct cman_node *node;
 | 
			
		||||
	int clvmd_up;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int num_nodes;
 | 
			
		||||
static struct cman_node *nodes = NULL;
 | 
			
		||||
static struct cman_node this_node;
 | 
			
		||||
static int count_nodes; /* size of allocated nodes array */
 | 
			
		||||
static struct dm_hash_table *node_updown_hash;
 | 
			
		||||
static dlm_lshandle_t *lockspace;
 | 
			
		||||
static cman_handle_t c_handle;
 | 
			
		||||
 | 
			
		||||
static void count_clvmds_running(void);
 | 
			
		||||
static void get_members(void);
 | 
			
		||||
static int nodeid_from_csid(const char *csid);
 | 
			
		||||
static int name_from_nodeid(int nodeid, char *name);
 | 
			
		||||
static void event_callback(cman_handle_t handle, void *private, int reason, int arg);
 | 
			
		||||
static void data_callback(cman_handle_t handle, void *private,
 | 
			
		||||
			  char *buf, int len, uint8_t port, int nodeid);
 | 
			
		||||
 | 
			
		||||
struct lock_wait {
 | 
			
		||||
	pthread_cond_t cond;
 | 
			
		||||
	pthread_mutex_t mutex;
 | 
			
		||||
	struct dlm_lksb lksb;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _init_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	node_updown_hash = dm_hash_create(100);
 | 
			
		||||
 | 
			
		||||
	/* Open the cluster communication socket */
 | 
			
		||||
	c_handle = cman_init(NULL);
 | 
			
		||||
	if (!c_handle) {
 | 
			
		||||
		syslog(LOG_ERR, "Can't open cluster manager socket: %m");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	DEBUGLOG("Connected to CMAN\n");
 | 
			
		||||
 | 
			
		||||
	if (cman_start_recv_data(c_handle, data_callback, CLUSTER_PORT_CLVMD)) {
 | 
			
		||||
		syslog(LOG_ERR, "Can't bind cluster socket: %m");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cman_start_notification(c_handle, event_callback)) {
 | 
			
		||||
		syslog(LOG_ERR, "Can't start cluster event listening");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get the cluster members list */
 | 
			
		||||
	get_members();
 | 
			
		||||
	count_clvmds_running();
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("CMAN initialisation complete\n");
 | 
			
		||||
 | 
			
		||||
	/* Create a lockspace for LV & VG locks to live in */
 | 
			
		||||
	lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
 | 
			
		||||
	if (!lockspace) {
 | 
			
		||||
		lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
 | 
			
		||||
		if (!lockspace) {
 | 
			
		||||
			syslog(LOG_ERR, "Unable to create DLM lockspace for CLVM: %m");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		DEBUGLOG("Created DLM lockspace for CLVMD.\n");
 | 
			
		||||
	} else
 | 
			
		||||
		DEBUGLOG("Opened existing DLM lockspace for CLVMD.\n");
 | 
			
		||||
 | 
			
		||||
	dlm_ls_pthread_init(lockspace);
 | 
			
		||||
	DEBUGLOG("DLM initialisation complete\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _cluster_init_completed(void)
 | 
			
		||||
{
 | 
			
		||||
	clvmd_cluster_init_completed();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_main_cluster_fd(void)
 | 
			
		||||
{
 | 
			
		||||
	return cman_get_fd(c_handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_num_nodes(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int nnodes = 0;
 | 
			
		||||
 | 
			
		||||
	/* return number of ACTIVE nodes */
 | 
			
		||||
	for (i=0; i<num_nodes; i++) {
 | 
			
		||||
		if (nodes[i].cn_member && nodes[i].cn_nodeid)
 | 
			
		||||
			nnodes++;
 | 
			
		||||
	}
 | 
			
		||||
	return nnodes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* send_message with the fd check removed */
 | 
			
		||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
 | 
			
		||||
				 const char *errtext)
 | 
			
		||||
{
 | 
			
		||||
	int nodeid = 0;
 | 
			
		||||
 | 
			
		||||
	if (csid)
 | 
			
		||||
		memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
 | 
			
		||||
 | 
			
		||||
	if (cman_send_data(c_handle, buf, msglen, 0, CLUSTER_PORT_CLVMD, nodeid) <= 0)
 | 
			
		||||
	{
 | 
			
		||||
		log_error("%s", errtext);
 | 
			
		||||
	}
 | 
			
		||||
	return msglen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _get_our_csid(char *csid)
 | 
			
		||||
{
 | 
			
		||||
	if (this_node.cn_nodeid == 0) {
 | 
			
		||||
		cman_get_node(c_handle, 0, &this_node);
 | 
			
		||||
	}
 | 
			
		||||
	memcpy(csid, &this_node.cn_nodeid, CMAN_MAX_CSID_LEN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call a callback routine for each node is that known (down means not running a clvmd) */
 | 
			
		||||
static int _cluster_do_node_callback(struct local_client *client,
 | 
			
		||||
				     void (*callback) (struct local_client *,
 | 
			
		||||
						       const char *,
 | 
			
		||||
						       int))
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int somedown = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < _get_num_nodes(); i++) {
 | 
			
		||||
		if (nodes[i].cn_member && nodes[i].cn_nodeid) {
 | 
			
		||||
			int up = (int)(long)dm_hash_lookup_binary(node_updown_hash, (char *)&nodes[i].cn_nodeid, sizeof(int));
 | 
			
		||||
 | 
			
		||||
			callback(client, (char *)&nodes[i].cn_nodeid, up);
 | 
			
		||||
			if (!up)
 | 
			
		||||
				somedown = -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return somedown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Process OOB messages from the cluster socket */
 | 
			
		||||
static void event_callback(cman_handle_t handle, void *private, int reason, int arg)
 | 
			
		||||
{
 | 
			
		||||
	char namebuf[MAX_CLUSTER_MEMBER_NAME_LEN];
 | 
			
		||||
 | 
			
		||||
	switch (reason) {
 | 
			
		||||
        case CMAN_REASON_PORTCLOSED:
 | 
			
		||||
		name_from_nodeid(arg, namebuf);
 | 
			
		||||
		log_notice("clvmd on node %s has died\n", namebuf);
 | 
			
		||||
		DEBUGLOG("Got port closed message, removing node %s\n", namebuf);
 | 
			
		||||
 | 
			
		||||
		dm_hash_insert_binary(node_updown_hash, (char *)&arg, sizeof(int), (void *)0);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CMAN_REASON_STATECHANGE:
 | 
			
		||||
		DEBUGLOG("Got state change message, re-reading members list\n");
 | 
			
		||||
		get_members();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
#if defined(LIBCMAN_VERSION) && LIBCMAN_VERSION >= 2
 | 
			
		||||
	case CMAN_REASON_PORTOPENED:
 | 
			
		||||
		/* Ignore this, wait for startup message from clvmd itself */
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CMAN_REASON_TRY_SHUTDOWN:
 | 
			
		||||
		DEBUGLOG("Got try shutdown, sending OK\n");
 | 
			
		||||
		cman_replyto_shutdown(c_handle, 1);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	default:
 | 
			
		||||
		/* ERROR */
 | 
			
		||||
		DEBUGLOG("Got unknown event callback message: %d\n", reason);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct local_client *cman_client;
 | 
			
		||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
 | 
			
		||||
				const char *csid,
 | 
			
		||||
				struct local_client **new_client)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/* Save this for data_callback */
 | 
			
		||||
	cman_client = fd;
 | 
			
		||||
 | 
			
		||||
	/* We never return a new client */
 | 
			
		||||
	*new_client = NULL;
 | 
			
		||||
 | 
			
		||||
	return cman_dispatch(c_handle, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void data_callback(cman_handle_t handle, void *private,
 | 
			
		||||
			  char *buf, int len, uint8_t port, int nodeid)
 | 
			
		||||
{
 | 
			
		||||
	/* Ignore looped back messages */
 | 
			
		||||
	if (nodeid == this_node.cn_nodeid)
 | 
			
		||||
		return;
 | 
			
		||||
	process_message(cman_client, buf, len, (char *)&nodeid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _add_up_node(const char *csid)
 | 
			
		||||
{
 | 
			
		||||
	/* It's up ! */
 | 
			
		||||
	int nodeid = nodeid_from_csid(csid);
 | 
			
		||||
 | 
			
		||||
	dm_hash_insert_binary(node_updown_hash, (char *)&nodeid, sizeof(int), (void *)1);
 | 
			
		||||
	DEBUGLOG("Added new node %d to updown list\n", nodeid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _cluster_closedown(void)
 | 
			
		||||
{
 | 
			
		||||
	dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
 | 
			
		||||
	cman_finish(c_handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int is_listening(int nodeid)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		status = cman_is_listening(c_handle, nodeid, CLUSTER_PORT_CLVMD);
 | 
			
		||||
		if (status < 0 && errno == EBUSY) {	/* Don't busywait */
 | 
			
		||||
			sleep(1);
 | 
			
		||||
			errno = EBUSY;	/* In case sleep trashes it */
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	while (status < 0 && errno == EBUSY);
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Populate the list of CLVMDs running.
 | 
			
		||||
   called only at startup time */
 | 
			
		||||
static void count_clvmds_running(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num_nodes; i++) {
 | 
			
		||||
		int nodeid = nodes[i].cn_nodeid;
 | 
			
		||||
 | 
			
		||||
		if (is_listening(nodeid) == 1)
 | 
			
		||||
			dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)1);
 | 
			
		||||
		else
 | 
			
		||||
			dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a list of active cluster members */
 | 
			
		||||
static void get_members(void)
 | 
			
		||||
{
 | 
			
		||||
	int retnodes;
 | 
			
		||||
	int status;
 | 
			
		||||
	int i;
 | 
			
		||||
	int high_nodeid = 0;
 | 
			
		||||
 | 
			
		||||
	num_nodes = cman_get_node_count(c_handle);
 | 
			
		||||
	if (num_nodes == -1) {
 | 
			
		||||
		log_error("Unable to get node count");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Not enough room for new nodes list ? */
 | 
			
		||||
	if (num_nodes > count_nodes && nodes) {
 | 
			
		||||
		free(nodes);
 | 
			
		||||
		nodes = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (nodes == NULL) {
 | 
			
		||||
		count_nodes = num_nodes + 10; /* Overallocate a little */
 | 
			
		||||
		nodes = malloc(count_nodes * sizeof(struct cman_node));
 | 
			
		||||
		if (!nodes) {
 | 
			
		||||
			log_error("Unable to allocate nodes array\n");
 | 
			
		||||
			exit(5);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status = cman_get_nodes(c_handle, count_nodes, &retnodes, nodes);
 | 
			
		||||
	if (status < 0) {
 | 
			
		||||
		log_error("Unable to get node details");
 | 
			
		||||
		exit(6);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get the highest nodeid */
 | 
			
		||||
	for (i=0; i<retnodes; i++) {
 | 
			
		||||
		if (nodes[i].cn_nodeid > high_nodeid)
 | 
			
		||||
			high_nodeid = nodes[i].cn_nodeid;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Convert a node name to a CSID */
 | 
			
		||||
static int _csid_from_name(char *csid, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num_nodes; i++) {
 | 
			
		||||
		if (strcmp(name, nodes[i].cn_name) == 0) {
 | 
			
		||||
			memcpy(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert a CSID to a node name */
 | 
			
		||||
static int _name_from_csid(const char *csid, char *name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num_nodes; i++) {
 | 
			
		||||
		if (memcmp(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN) == 0) {
 | 
			
		||||
			strcpy(name, nodes[i].cn_name);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* Who?? */
 | 
			
		||||
	strcpy(name, "Unknown");
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert a node ID to a node name */
 | 
			
		||||
static int name_from_nodeid(int nodeid, char *name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num_nodes; i++) {
 | 
			
		||||
		if (nodeid == nodes[i].cn_nodeid) {
 | 
			
		||||
			strcpy(name, nodes[i].cn_name);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* Who?? */
 | 
			
		||||
	strcpy(name, "Unknown");
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert a CSID to a node ID */
 | 
			
		||||
static int nodeid_from_csid(const char *csid)
 | 
			
		||||
{
 | 
			
		||||
        int nodeid;
 | 
			
		||||
 | 
			
		||||
	memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
 | 
			
		||||
 | 
			
		||||
	return nodeid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _is_quorate(void)
 | 
			
		||||
{
 | 
			
		||||
	return cman_is_quorate(c_handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sync_ast_routine(void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct lock_wait *lwait = arg;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lwait->mutex);
 | 
			
		||||
	pthread_cond_signal(&lwait->cond);
 | 
			
		||||
	pthread_mutex_unlock(&lwait->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
	struct lock_wait lwait;
 | 
			
		||||
 | 
			
		||||
	if (!lockid) {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("sync_lock: '%s' mode:%d flags=%d\n", resource,mode,flags);
 | 
			
		||||
	/* Conversions need the lockid in the LKSB */
 | 
			
		||||
	if (flags & LKF_CONVERT)
 | 
			
		||||
		lwait.lksb.sb_lkid = *lockid;
 | 
			
		||||
 | 
			
		||||
	pthread_cond_init(&lwait.cond, NULL);
 | 
			
		||||
	pthread_mutex_init(&lwait.mutex, NULL);
 | 
			
		||||
	pthread_mutex_lock(&lwait.mutex);
 | 
			
		||||
 | 
			
		||||
	status = dlm_ls_lock(lockspace,
 | 
			
		||||
			     mode,
 | 
			
		||||
			     &lwait.lksb,
 | 
			
		||||
			     flags,
 | 
			
		||||
			     resource,
 | 
			
		||||
			     strlen(resource),
 | 
			
		||||
			     0, sync_ast_routine, &lwait, NULL, NULL);
 | 
			
		||||
	if (status)
 | 
			
		||||
		return status;
 | 
			
		||||
 | 
			
		||||
	/* Wait for it to complete */
 | 
			
		||||
	pthread_cond_wait(&lwait.cond, &lwait.mutex);
 | 
			
		||||
	pthread_mutex_unlock(&lwait.mutex);
 | 
			
		||||
 | 
			
		||||
	*lockid = lwait.lksb.sb_lkid;
 | 
			
		||||
 | 
			
		||||
	errno = lwait.lksb.sb_status;
 | 
			
		||||
	DEBUGLOG("sync_lock: returning lkid %x\n", *lockid);
 | 
			
		||||
	if (lwait.lksb.sb_status)
 | 
			
		||||
		return -1;
 | 
			
		||||
	else
 | 
			
		||||
		return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _sync_unlock(const char *resource /* UNUSED */, int lockid)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
	struct lock_wait lwait;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("sync_unlock: '%s' lkid:%x\n", resource, lockid);
 | 
			
		||||
 | 
			
		||||
	pthread_cond_init(&lwait.cond, NULL);
 | 
			
		||||
	pthread_mutex_init(&lwait.mutex, NULL);
 | 
			
		||||
	pthread_mutex_lock(&lwait.mutex);
 | 
			
		||||
 | 
			
		||||
	status = dlm_ls_unlock(lockspace, lockid, 0, &lwait.lksb, &lwait);
 | 
			
		||||
 | 
			
		||||
	if (status)
 | 
			
		||||
		return status;
 | 
			
		||||
 | 
			
		||||
	/* Wait for it to complete */
 | 
			
		||||
	pthread_cond_wait(&lwait.cond, &lwait.mutex);
 | 
			
		||||
	pthread_mutex_unlock(&lwait.mutex);
 | 
			
		||||
 | 
			
		||||
	errno = lwait.lksb.sb_status;
 | 
			
		||||
	if (lwait.lksb.sb_status != EUNLOCK)
 | 
			
		||||
		return -1;
 | 
			
		||||
	else
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_cluster_name(char *buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	cman_cluster_t cluster_info;
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	status = cman_get_cluster(c_handle, &cluster_info);
 | 
			
		||||
	if (!status) {
 | 
			
		||||
		strncpy(buf, cluster_info.ci_name, buflen);
 | 
			
		||||
	}
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cluster_ops _cluster_cman_ops = {
 | 
			
		||||
	.name                     = "cman",
 | 
			
		||||
	.cluster_init_completed   = _cluster_init_completed,
 | 
			
		||||
	.cluster_send_message     = _cluster_send_message,
 | 
			
		||||
	.name_from_csid           = _name_from_csid,
 | 
			
		||||
	.csid_from_name           = _csid_from_name,
 | 
			
		||||
	.get_num_nodes            = _get_num_nodes,
 | 
			
		||||
	.cluster_fd_callback      = _cluster_fd_callback,
 | 
			
		||||
	.get_main_cluster_fd      = _get_main_cluster_fd,
 | 
			
		||||
	.cluster_do_node_callback = _cluster_do_node_callback,
 | 
			
		||||
	.is_quorate               = _is_quorate,
 | 
			
		||||
	.get_our_csid             = _get_our_csid,
 | 
			
		||||
	.add_up_node              = _add_up_node,
 | 
			
		||||
	.cluster_closedown        = _cluster_closedown,
 | 
			
		||||
	.get_cluster_name         = _get_cluster_name,
 | 
			
		||||
	.sync_lock                = _sync_lock,
 | 
			
		||||
	.sync_unlock              = _sync_unlock,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cluster_ops *init_cman_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!_init_cluster())
 | 
			
		||||
		return &_cluster_cman_ops;
 | 
			
		||||
	else
 | 
			
		||||
		return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										414
									
								
								daemons/clvmd/clvmd-command.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								daemons/clvmd/clvmd-command.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,414 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  CLVMD Cluster LVM daemon command processor.
 | 
			
		||||
 | 
			
		||||
  To add commands to the daemon simply add a processor in do_command and return
 | 
			
		||||
  and messages back in buf and the length in *retlen. The initial value of
 | 
			
		||||
  buflen is the maximum size of the buffer. if buf is not large enough then it
 | 
			
		||||
  may be reallocated by the functions in here to a suitable size bearing in
 | 
			
		||||
  mind that anything larger than the passed-in size will have to be returned
 | 
			
		||||
  using the system LV and so performance will suffer.
 | 
			
		||||
 | 
			
		||||
  The status return will be negated and passed back to the originating node.
 | 
			
		||||
 | 
			
		||||
  pre- and post- command routines are called only on the local node. The
 | 
			
		||||
  purpose is primarily to get and release locks, though the pre- routine should
 | 
			
		||||
  also do any other local setups required by the command (if any) and can
 | 
			
		||||
  return a failure code that prevents the command from being distributed around
 | 
			
		||||
  the cluster
 | 
			
		||||
 | 
			
		||||
  The pre- and post- routines are run in their own thread so can block as long
 | 
			
		||||
  they like, do_command is run in the main clvmd thread so should not block for
 | 
			
		||||
  too long. If the pre-command returns an error code (!=0) then the command
 | 
			
		||||
  will not be propogated around the cluster but the post-command WILL be called
 | 
			
		||||
 | 
			
		||||
  Also note that the pre and post routine are *always* called on the local
 | 
			
		||||
  node, even if the command to be executed was only requested to run on a
 | 
			
		||||
  remote node. It may peek inside the client structure to check the status of
 | 
			
		||||
  the command.
 | 
			
		||||
 | 
			
		||||
  The clients of the daemon must, naturally, understand the return messages and
 | 
			
		||||
  codes.
 | 
			
		||||
 | 
			
		||||
  Routines in here may only READ the values in the client structure passed in
 | 
			
		||||
  apart from client->private which they are free to do what they like with.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
#include "clvmd-comms.h"
 | 
			
		||||
#include "clvm.h"
 | 
			
		||||
#include "clvmd.h"
 | 
			
		||||
#include "lvm-globals.h"
 | 
			
		||||
#include "lvm-functions.h"
 | 
			
		||||
 | 
			
		||||
#include "locking.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/utsname.h>
 | 
			
		||||
 | 
			
		||||
extern struct cluster_ops *clops;
 | 
			
		||||
static int restart_clvmd(void);
 | 
			
		||||
 | 
			
		||||
/* This is where all the real work happens:
 | 
			
		||||
   NOTE: client will be NULL when this is executed on a remote node */
 | 
			
		||||
int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
 | 
			
		||||
	       char **buf, int buflen, int *retlen)
 | 
			
		||||
{
 | 
			
		||||
	char *args = msg->node + strlen(msg->node) + 1;
 | 
			
		||||
	int arglen = msglen - sizeof(struct clvm_header) - strlen(msg->node);
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	char *lockname;
 | 
			
		||||
	const char *locktype;
 | 
			
		||||
	struct utsname nodeinfo;
 | 
			
		||||
	unsigned char lock_cmd;
 | 
			
		||||
	unsigned char lock_flags;
 | 
			
		||||
 | 
			
		||||
	/* Do the command */
 | 
			
		||||
	switch (msg->cmd) {
 | 
			
		||||
		/* Just a test message */
 | 
			
		||||
	case CLVMD_CMD_TEST:
 | 
			
		||||
		if (arglen > buflen) {
 | 
			
		||||
			char *new_buf;
 | 
			
		||||
			buflen = arglen + 200;
 | 
			
		||||
			new_buf = realloc(*buf, buflen);
 | 
			
		||||
			if (new_buf == NULL) {
 | 
			
		||||
				status = errno;
 | 
			
		||||
				free (*buf);
 | 
			
		||||
			}
 | 
			
		||||
			*buf = new_buf;
 | 
			
		||||
		}
 | 
			
		||||
		if (*buf) {
 | 
			
		||||
			if (uname(&nodeinfo))
 | 
			
		||||
				memset(&nodeinfo, 0, sizeof(nodeinfo));
 | 
			
		||||
 | 
			
		||||
			*retlen = 1 + dm_snprintf(*buf, buflen,
 | 
			
		||||
						  "TEST from %s: %s v%s",
 | 
			
		||||
						  nodeinfo.nodename, args,
 | 
			
		||||
						  nodeinfo.release);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_LOCK_VG:
 | 
			
		||||
		lock_cmd = args[0];
 | 
			
		||||
		lock_flags = args[1];
 | 
			
		||||
		lockname = &args[2];
 | 
			
		||||
		/* Check to see if the VG is in use by LVM1 */
 | 
			
		||||
		status = do_check_lvm1(lockname);
 | 
			
		||||
		do_lock_vg(lock_cmd, lock_flags, lockname);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_LOCK_LV:
 | 
			
		||||
		/* This is the biggie */
 | 
			
		||||
		lock_cmd = args[0];
 | 
			
		||||
		lock_flags = args[1];
 | 
			
		||||
		lockname = &args[2];
 | 
			
		||||
		status = do_lock_lv(lock_cmd, lock_flags, lockname);
 | 
			
		||||
		/* Replace EIO with something less scary */
 | 
			
		||||
		if (status == EIO) {
 | 
			
		||||
			*retlen = 1 + dm_snprintf(*buf, buflen, "%s",
 | 
			
		||||
						  get_last_lvm_error());
 | 
			
		||||
			return EIO;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_LOCK_QUERY:
 | 
			
		||||
		lockname = &args[2];
 | 
			
		||||
		if (buflen < 3)
 | 
			
		||||
			return EIO;
 | 
			
		||||
		if ((locktype = do_lock_query(lockname)))
 | 
			
		||||
			*retlen = 1 + dm_snprintf(*buf, buflen, "%s", locktype);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_REFRESH:
 | 
			
		||||
		do_refresh_cache();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_SYNC_NAMES:
 | 
			
		||||
		lvm_do_fs_unlock();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_SET_DEBUG:
 | 
			
		||||
		clvmd_set_debug((debug_t) args[0]);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_RESTART:
 | 
			
		||||
		status = restart_clvmd();
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_GET_CLUSTERNAME:
 | 
			
		||||
		status = clops->get_cluster_name(*buf, buflen);
 | 
			
		||||
		if (!status)
 | 
			
		||||
			*retlen = strlen(*buf)+1;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_VG_BACKUP:
 | 
			
		||||
		/*
 | 
			
		||||
		 * Do not run backup on local node, caller should do that.
 | 
			
		||||
		 */
 | 
			
		||||
		if (!client)
 | 
			
		||||
			lvm_do_backup(&args[2]);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		/* Won't get here because command is validated in pre_command */
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check the status of the command and return the error text */
 | 
			
		||||
	if (status) {
 | 
			
		||||
		*retlen = 1 + ((*buf) ? dm_snprintf(*buf, buflen, "%s",
 | 
			
		||||
						    strerror(status)) : -1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lock_vg(struct local_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_hash_table *lock_hash;
 | 
			
		||||
	struct clvm_header *header =
 | 
			
		||||
		(struct clvm_header *) client->bits.localsock.cmd;
 | 
			
		||||
	unsigned char lock_cmd;
 | 
			
		||||
	int lock_mode;
 | 
			
		||||
	char *args = header->node + strlen(header->node) + 1;
 | 
			
		||||
	int lkid;
 | 
			
		||||
	int status;
 | 
			
		||||
	char *lockname;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Keep a track of VG locks in our own hash table. In current
 | 
			
		||||
	 * practice there should only ever be more than two VGs locked
 | 
			
		||||
	 * if a user tries to merge lots of them at once
 | 
			
		||||
	 */
 | 
			
		||||
	if (!client->bits.localsock.private) {
 | 
			
		||||
		if (!(lock_hash = dm_hash_create(3)))
 | 
			
		||||
			return ENOMEM;
 | 
			
		||||
		client->bits.localsock.private = (void *) lock_hash;
 | 
			
		||||
	} else
 | 
			
		||||
		lock_hash = (struct dm_hash_table *) client->bits.localsock.private;
 | 
			
		||||
 | 
			
		||||
	lock_cmd = args[0] & (LCK_NONBLOCK | LCK_HOLD | LCK_SCOPE_MASK | LCK_TYPE_MASK);
 | 
			
		||||
	lock_mode = ((int) lock_cmd & LCK_TYPE_MASK);
 | 
			
		||||
	/* lock_flags = args[1]; */
 | 
			
		||||
	lockname = &args[2];
 | 
			
		||||
	DEBUGLOG("doing PRE command LOCK_VG '%s' at %x (client=%p)\n", lockname, lock_cmd, client);
 | 
			
		||||
 | 
			
		||||
	if (lock_mode == LCK_UNLOCK) {
 | 
			
		||||
		if (!(lkid = (int) (long) dm_hash_lookup(lock_hash, lockname)))
 | 
			
		||||
			return EINVAL;
 | 
			
		||||
 | 
			
		||||
		if ((status = sync_unlock(lockname, lkid)))
 | 
			
		||||
			status = errno;
 | 
			
		||||
		else
 | 
			
		||||
			dm_hash_remove(lock_hash, lockname);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Read locks need to be PR; other modes get passed through */
 | 
			
		||||
		if (lock_mode == LCK_READ)
 | 
			
		||||
			lock_mode = LCK_PREAD;
 | 
			
		||||
 | 
			
		||||
		if ((status = sync_lock(lockname, lock_mode, (lock_cmd & LCK_NONBLOCK) ? LCKF_NOQUEUE : 0, &lkid)))
 | 
			
		||||
			status = errno;
 | 
			
		||||
		else if (!dm_hash_insert(lock_hash, lockname, (void *) (long) lkid))
 | 
			
		||||
			return ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Pre-command is a good place to get locks that are needed only for the duration
 | 
			
		||||
   of the commands around the cluster (don't forget to free them in post-command),
 | 
			
		||||
   and to sanity check the command arguments */
 | 
			
		||||
int do_pre_command(struct local_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct clvm_header *header =
 | 
			
		||||
	    (struct clvm_header *) client->bits.localsock.cmd;
 | 
			
		||||
	unsigned char lock_cmd;
 | 
			
		||||
	unsigned char lock_flags;
 | 
			
		||||
	char *args = header->node + strlen(header->node) + 1;
 | 
			
		||||
	int lockid = 0;
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	char *lockname;
 | 
			
		||||
 | 
			
		||||
	switch (header->cmd) {
 | 
			
		||||
	case CLVMD_CMD_TEST:
 | 
			
		||||
		status = sync_lock("CLVMD_TEST", LCK_EXCL, 0, &lockid);
 | 
			
		||||
		client->bits.localsock.private = (void *)(long)lockid;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_LOCK_VG:
 | 
			
		||||
		lockname = &args[2];
 | 
			
		||||
		/* We take out a real lock unless LCK_CACHE was set */
 | 
			
		||||
		if (!strncmp(lockname, "V_", 2) ||
 | 
			
		||||
		    !strncmp(lockname, "P_#", 3))
 | 
			
		||||
			status = lock_vg(client);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_LOCK_LV:
 | 
			
		||||
		lock_cmd = args[0];
 | 
			
		||||
		lock_flags = args[1];
 | 
			
		||||
		lockname = &args[2];
 | 
			
		||||
		status = pre_lock_lv(lock_cmd, lock_flags, lockname);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_REFRESH:
 | 
			
		||||
	case CLVMD_CMD_GET_CLUSTERNAME:
 | 
			
		||||
	case CLVMD_CMD_SET_DEBUG:
 | 
			
		||||
	case CLVMD_CMD_VG_BACKUP:
 | 
			
		||||
	case CLVMD_CMD_SYNC_NAMES:
 | 
			
		||||
	case CLVMD_CMD_LOCK_QUERY:
 | 
			
		||||
	case CLVMD_CMD_RESTART:
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		log_error("Unknown command %d received\n", header->cmd);
 | 
			
		||||
		status = EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note that the post-command routine is called even if the pre-command or the real command
 | 
			
		||||
   failed */
 | 
			
		||||
int do_post_command(struct local_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct clvm_header *header =
 | 
			
		||||
	    (struct clvm_header *) client->bits.localsock.cmd;
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	unsigned char lock_cmd;
 | 
			
		||||
	unsigned char lock_flags;
 | 
			
		||||
	char *args = header->node + strlen(header->node) + 1;
 | 
			
		||||
	char *lockname;
 | 
			
		||||
 | 
			
		||||
	switch (header->cmd) {
 | 
			
		||||
	case CLVMD_CMD_TEST:
 | 
			
		||||
		status = sync_unlock("CLVMD_TEST", (int) (long) client->bits.localsock.private);
 | 
			
		||||
		client->bits.localsock.private = NULL;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CLVMD_CMD_LOCK_LV:
 | 
			
		||||
		lock_cmd = args[0];
 | 
			
		||||
		lock_flags = args[1];
 | 
			
		||||
		lockname = &args[2];
 | 
			
		||||
		status = post_lock_lv(lock_cmd, lock_flags, lockname);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		/* Nothing to do here */
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Called when the client is about to be deleted */
 | 
			
		||||
void cmd_client_cleanup(struct local_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_hash_node *v;
 | 
			
		||||
	struct dm_hash_table *lock_hash;
 | 
			
		||||
	int lkid;
 | 
			
		||||
	char *lockname;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Client thread cleanup (%p)\n", client);
 | 
			
		||||
	if (!client->bits.localsock.private)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	lock_hash = (struct dm_hash_table *)client->bits.localsock.private;
 | 
			
		||||
 | 
			
		||||
	dm_hash_iterate(v, lock_hash) {
 | 
			
		||||
		lkid = (int)(long)dm_hash_get_data(lock_hash, v);
 | 
			
		||||
		lockname = dm_hash_get_key(lock_hash, v);
 | 
			
		||||
		DEBUGLOG("Cleanup (%p): Unlocking lock %s %x\n", client, lockname, lkid);
 | 
			
		||||
		(void) sync_unlock(lockname, lkid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm_hash_destroy(lock_hash);
 | 
			
		||||
	client->bits.localsock.private = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int restart_clvmd(void)
 | 
			
		||||
{
 | 
			
		||||
	const char **argv;
 | 
			
		||||
	char *lv_name;
 | 
			
		||||
	int argc = 0, max_locks = 0;
 | 
			
		||||
	struct dm_hash_node *hn = NULL;
 | 
			
		||||
	char debug_arg[16];
 | 
			
		||||
	const char *clvmd = getenv("LVM_CLVMD_BINARY") ? : CLVMD_PATH;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("clvmd restart requested\n");
 | 
			
		||||
 | 
			
		||||
	/* Count exclusively-open LVs */
 | 
			
		||||
	do {
 | 
			
		||||
		hn = get_next_excl_lock(hn, &lv_name);
 | 
			
		||||
		if (lv_name) {
 | 
			
		||||
			max_locks++;
 | 
			
		||||
			if (!*lv_name)
 | 
			
		||||
				break; /* FIXME: Is this error ? */
 | 
			
		||||
		}
 | 
			
		||||
	} while (hn);
 | 
			
		||||
 | 
			
		||||
	/* clvmd + locks (-E uuid) + debug (-d X) + NULL */
 | 
			
		||||
	if (!(argv = malloc((max_locks * 2 + 6) * sizeof(*argv))))
 | 
			
		||||
		goto_out;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Build the command-line
 | 
			
		||||
	 */
 | 
			
		||||
	argv[argc++] = "clvmd";
 | 
			
		||||
 | 
			
		||||
	/* Propagate debug options */
 | 
			
		||||
	if (clvmd_get_debug()) {
 | 
			
		||||
		if (dm_snprintf(debug_arg, sizeof(debug_arg), "-d%u", clvmd_get_debug()) < 0)
 | 
			
		||||
			goto_out;
 | 
			
		||||
		argv[argc++] = debug_arg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Propagate foreground options */
 | 
			
		||||
	if (clvmd_get_foreground())
 | 
			
		||||
		argv[argc++] = "-f";
 | 
			
		||||
 | 
			
		||||
	argv[argc++] = "-I";
 | 
			
		||||
	argv[argc++] = clops->name;
 | 
			
		||||
 | 
			
		||||
	/* Now add the exclusively-open LVs */
 | 
			
		||||
	hn = NULL;
 | 
			
		||||
	do {
 | 
			
		||||
		hn = get_next_excl_lock(hn, &lv_name);
 | 
			
		||||
		if (lv_name) {
 | 
			
		||||
			if (!*lv_name)
 | 
			
		||||
				break; /* FIXME: Is this error ? */
 | 
			
		||||
			argv[argc++] = "-E";
 | 
			
		||||
			argv[argc++] = lv_name;
 | 
			
		||||
			DEBUGLOG("excl lock: %s\n", lv_name);
 | 
			
		||||
		}
 | 
			
		||||
	} while (hn);
 | 
			
		||||
	argv[argc] = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Exec new clvmd */
 | 
			
		||||
	DEBUGLOG("--- Restarting %s ---\n", clvmd);
 | 
			
		||||
	for (argc = 1; argv[argc]; argc++) DEBUGLOG("--- %d: %s\n", argc, argv[argc]);
 | 
			
		||||
 | 
			
		||||
	/* NOTE: This will fail when downgrading! */
 | 
			
		||||
	execvp(clvmd, (char **)argv);
 | 
			
		||||
out:
 | 
			
		||||
	/* We failed */
 | 
			
		||||
	DEBUGLOG("Restart of clvmd failed.\n");
 | 
			
		||||
 | 
			
		||||
	free(argv);
 | 
			
		||||
 | 
			
		||||
	return EIO;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.  
 | 
			
		||||
 * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -12,3 +11,17 @@
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This file must be included first by every clvmd source file.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LVM_CLVMD_COMMON_H
 | 
			
		||||
#define _LVM_CLVMD_COMMON_H
 | 
			
		||||
 | 
			
		||||
#define _REENTRANT
 | 
			
		||||
 | 
			
		||||
#include "tool.h"
 | 
			
		||||
 | 
			
		||||
#include "lvm-logging.h"
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										119
									
								
								daemons/clvmd/clvmd-comms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								daemons/clvmd/clvmd-comms.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Abstraction layer for clvmd cluster communications
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _CLVMD_COMMS_H
 | 
			
		||||
#define _CLVMD_COMMS_H
 | 
			
		||||
 | 
			
		||||
struct local_client;
 | 
			
		||||
 | 
			
		||||
struct cluster_ops {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	void (*cluster_init_completed) (void);
 | 
			
		||||
 | 
			
		||||
	int (*cluster_send_message) (const void *buf, int msglen,
 | 
			
		||||
				     const char *csid,
 | 
			
		||||
				     const char *errtext);
 | 
			
		||||
	int (*name_from_csid) (const char *csid, char *name);
 | 
			
		||||
	int (*csid_from_name) (char *csid, const char *name);
 | 
			
		||||
	int (*get_num_nodes) (void);
 | 
			
		||||
	int (*cluster_fd_callback) (struct local_client *fd, char *buf, int len,
 | 
			
		||||
				    const char *csid,
 | 
			
		||||
				    struct local_client **new_client);
 | 
			
		||||
	int (*get_main_cluster_fd) (void);	/* gets accept FD or cman cluster socket */
 | 
			
		||||
	int (*cluster_do_node_callback) (struct local_client *client,
 | 
			
		||||
					 void (*callback) (struct local_client *,
 | 
			
		||||
							   const char *csid,
 | 
			
		||||
							   int node_up));
 | 
			
		||||
	int (*is_quorate) (void);
 | 
			
		||||
 | 
			
		||||
	void (*get_our_csid) (char *csid);
 | 
			
		||||
	void (*add_up_node) (const char *csid);
 | 
			
		||||
	void (*reread_config) (void);
 | 
			
		||||
	void (*cluster_closedown) (void);
 | 
			
		||||
 | 
			
		||||
	int (*get_cluster_name)(char *buf, int buflen);
 | 
			
		||||
 | 
			
		||||
	int (*sync_lock) (const char *resource, int mode,
 | 
			
		||||
			  int flags, int *lockid);
 | 
			
		||||
	int (*sync_unlock) (const char *resource, int lockid);
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CMAN
 | 
			
		||||
#  include <netinet/in.h>
 | 
			
		||||
#  include "libcman.h"
 | 
			
		||||
#  define CMAN_MAX_CSID_LEN 4
 | 
			
		||||
#  ifndef MAX_CSID_LEN
 | 
			
		||||
#    define MAX_CSID_LEN CMAN_MAX_CSID_LEN
 | 
			
		||||
#  endif
 | 
			
		||||
#  undef MAX_CLUSTER_MEMBER_NAME_LEN
 | 
			
		||||
#  define MAX_CLUSTER_MEMBER_NAME_LEN   CMAN_MAX_NODENAME_LEN
 | 
			
		||||
#  define CMAN_MAX_CLUSTER_MESSAGE 1500
 | 
			
		||||
#  define CLUSTER_PORT_CLVMD 11
 | 
			
		||||
struct cluster_ops *init_cman_cluster(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_OPENAIS
 | 
			
		||||
#  include <openais/saAis.h>
 | 
			
		||||
#  include <corosync/totem/totem.h>
 | 
			
		||||
#  define OPENAIS_CSID_LEN (sizeof(int))
 | 
			
		||||
#  define OPENAIS_MAX_CLUSTER_MESSAGE         MESSAGE_SIZE_MAX
 | 
			
		||||
#  define OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH
 | 
			
		||||
#  ifndef MAX_CLUSTER_MEMBER_NAME_LEN
 | 
			
		||||
#    define MAX_CLUSTER_MEMBER_NAME_LEN       SA_MAX_NAME_LENGTH
 | 
			
		||||
#  endif
 | 
			
		||||
#  ifndef CMAN_MAX_CLUSTER_MESSAGE
 | 
			
		||||
#    define CMAN_MAX_CLUSTER_MESSAGE          MESSAGE_SIZE_MAX
 | 
			
		||||
#  endif
 | 
			
		||||
#  ifndef MAX_CSID_LEN
 | 
			
		||||
#    define MAX_CSID_LEN sizeof(int)
 | 
			
		||||
#  endif
 | 
			
		||||
struct cluster_ops *init_openais_cluster(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COROSYNC
 | 
			
		||||
#  include <corosync/corotypes.h>
 | 
			
		||||
#  define COROSYNC_CSID_LEN (sizeof(int))
 | 
			
		||||
#  define COROSYNC_MAX_CLUSTER_MESSAGE         65535
 | 
			
		||||
#  define COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH
 | 
			
		||||
#  ifndef MAX_CLUSTER_MEMBER_NAME_LEN
 | 
			
		||||
#    define MAX_CLUSTER_MEMBER_NAME_LEN       CS_MAX_NAME_LENGTH
 | 
			
		||||
#  endif
 | 
			
		||||
#  ifndef CMAN_MAX_CLUSTER_MESSAGE
 | 
			
		||||
#    define CMAN_MAX_CLUSTER_MESSAGE          65535
 | 
			
		||||
#  endif
 | 
			
		||||
#  ifndef MAX_CSID_LEN
 | 
			
		||||
#    define MAX_CSID_LEN sizeof(int)
 | 
			
		||||
#  endif
 | 
			
		||||
struct cluster_ops *init_corosync_cluster(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SINGLENODE
 | 
			
		||||
#  define SINGLENODE_CSID_LEN (sizeof(int))
 | 
			
		||||
#  ifndef MAX_CLUSTER_MEMBER_NAME_LEN
 | 
			
		||||
#    define MAX_CLUSTER_MEMBER_NAME_LEN       64
 | 
			
		||||
#  endif
 | 
			
		||||
#  define SINGLENODE_MAX_CLUSTER_MESSAGE          65535
 | 
			
		||||
#  ifndef MAX_CSID_LEN
 | 
			
		||||
#    define MAX_CSID_LEN sizeof(int)
 | 
			
		||||
#  endif
 | 
			
		||||
struct cluster_ops *init_singlenode_cluster(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										658
									
								
								daemons/clvmd/clvmd-corosync.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										658
									
								
								daemons/clvmd/clvmd-corosync.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,658 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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)
 | 
			
		||||
{
 | 
			
		||||
	struct iovec iov[2];
 | 
			
		||||
	cs_error_t err;
 | 
			
		||||
	int target_node;
 | 
			
		||||
 | 
			
		||||
	if (csid)
 | 
			
		||||
		memcpy(&target_node, csid, COROSYNC_CSID_LEN);
 | 
			
		||||
	else
 | 
			
		||||
		target_node = 0;
 | 
			
		||||
 | 
			
		||||
	iov[0].iov_base = &target_node;
 | 
			
		||||
	iov[0].iov_len = sizeof(int);
 | 
			
		||||
	iov[1].iov_base = (char *)buf;
 | 
			
		||||
	iov[1].iov_len = msglen;
 | 
			
		||||
 | 
			
		||||
	err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
 | 
			
		||||
	return cs_to_errno(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_COROSYNC_CONFDB_H
 | 
			
		||||
/*
 | 
			
		||||
 * We are not necessarily connected to a Red Hat Cluster system,
 | 
			
		||||
 * but if we are, this returns the cluster name from cluster.conf.
 | 
			
		||||
 * I've used confdb rather than ccs to reduce the inter-package
 | 
			
		||||
 * dependancies as well as to allow people to set a cluster name
 | 
			
		||||
 * for themselves even if they are not running on RH cluster.
 | 
			
		||||
 */
 | 
			
		||||
static int _get_cluster_name(char *buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	confdb_handle_t handle;
 | 
			
		||||
	int result;
 | 
			
		||||
	size_t namelen = buflen;
 | 
			
		||||
	hdb_handle_t cluster_handle;
 | 
			
		||||
	confdb_callbacks_t callbacks = {
 | 
			
		||||
		.confdb_key_change_notify_fn = NULL,
 | 
			
		||||
		.confdb_object_create_change_notify_fn = NULL,
 | 
			
		||||
		.confdb_object_delete_change_notify_fn = NULL
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* This is a default in case everything else fails */
 | 
			
		||||
	strncpy(buf, "Corosync", buflen);
 | 
			
		||||
 | 
			
		||||
	/* Look for a cluster name in confdb */
 | 
			
		||||
	result = confdb_initialize (&handle, &callbacks);
 | 
			
		||||
        if (result != CS_OK)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
        result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
 | 
			
		||||
	if (result != CS_OK)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
        result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, (void *)"cluster", strlen("cluster"), &cluster_handle);
 | 
			
		||||
        if (result != CS_OK)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
        result = confdb_key_get(handle, cluster_handle, (void *)"name", strlen("name"), buf, &namelen);
 | 
			
		||||
        if (result != CS_OK)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	buf[namelen] = '\0';
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	confdb_finalize(handle);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif defined HAVE_COROSYNC_CMAP_H
 | 
			
		||||
 | 
			
		||||
static int _get_cluster_name(char *buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	cmap_handle_t cmap_handle = 0;
 | 
			
		||||
	int result;
 | 
			
		||||
	char *name = NULL;
 | 
			
		||||
 | 
			
		||||
	/* This is a default in case everything else fails */
 | 
			
		||||
	strncpy(buf, "Corosync", buflen);
 | 
			
		||||
 | 
			
		||||
	/* Look for a cluster name in cmap */
 | 
			
		||||
	result = cmap_initialize(&cmap_handle);
 | 
			
		||||
	if (result != CS_OK)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	result = cmap_get_string(cmap_handle, "totem.cluster_name", &name);
 | 
			
		||||
	if (result != CS_OK)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	memset(buf, 0, buflen);
 | 
			
		||||
	strncpy(buf, name, buflen - 1);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (name)
 | 
			
		||||
		free(name);
 | 
			
		||||
	cmap_finalize(cmap_handle);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static struct cluster_ops _cluster_corosync_ops = {
 | 
			
		||||
	.name                     = "corosync",
 | 
			
		||||
	.cluster_init_completed   = NULL,
 | 
			
		||||
	.cluster_send_message     = _cluster_send_message,
 | 
			
		||||
	.name_from_csid           = _name_from_csid,
 | 
			
		||||
	.csid_from_name           = _csid_from_name,
 | 
			
		||||
	.get_num_nodes            = _get_num_nodes,
 | 
			
		||||
	.cluster_fd_callback      = _cluster_fd_callback,
 | 
			
		||||
	.get_main_cluster_fd      = _get_main_cluster_fd,
 | 
			
		||||
	.cluster_do_node_callback = _cluster_do_node_callback,
 | 
			
		||||
	.is_quorate               = _is_quorate,
 | 
			
		||||
	.get_our_csid             = _get_our_csid,
 | 
			
		||||
	.add_up_node              = _add_up_node,
 | 
			
		||||
	.reread_config            = NULL,
 | 
			
		||||
	.cluster_closedown        = _cluster_closedown,
 | 
			
		||||
	.get_cluster_name         = _get_cluster_name,
 | 
			
		||||
	.sync_lock                = _lock_resource,
 | 
			
		||||
	.sync_unlock              = _unlock_resource,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cluster_ops *init_corosync_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!_init_cluster())
 | 
			
		||||
		return &_cluster_corosync_ops;
 | 
			
		||||
	else
 | 
			
		||||
		return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										689
									
								
								daemons/clvmd/clvmd-openais.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										689
									
								
								daemons/clvmd/clvmd-openais.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,689 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2007-2009 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU Lesser General Public License v.2.1.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This provides the interface between clvmd and OpenAIS as the cluster
 | 
			
		||||
 * and lock manager.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <syslog.h>
 | 
			
		||||
 | 
			
		||||
#include <openais/saAis.h>
 | 
			
		||||
#include <openais/saLck.h>
 | 
			
		||||
 | 
			
		||||
#include <corosync/corotypes.h>
 | 
			
		||||
#include <corosync/cpg.h>
 | 
			
		||||
 | 
			
		||||
#include "locking.h"
 | 
			
		||||
#include "clvm.h"
 | 
			
		||||
#include "clvmd-comms.h"
 | 
			
		||||
#include "lvm-functions.h"
 | 
			
		||||
#include "clvmd.h"
 | 
			
		||||
 | 
			
		||||
/* Timeout value for several openais calls */
 | 
			
		||||
#define TIMEOUT 10
 | 
			
		||||
 | 
			
		||||
static void openais_cpg_deliver_callback (cpg_handle_t handle,
 | 
			
		||||
				  const struct cpg_name *groupName,
 | 
			
		||||
				  uint32_t nodeid,
 | 
			
		||||
				  uint32_t pid,
 | 
			
		||||
				  void *msg,
 | 
			
		||||
				  size_t msg_len);
 | 
			
		||||
static void openais_cpg_confchg_callback(cpg_handle_t handle,
 | 
			
		||||
				 const struct cpg_name *groupName,
 | 
			
		||||
				 const struct cpg_address *member_list, size_t member_list_entries,
 | 
			
		||||
				 const struct cpg_address *left_list, size_t left_list_entries,
 | 
			
		||||
				 const struct cpg_address *joined_list, size_t joined_list_entries);
 | 
			
		||||
 | 
			
		||||
static void _cluster_closedown(void);
 | 
			
		||||
 | 
			
		||||
/* Hash list of nodes in the cluster */
 | 
			
		||||
static struct dm_hash_table *node_hash;
 | 
			
		||||
 | 
			
		||||
/* For associating lock IDs & resource handles */
 | 
			
		||||
static struct dm_hash_table *lock_hash;
 | 
			
		||||
 | 
			
		||||
/* Number of active nodes */
 | 
			
		||||
static int num_nodes;
 | 
			
		||||
static unsigned int our_nodeid;
 | 
			
		||||
 | 
			
		||||
static struct local_client *cluster_client;
 | 
			
		||||
 | 
			
		||||
/* OpenAIS handles */
 | 
			
		||||
static cpg_handle_t cpg_handle;
 | 
			
		||||
static SaLckHandleT lck_handle;
 | 
			
		||||
 | 
			
		||||
static struct cpg_name cpg_group_name;
 | 
			
		||||
 | 
			
		||||
/* Openais callback structs */
 | 
			
		||||
cpg_callbacks_t openais_cpg_callbacks = {
 | 
			
		||||
	.cpg_deliver_fn =            openais_cpg_deliver_callback,
 | 
			
		||||
	.cpg_confchg_fn =            openais_cpg_confchg_callback,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct node_info
 | 
			
		||||
{
 | 
			
		||||
	enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
 | 
			
		||||
	int nodeid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lock_info
 | 
			
		||||
{
 | 
			
		||||
	SaLckResourceHandleT res_handle;
 | 
			
		||||
	SaLckLockIdT         lock_id;
 | 
			
		||||
	SaNameT              lock_name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Set errno to something approximating the right value and return 0 or -1 */
 | 
			
		||||
static int ais_to_errno(SaAisErrorT err)
 | 
			
		||||
{
 | 
			
		||||
	switch(err)
 | 
			
		||||
	{
 | 
			
		||||
	case SA_AIS_OK:
 | 
			
		||||
		return 0;
 | 
			
		||||
        case SA_AIS_ERR_LIBRARY:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_VERSION:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_INIT:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_TIMEOUT:
 | 
			
		||||
		errno = ETIME;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_TRY_AGAIN:
 | 
			
		||||
		errno = EAGAIN;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_INVALID_PARAM:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NO_MEMORY:
 | 
			
		||||
		errno = ENOMEM;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_BAD_HANDLE:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_BUSY:
 | 
			
		||||
		errno = EBUSY;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_ACCESS:
 | 
			
		||||
		errno = EPERM;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NOT_EXIST:
 | 
			
		||||
		errno = ENOENT;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NAME_TOO_LONG:
 | 
			
		||||
		errno = ENAMETOOLONG;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_EXIST:
 | 
			
		||||
		errno = EEXIST;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NO_SPACE:
 | 
			
		||||
		errno = ENOSPC;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_INTERRUPT:
 | 
			
		||||
		errno = EINTR;
 | 
			
		||||
		break;
 | 
			
		||||
	case SA_AIS_ERR_NAME_NOT_FOUND:
 | 
			
		||||
		errno = ENOENT;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NO_RESOURCES:
 | 
			
		||||
		errno = ENOMEM;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NOT_SUPPORTED:
 | 
			
		||||
		errno = EOPNOTSUPP;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_BAD_OPERATION:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_FAILED_OPERATION:
 | 
			
		||||
		errno = EIO;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_MESSAGE_ERROR:
 | 
			
		||||
		errno = EIO;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_QUEUE_FULL:
 | 
			
		||||
		errno = EXFULL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_QUEUE_NOT_AVAILABLE:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_BAD_FLAGS:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_TOO_BIG:
 | 
			
		||||
		errno = E2BIG;
 | 
			
		||||
		break;
 | 
			
		||||
        case SA_AIS_ERR_NO_SECTIONS:
 | 
			
		||||
		errno = ENOMEM;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *print_openais_csid(const char *csid)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[128];
 | 
			
		||||
	int id;
 | 
			
		||||
 | 
			
		||||
	memcpy(&id, csid, sizeof(int));
 | 
			
		||||
	sprintf(buf, "%d", id);
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int add_internal_client(int fd, fd_callback_t callback)
 | 
			
		||||
{
 | 
			
		||||
	struct local_client *client;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Add_internal_client, fd = %d\n", fd);
 | 
			
		||||
 | 
			
		||||
	if (!(client = dm_zalloc(sizeof(*client)))) {
 | 
			
		||||
		DEBUGLOG("malloc failed\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client->fd = fd;
 | 
			
		||||
	client->type = CLUSTER_INTERNAL;
 | 
			
		||||
	client->callback = callback;
 | 
			
		||||
	add_client(client);
 | 
			
		||||
 | 
			
		||||
	/* Set Close-on-exec */
 | 
			
		||||
	fcntl(fd, F_SETFD, 1);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void openais_cpg_deliver_callback (cpg_handle_t handle,
 | 
			
		||||
				  const struct cpg_name *groupName,
 | 
			
		||||
				  uint32_t nodeid,
 | 
			
		||||
				  uint32_t pid,
 | 
			
		||||
				  void *msg,
 | 
			
		||||
				  size_t msg_len)
 | 
			
		||||
{
 | 
			
		||||
	int target_nodeid;
 | 
			
		||||
 | 
			
		||||
	memcpy(&target_nodeid, msg, OPENAIS_CSID_LEN);
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("%u got message from nodeid %d for %d. len %" PRIsize_t "\n",
 | 
			
		||||
		 our_nodeid, nodeid, target_nodeid, msg_len-4);
 | 
			
		||||
 | 
			
		||||
	if (nodeid != our_nodeid)
 | 
			
		||||
		if (target_nodeid == our_nodeid || target_nodeid == 0)
 | 
			
		||||
			process_message(cluster_client, (char *)msg+OPENAIS_CSID_LEN,
 | 
			
		||||
					msg_len-OPENAIS_CSID_LEN, (char*)&nodeid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void openais_cpg_confchg_callback(cpg_handle_t handle,
 | 
			
		||||
				 const struct cpg_name *groupName,
 | 
			
		||||
				 const struct cpg_address *member_list, size_t member_list_entries,
 | 
			
		||||
				 const struct cpg_address *left_list, size_t left_list_entries,
 | 
			
		||||
				 const struct cpg_address *joined_list, size_t joined_list_entries)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct node_info *ninfo;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("confchg callback. %" PRIsize_t " joined, "
 | 
			
		||||
		 FMTsize_t " left, %" PRIsize_t " members\n",
 | 
			
		||||
		 joined_list_entries, left_list_entries, member_list_entries);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<joined_list_entries; i++) {
 | 
			
		||||
		ninfo = dm_hash_lookup_binary(node_hash,
 | 
			
		||||
					      (char *)&joined_list[i].nodeid,
 | 
			
		||||
					      OPENAIS_CSID_LEN);
 | 
			
		||||
		if (!ninfo) {
 | 
			
		||||
			ninfo = malloc(sizeof(struct node_info));
 | 
			
		||||
			if (!ninfo) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				ninfo->nodeid = joined_list[i].nodeid;
 | 
			
		||||
				dm_hash_insert_binary(node_hash,
 | 
			
		||||
						      (char *)&ninfo->nodeid,
 | 
			
		||||
						      OPENAIS_CSID_LEN, ninfo);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ninfo->state = NODE_CLVMD;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<left_list_entries; i++) {
 | 
			
		||||
		ninfo = dm_hash_lookup_binary(node_hash,
 | 
			
		||||
					      (char *)&left_list[i].nodeid,
 | 
			
		||||
					      OPENAIS_CSID_LEN);
 | 
			
		||||
		if (ninfo)
 | 
			
		||||
			ninfo->state = NODE_DOWN;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<member_list_entries; i++) {
 | 
			
		||||
		if (member_list[i].nodeid == 0) continue;
 | 
			
		||||
		ninfo = dm_hash_lookup_binary(node_hash,
 | 
			
		||||
				(char *)&member_list[i].nodeid,
 | 
			
		||||
				OPENAIS_CSID_LEN);
 | 
			
		||||
		if (!ninfo) {
 | 
			
		||||
			ninfo = malloc(sizeof(struct node_info));
 | 
			
		||||
			if (!ninfo) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				ninfo->nodeid = member_list[i].nodeid;
 | 
			
		||||
				dm_hash_insert_binary(node_hash,
 | 
			
		||||
						(char *)&ninfo->nodeid,
 | 
			
		||||
						OPENAIS_CSID_LEN, ninfo);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ninfo->state = NODE_CLVMD;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	num_nodes = member_list_entries;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lck_dispatch(struct local_client *client, char *buf, int len,
 | 
			
		||||
			const char *csid, struct local_client **new_client)
 | 
			
		||||
{
 | 
			
		||||
	*new_client = NULL;
 | 
			
		||||
	saLckDispatch(lck_handle, SA_DISPATCH_ONE);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _init_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	SaAisErrorT err;
 | 
			
		||||
	SaVersionT  ver = { 'B', 1, 1 };
 | 
			
		||||
	int select_fd;
 | 
			
		||||
 | 
			
		||||
	node_hash = dm_hash_create(100);
 | 
			
		||||
	lock_hash = dm_hash_create(10);
 | 
			
		||||
 | 
			
		||||
	err = cpg_initialize(&cpg_handle,
 | 
			
		||||
			     &openais_cpg_callbacks);
 | 
			
		||||
	if (err != SA_AIS_OK) {
 | 
			
		||||
		syslog(LOG_ERR, "Cannot initialise OpenAIS CPG service: %d",
 | 
			
		||||
		       err);
 | 
			
		||||
		DEBUGLOG("Cannot initialise OpenAIS CPG service: %d", err);
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = saLckInitialize(&lck_handle,
 | 
			
		||||
					NULL,
 | 
			
		||||
			      &ver);
 | 
			
		||||
	if (err != SA_AIS_OK) {
 | 
			
		||||
		cpg_initialize(&cpg_handle, &openais_cpg_callbacks);
 | 
			
		||||
		syslog(LOG_ERR, "Cannot initialise OpenAIS lock service: %d",
 | 
			
		||||
		       err);
 | 
			
		||||
		DEBUGLOG("Cannot initialise OpenAIS lock service: %d\n\n", err);
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Connect to the clvmd group */
 | 
			
		||||
	strcpy((char *)cpg_group_name.value, "clvmd");
 | 
			
		||||
	cpg_group_name.length = strlen((char *)cpg_group_name.value);
 | 
			
		||||
	err = cpg_join(cpg_handle, &cpg_group_name);
 | 
			
		||||
	if (err != SA_AIS_OK) {
 | 
			
		||||
		cpg_finalize(cpg_handle);
 | 
			
		||||
		saLckFinalize(lck_handle);
 | 
			
		||||
		syslog(LOG_ERR, "Cannot join clvmd process group");
 | 
			
		||||
		DEBUGLOG("Cannot join clvmd process group: %d\n", err);
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cpg_local_get(cpg_handle,
 | 
			
		||||
			    &our_nodeid);
 | 
			
		||||
	if (err != SA_AIS_OK) {
 | 
			
		||||
		cpg_finalize(cpg_handle);
 | 
			
		||||
		saLckFinalize(lck_handle);
 | 
			
		||||
		syslog(LOG_ERR, "Cannot get local node id\n");
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
	DEBUGLOG("Our local node id is %d\n", our_nodeid);
 | 
			
		||||
 | 
			
		||||
	saLckSelectionObjectGet(lck_handle, (SaSelectionObjectT *)&select_fd);
 | 
			
		||||
	add_internal_client(select_fd, lck_dispatch);
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Connected to OpenAIS\n");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _cluster_closedown(void)
 | 
			
		||||
{
 | 
			
		||||
	saLckFinalize(lck_handle);
 | 
			
		||||
	cpg_finalize(cpg_handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _get_our_csid(char *csid)
 | 
			
		||||
{
 | 
			
		||||
	memcpy(csid, &our_nodeid, sizeof(int));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* OpenAIS doesn't really have nmode names so we
 | 
			
		||||
   just use the node ID in hex instead */
 | 
			
		||||
static int _csid_from_name(char *csid, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	int nodeid;
 | 
			
		||||
	struct node_info *ninfo;
 | 
			
		||||
 | 
			
		||||
	if (sscanf(name, "%x", &nodeid) == 1) {
 | 
			
		||||
		ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
 | 
			
		||||
		if (ninfo)
 | 
			
		||||
			return nodeid;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _name_from_csid(const char *csid, char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct node_info *ninfo;
 | 
			
		||||
 | 
			
		||||
	ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
 | 
			
		||||
	if (!ninfo)
 | 
			
		||||
	{
 | 
			
		||||
		sprintf(name, "UNKNOWN %s", print_openais_csid(csid));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sprintf(name, "%x", ninfo->nodeid);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_num_nodes()
 | 
			
		||||
{
 | 
			
		||||
	DEBUGLOG("num_nodes = %d\n", num_nodes);
 | 
			
		||||
	return num_nodes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Node is now known to be running a clvmd */
 | 
			
		||||
static void _add_up_node(const char *csid)
 | 
			
		||||
{
 | 
			
		||||
	struct node_info *ninfo;
 | 
			
		||||
 | 
			
		||||
	ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
 | 
			
		||||
	if (!ninfo) {
 | 
			
		||||
		DEBUGLOG("openais_add_up_node no node_hash entry for csid %s\n",
 | 
			
		||||
			 print_openais_csid(csid));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("openais_add_up_node %d\n", ninfo->nodeid);
 | 
			
		||||
 | 
			
		||||
	ninfo->state = NODE_CLVMD;
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call a callback for each node, so the caller knows whether it's up or down */
 | 
			
		||||
static int _cluster_do_node_callback(struct local_client *master_client,
 | 
			
		||||
				     void (*callback)(struct local_client *,
 | 
			
		||||
						      const char *csid, int node_up))
 | 
			
		||||
{
 | 
			
		||||
	struct dm_hash_node *hn;
 | 
			
		||||
	struct node_info *ninfo;
 | 
			
		||||
	int somedown = 0;
 | 
			
		||||
 | 
			
		||||
	dm_hash_iterate(hn, node_hash)
 | 
			
		||||
	{
 | 
			
		||||
		char csid[OPENAIS_CSID_LEN];
 | 
			
		||||
 | 
			
		||||
		ninfo = dm_hash_get_data(node_hash, hn);
 | 
			
		||||
		memcpy(csid, dm_hash_get_key(node_hash, hn), OPENAIS_CSID_LEN);
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
 | 
			
		||||
			 ninfo->state);
 | 
			
		||||
 | 
			
		||||
		if (ninfo->state != NODE_DOWN)
 | 
			
		||||
			callback(master_client, csid, ninfo->state == NODE_CLVMD);
 | 
			
		||||
		if (ninfo->state != NODE_CLVMD)
 | 
			
		||||
			somedown = -1;
 | 
			
		||||
	}
 | 
			
		||||
	return somedown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Real locking */
 | 
			
		||||
static int _lock_resource(char *resource, int mode, int flags, int *lockid)
 | 
			
		||||
{
 | 
			
		||||
	struct lock_info *linfo;
 | 
			
		||||
	SaLckResourceHandleT res_handle;
 | 
			
		||||
	SaAisErrorT err;
 | 
			
		||||
	SaLckLockIdT lock_id;
 | 
			
		||||
	SaLckLockStatusT lockStatus;
 | 
			
		||||
 | 
			
		||||
	/* This needs to be converted from DLM/LVM2 value for OpenAIS LCK */
 | 
			
		||||
	if (flags & LCK_NONBLOCK) flags = SA_LCK_LOCK_NO_QUEUE;
 | 
			
		||||
 | 
			
		||||
	linfo = malloc(sizeof(struct lock_info));
 | 
			
		||||
	if (!linfo)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
 | 
			
		||||
 | 
			
		||||
	linfo->lock_name.length = strlen(resource)+1;
 | 
			
		||||
	strcpy((char *)linfo->lock_name.value, resource);
 | 
			
		||||
 | 
			
		||||
	err = saLckResourceOpen(lck_handle, &linfo->lock_name,
 | 
			
		||||
				SA_LCK_RESOURCE_CREATE, TIMEOUT, &res_handle);
 | 
			
		||||
	if (err != SA_AIS_OK)
 | 
			
		||||
	{
 | 
			
		||||
		DEBUGLOG("ResourceOpen returned %d\n", err);
 | 
			
		||||
		free(linfo);
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = saLckResourceLock(
 | 
			
		||||
			res_handle,
 | 
			
		||||
			&lock_id,
 | 
			
		||||
			mode,
 | 
			
		||||
			flags,
 | 
			
		||||
			0,
 | 
			
		||||
			SA_TIME_END,
 | 
			
		||||
			&lockStatus);
 | 
			
		||||
	if (err != SA_AIS_OK && lockStatus != SA_LCK_LOCK_GRANTED)
 | 
			
		||||
	{
 | 
			
		||||
		free(linfo);
 | 
			
		||||
		saLckResourceClose(res_handle);
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Wait for it to complete */
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("lock_resource returning %d, lock_id=%" PRIx64 "\n",
 | 
			
		||||
		 err, lock_id);
 | 
			
		||||
 | 
			
		||||
	linfo->lock_id = lock_id;
 | 
			
		||||
	linfo->res_handle = res_handle;
 | 
			
		||||
 | 
			
		||||
	dm_hash_insert(lock_hash, resource, linfo);
 | 
			
		||||
 | 
			
		||||
	return ais_to_errno(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int _unlock_resource(char *resource, int lockid)
 | 
			
		||||
{
 | 
			
		||||
	SaAisErrorT err;
 | 
			
		||||
	struct lock_info *linfo;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("unlock_resource %s\n", resource);
 | 
			
		||||
	linfo = dm_hash_lookup(lock_hash, resource);
 | 
			
		||||
	if (!linfo)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("unlock_resource: lockid: %" PRIx64 "\n", linfo->lock_id);
 | 
			
		||||
	err = saLckResourceUnlock(linfo->lock_id, SA_TIME_END);
 | 
			
		||||
	if (err != SA_AIS_OK)
 | 
			
		||||
	{
 | 
			
		||||
		DEBUGLOG("Unlock returned %d\n", err);
 | 
			
		||||
		return ais_to_errno(err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Release the resource */
 | 
			
		||||
	dm_hash_remove(lock_hash, resource);
 | 
			
		||||
	saLckResourceClose(linfo->res_handle);
 | 
			
		||||
	free(linfo);
 | 
			
		||||
 | 
			
		||||
	return ais_to_errno(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
	char lock1[strlen(resource)+3];
 | 
			
		||||
	char lock2[strlen(resource)+3];
 | 
			
		||||
 | 
			
		||||
	snprintf(lock1, sizeof(lock1), "%s-1", resource);
 | 
			
		||||
	snprintf(lock2, sizeof(lock2), "%s-2", resource);
 | 
			
		||||
 | 
			
		||||
	switch (mode)
 | 
			
		||||
	{
 | 
			
		||||
	case LCK_EXCL:
 | 
			
		||||
		status = _lock_resource(lock1, SA_LCK_EX_LOCK_MODE, flags, lockid);
 | 
			
		||||
		if (status)
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		/* If we can't get this lock too then bail out */
 | 
			
		||||
		status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, LCK_NONBLOCK,
 | 
			
		||||
					lockid);
 | 
			
		||||
		if (status == SA_LCK_LOCK_NOT_QUEUED)
 | 
			
		||||
		{
 | 
			
		||||
			_unlock_resource(lock1, *lockid);
 | 
			
		||||
			status = -1;
 | 
			
		||||
			errno = EAGAIN;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case LCK_PREAD:
 | 
			
		||||
	case LCK_READ:
 | 
			
		||||
		status = _lock_resource(lock1, SA_LCK_PR_LOCK_MODE, flags, lockid);
 | 
			
		||||
		if (status)
 | 
			
		||||
			goto out;
 | 
			
		||||
		_unlock_resource(lock2, *lockid);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case LCK_WRITE:
 | 
			
		||||
		status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, flags, lockid);
 | 
			
		||||
		if (status)
 | 
			
		||||
			goto out;
 | 
			
		||||
		_unlock_resource(lock1, *lockid);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		status = -1;
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	*lockid = mode;
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _sync_unlock(const char *resource, int lockid)
 | 
			
		||||
{
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	char lock1[strlen(resource)+3];
 | 
			
		||||
	char lock2[strlen(resource)+3];
 | 
			
		||||
 | 
			
		||||
	snprintf(lock1, sizeof(lock1), "%s-1", resource);
 | 
			
		||||
	snprintf(lock2, sizeof(lock2), "%s-2", resource);
 | 
			
		||||
 | 
			
		||||
	_unlock_resource(lock1, lockid);
 | 
			
		||||
	_unlock_resource(lock2, lockid);
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We are always quorate ! */
 | 
			
		||||
static int _is_quorate()
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_main_cluster_fd(void)
 | 
			
		||||
{
 | 
			
		||||
	int select_fd;
 | 
			
		||||
 | 
			
		||||
	cpg_fd_get(cpg_handle, &select_fd);
 | 
			
		||||
	return select_fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
 | 
			
		||||
				const char *csid,
 | 
			
		||||
				struct local_client **new_client)
 | 
			
		||||
{
 | 
			
		||||
	cluster_client = fd;
 | 
			
		||||
	*new_client = NULL;
 | 
			
		||||
	cpg_dispatch(cpg_handle, SA_DISPATCH_ONE);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
 | 
			
		||||
				 const char *errtext)
 | 
			
		||||
{
 | 
			
		||||
	struct iovec iov[2];
 | 
			
		||||
	SaAisErrorT err;
 | 
			
		||||
	int target_node;
 | 
			
		||||
 | 
			
		||||
	if (csid)
 | 
			
		||||
		memcpy(&target_node, csid, OPENAIS_CSID_LEN);
 | 
			
		||||
	else
 | 
			
		||||
		target_node = 0;
 | 
			
		||||
 | 
			
		||||
	iov[0].iov_base = &target_node;
 | 
			
		||||
	iov[0].iov_len = sizeof(int);
 | 
			
		||||
	iov[1].iov_base = (char *)buf;
 | 
			
		||||
	iov[1].iov_len = msglen;
 | 
			
		||||
 | 
			
		||||
	err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
 | 
			
		||||
	return ais_to_errno(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We don't have a cluster name to report here */
 | 
			
		||||
static int _get_cluster_name(char *buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	strncpy(buf, "OpenAIS", buflen);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cluster_ops _cluster_openais_ops = {
 | 
			
		||||
	.name                     = "openais",
 | 
			
		||||
	.cluster_init_completed   = NULL,
 | 
			
		||||
	.cluster_send_message     = _cluster_send_message,
 | 
			
		||||
	.name_from_csid           = _name_from_csid,
 | 
			
		||||
	.csid_from_name           = _csid_from_name,
 | 
			
		||||
	.get_num_nodes            = _get_num_nodes,
 | 
			
		||||
	.cluster_fd_callback      = _cluster_fd_callback,
 | 
			
		||||
	.get_main_cluster_fd      = _get_main_cluster_fd,
 | 
			
		||||
	.cluster_do_node_callback = _cluster_do_node_callback,
 | 
			
		||||
	.is_quorate               = _is_quorate,
 | 
			
		||||
	.get_our_csid             = _get_our_csid,
 | 
			
		||||
	.add_up_node              = _add_up_node,
 | 
			
		||||
	.reread_config            = NULL,
 | 
			
		||||
	.cluster_closedown        = _cluster_closedown,
 | 
			
		||||
	.get_cluster_name         = _get_cluster_name,
 | 
			
		||||
	.sync_lock                = _sync_lock,
 | 
			
		||||
	.sync_unlock              = _sync_unlock,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cluster_ops *init_openais_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!_init_cluster())
 | 
			
		||||
		return &_cluster_openais_ops;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										382
									
								
								daemons/clvmd/clvmd-singlenode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								daemons/clvmd/clvmd-singlenode.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,382 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2009-2013 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU Lesser General Public License v.2.1.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#include "locking.h"
 | 
			
		||||
#include "clvm.h"
 | 
			
		||||
#include "clvmd-comms.h"
 | 
			
		||||
#include "clvmd.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
static const char SINGLENODE_CLVMD_SOCKNAME[] = DEFAULT_RUN_DIR "/clvmd_singlenode.sock";
 | 
			
		||||
static int listen_fd = -1;
 | 
			
		||||
 | 
			
		||||
static struct dm_hash_table *_locks;
 | 
			
		||||
static int _lockid;
 | 
			
		||||
 | 
			
		||||
static pthread_mutex_t _lock_mutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
/* Using one common condition for all locks for simplicity */
 | 
			
		||||
static pthread_cond_t _lock_cond = PTHREAD_COND_INITIALIZER;
 | 
			
		||||
 | 
			
		||||
struct lock {
 | 
			
		||||
	struct dm_list list;
 | 
			
		||||
	int lockid;
 | 
			
		||||
	int mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void close_comms(void)
 | 
			
		||||
{
 | 
			
		||||
	if (listen_fd != -1 && close(listen_fd))
 | 
			
		||||
		stack;
 | 
			
		||||
	(void)unlink(SINGLENODE_CLVMD_SOCKNAME);
 | 
			
		||||
	listen_fd = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int init_comms(void)
 | 
			
		||||
{
 | 
			
		||||
	mode_t old_mask;
 | 
			
		||||
	struct sockaddr_un addr = { .sun_family = AF_UNIX };
 | 
			
		||||
 | 
			
		||||
	if (!dm_strncpy(addr.sun_path, SINGLENODE_CLVMD_SOCKNAME,
 | 
			
		||||
			sizeof(addr.sun_path))) {
 | 
			
		||||
		DEBUGLOG("%s: singlenode socket name too long.",
 | 
			
		||||
			 SINGLENODE_CLVMD_SOCKNAME);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close_comms();
 | 
			
		||||
 | 
			
		||||
	(void) dm_prepare_selinux_context(SINGLENODE_CLVMD_SOCKNAME, S_IFSOCK);
 | 
			
		||||
	old_mask = umask(0077);
 | 
			
		||||
 | 
			
		||||
	listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
 | 
			
		||||
	if (listen_fd < 0) {
 | 
			
		||||
		DEBUGLOG("Can't create local socket: %s\n", strerror(errno));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	/* Set Close-on-exec */
 | 
			
		||||
	if (fcntl(listen_fd, F_SETFD, 1)) {
 | 
			
		||||
		DEBUGLOG("Setting CLOEXEC on client fd failed: %s\n", strerror(errno));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
 | 
			
		||||
		DEBUGLOG("Can't bind local socket: %s\n", strerror(errno));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	if (listen(listen_fd, 10) < 0) {
 | 
			
		||||
		DEBUGLOG("Can't listen local socket: %s\n", strerror(errno));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	umask(old_mask);
 | 
			
		||||
	(void) dm_prepare_selinux_context(NULL, 0);
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	umask(old_mask);
 | 
			
		||||
	(void) dm_prepare_selinux_context(NULL, 0);
 | 
			
		||||
	close_comms();
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _init_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	if (!(_locks = dm_hash_create(128))) {
 | 
			
		||||
		DEBUGLOG("Failed to allocate single-node hash table.\n");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r = init_comms();
 | 
			
		||||
	if (r) {
 | 
			
		||||
		dm_hash_destroy(_locks);
 | 
			
		||||
		_locks = NULL;
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Single-node cluster initialised.\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _cluster_closedown(void)
 | 
			
		||||
{
 | 
			
		||||
	close_comms();
 | 
			
		||||
 | 
			
		||||
	/* If there is any awaited resource, kill it softly */
 | 
			
		||||
	pthread_mutex_lock(&_lock_mutex);
 | 
			
		||||
	dm_hash_destroy(_locks);
 | 
			
		||||
	_locks = NULL;
 | 
			
		||||
	_lockid = 0;
 | 
			
		||||
	pthread_cond_broadcast(&_lock_cond); /* wakeup waiters */
 | 
			
		||||
	pthread_mutex_unlock(&_lock_mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _get_our_csid(char *csid)
 | 
			
		||||
{
 | 
			
		||||
	int nodeid = 1;
 | 
			
		||||
	memcpy(csid, &nodeid, sizeof(int));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _csid_from_name(char *csid, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _name_from_csid(const char *csid, char *name)
 | 
			
		||||
{
 | 
			
		||||
	strcpy(name, "SINGLENODE");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_num_nodes(void)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Node is now known to be running a clvmd */
 | 
			
		||||
static void _add_up_node(const char *csid)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call a callback for each node, so the caller knows whether it's up or down */
 | 
			
		||||
static int _cluster_do_node_callback(struct local_client *master_client,
 | 
			
		||||
				     void (*callback)(struct local_client *,
 | 
			
		||||
				     const char *csid, int node_up))
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int _lock_file(const char *file, uint32_t flags);
 | 
			
		||||
 | 
			
		||||
static const char *_get_mode(int mode)
 | 
			
		||||
{
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case LCK_NULL: return "NULL";
 | 
			
		||||
	case LCK_READ: return "READ";
 | 
			
		||||
	case LCK_PREAD: return "PREAD";
 | 
			
		||||
	case LCK_WRITE: return "WRITE";
 | 
			
		||||
	case LCK_EXCL: return "EXCLUSIVE";
 | 
			
		||||
	case LCK_UNLOCK: return "UNLOCK";
 | 
			
		||||
	default: return "????";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Real locking */
 | 
			
		||||
static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
 | 
			
		||||
{
 | 
			
		||||
	/* DLM table of allowed transition states */
 | 
			
		||||
	static const int _dlm_table[6][6] = {
 | 
			
		||||
	/* Mode	   NL	CR	CW	PR	PW	EX */
 | 
			
		||||
	/* NL */ { 1,	 1,	 1,	 1,	 1,	 1},
 | 
			
		||||
	/* CR */ { 1,	 1,	 1,	 1,	 1,	 0},
 | 
			
		||||
	/* CW */ { 1,	 1,	 1,	 0,	 0,	 0},
 | 
			
		||||
	/* PR */ { 1,	 1,	 0,	 1,	 0,	 0},
 | 
			
		||||
	/* PW */ { 1,	 1,	 0,	 0,	 0,	 0},
 | 
			
		||||
	/* EX */ { 1,	 0,	 0,	 0,	 0,	 0}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct lock *lck = NULL, *lckt;
 | 
			
		||||
	struct dm_list *head;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Locking resource %s, flags=0x%02x (%s%s%s), mode=%s (%d)\n",
 | 
			
		||||
		 resource, flags,
 | 
			
		||||
		 (flags & LCKF_NOQUEUE) ? "NOQUEUE" : "",
 | 
			
		||||
		 ((flags & (LCKF_NOQUEUE | LCKF_CONVERT)) ==
 | 
			
		||||
		  (LCKF_NOQUEUE | LCKF_CONVERT)) ? "|" : "",
 | 
			
		||||
		 (flags & LCKF_CONVERT) ? "CONVERT" : "",
 | 
			
		||||
		 _get_mode(mode), mode);
 | 
			
		||||
 | 
			
		||||
	mode &= LCK_TYPE_MASK;
 | 
			
		||||
	pthread_mutex_lock(&_lock_mutex);
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
	if (!(head = dm_hash_lookup(_locks, resource))) {
 | 
			
		||||
		if (flags & LCKF_CONVERT) {
 | 
			
		||||
			/* In real DLM, lock is identified only by lockid, resource is not used */
 | 
			
		||||
			DEBUGLOG("Unlocked resource %s cannot be converted\n", resource);
 | 
			
		||||
			goto_bad;
 | 
			
		||||
		}
 | 
			
		||||
		/* Add new locked resource */
 | 
			
		||||
		if (!(head = dm_malloc(sizeof(struct dm_list))) ||
 | 
			
		||||
		    !dm_hash_insert(_locks, resource, head)) {
 | 
			
		||||
			dm_free(head);
 | 
			
		||||
			goto_bad;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dm_list_init(head);
 | 
			
		||||
	} else	/* Update/convert locked resource */
 | 
			
		||||
		dm_list_iterate_items(lck, head) {
 | 
			
		||||
			/* Check is all locks are compatible with requested lock */
 | 
			
		||||
			if (flags & LCKF_CONVERT) {
 | 
			
		||||
				if (lck->lockid != *lockid)
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				DEBUGLOG("Converting resource %s lockid=%d mode:%s -> %s...\n",
 | 
			
		||||
					 resource, lck->lockid, _get_mode(lck->mode), _get_mode(mode));
 | 
			
		||||
				dm_list_iterate_items(lckt, head) {
 | 
			
		||||
					if ((lckt->lockid != *lockid) &&
 | 
			
		||||
					    !_dlm_table[mode][lckt->mode]) {
 | 
			
		||||
						if (!(flags & LCKF_NOQUEUE) &&
 | 
			
		||||
						    /* TODO: Real dlm uses here conversion queues */
 | 
			
		||||
						    !pthread_cond_wait(&_lock_cond, &_lock_mutex) &&
 | 
			
		||||
						    _locks) /* End of the game? */
 | 
			
		||||
							goto retry;
 | 
			
		||||
						goto bad;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				lck->mode = mode; /* Lock is now converted */
 | 
			
		||||
				goto out;
 | 
			
		||||
			} else if (!_dlm_table[mode][lck->mode]) {
 | 
			
		||||
				DEBUGLOG("Resource %s already locked lockid=%d, mode:%s\n",
 | 
			
		||||
					 resource, lck->lockid, _get_mode(lck->mode));
 | 
			
		||||
				if (!(flags & LCKF_NOQUEUE) &&
 | 
			
		||||
				    !pthread_cond_wait(&_lock_cond, &_lock_mutex) &&
 | 
			
		||||
				    _locks) { /* End of the game? */
 | 
			
		||||
					DEBUGLOG("Resource %s retrying lock in mode:%s...\n",
 | 
			
		||||
						 resource, _get_mode(mode));
 | 
			
		||||
					goto retry;
 | 
			
		||||
				}
 | 
			
		||||
				goto bad;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if (!(flags & LCKF_CONVERT)) {
 | 
			
		||||
		if (!(lck = dm_malloc(sizeof(struct lock))))
 | 
			
		||||
			goto_bad;
 | 
			
		||||
 | 
			
		||||
		*lockid = lck->lockid = ++_lockid;
 | 
			
		||||
		lck->mode = mode;
 | 
			
		||||
		dm_list_add(head, &lck->list);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */
 | 
			
		||||
	pthread_mutex_unlock(&_lock_mutex);
 | 
			
		||||
	DEBUGLOG("Locked resource %s, lockid=%d, mode=%s\n",
 | 
			
		||||
		 resource, lck->lockid, _get_mode(lck->mode));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
bad:
 | 
			
		||||
	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */
 | 
			
		||||
	pthread_mutex_unlock(&_lock_mutex);
 | 
			
		||||
	DEBUGLOG("Failed to lock resource %s\n", resource);
 | 
			
		||||
 | 
			
		||||
	return 1; /* fail */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _unlock_resource(const char *resource, int lockid)
 | 
			
		||||
{
 | 
			
		||||
	struct lock *lck;
 | 
			
		||||
	struct dm_list *head;
 | 
			
		||||
	int r = 1;
 | 
			
		||||
 | 
			
		||||
	if (lockid < 0) {
 | 
			
		||||
		DEBUGLOG("Not tracking unlock of lockid -1: %s, lockid=%d\n",
 | 
			
		||||
			 resource, lockid);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Unlocking resource %s, lockid=%d\n", resource, lockid);
 | 
			
		||||
	pthread_mutex_lock(&_lock_mutex);
 | 
			
		||||
	pthread_cond_broadcast(&_lock_cond); /* wakeup waiters */
 | 
			
		||||
 | 
			
		||||
	if (!(head = dm_hash_lookup(_locks, resource))) {
 | 
			
		||||
		pthread_mutex_unlock(&_lock_mutex);
 | 
			
		||||
		DEBUGLOG("Resource %s is not locked.\n", resource);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm_list_iterate_items(lck, head)
 | 
			
		||||
		if (lck->lockid == lockid) {
 | 
			
		||||
			dm_list_del(&lck->list);
 | 
			
		||||
			dm_free(lck);
 | 
			
		||||
			r = 0;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Resource %s has wrong lockid %d.\n", resource, lockid);
 | 
			
		||||
out:
 | 
			
		||||
	if (dm_list_empty(head)) {
 | 
			
		||||
		//DEBUGLOG("Resource %s is no longer hashed (lockid=%d).\n", resource, lockid);
 | 
			
		||||
		dm_hash_remove(_locks, resource);
 | 
			
		||||
		dm_free(head);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&_lock_mutex);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _is_quorate(void)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_main_cluster_fd(void)
 | 
			
		||||
{
 | 
			
		||||
	return listen_fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
 | 
			
		||||
				const char *csid,
 | 
			
		||||
				struct local_client **new_client)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _cluster_send_message(const void *buf, int msglen,
 | 
			
		||||
				 const char *csid,
 | 
			
		||||
				 const char *errtext)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_cluster_name(char *buf, int buflen)
 | 
			
		||||
{
 | 
			
		||||
	return dm_strncpy(buf, "localcluster", buflen) ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cluster_ops _cluster_singlenode_ops = {
 | 
			
		||||
	.name                     = "singlenode",
 | 
			
		||||
	.cluster_init_completed   = NULL,
 | 
			
		||||
	.cluster_send_message     = _cluster_send_message,
 | 
			
		||||
	.name_from_csid           = _name_from_csid,
 | 
			
		||||
	.csid_from_name           = _csid_from_name,
 | 
			
		||||
	.get_num_nodes            = _get_num_nodes,
 | 
			
		||||
	.cluster_fd_callback      = _cluster_fd_callback,
 | 
			
		||||
	.get_main_cluster_fd      = _get_main_cluster_fd,
 | 
			
		||||
	.cluster_do_node_callback = _cluster_do_node_callback,
 | 
			
		||||
	.is_quorate               = _is_quorate,
 | 
			
		||||
	.get_our_csid             = _get_our_csid,
 | 
			
		||||
	.add_up_node              = _add_up_node,
 | 
			
		||||
	.reread_config            = NULL,
 | 
			
		||||
	.cluster_closedown        = _cluster_closedown,
 | 
			
		||||
	.get_cluster_name         = _get_cluster_name,
 | 
			
		||||
	.sync_lock                = _lock_resource,
 | 
			
		||||
	.sync_unlock              = _unlock_resource,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cluster_ops *init_singlenode_cluster(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!_init_cluster())
 | 
			
		||||
		return &_cluster_singlenode_ops;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2381
									
								
								daemons/clvmd/clvmd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2381
									
								
								daemons/clvmd/clvmd.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										126
									
								
								daemons/clvmd/clvmd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								daemons/clvmd/clvmd.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _CLVMD_H
 | 
			
		||||
#define _CLVMD_H
 | 
			
		||||
 | 
			
		||||
#define CLVMD_MAJOR_VERSION 0
 | 
			
		||||
#define CLVMD_MINOR_VERSION 2
 | 
			
		||||
#define CLVMD_PATCH_VERSION 1
 | 
			
		||||
 | 
			
		||||
/* Default time (in seconds) we will wait for all remote commands to execute
 | 
			
		||||
   before declaring them dead */
 | 
			
		||||
#define DEFAULT_CMD_TIMEOUT 60
 | 
			
		||||
 | 
			
		||||
/* One of these for each reply we get from command execution on a node */
 | 
			
		||||
struct node_reply {
 | 
			
		||||
	char node[MAX_CLUSTER_MEMBER_NAME_LEN];
 | 
			
		||||
	char *replymsg;
 | 
			
		||||
	int status;
 | 
			
		||||
	struct node_reply *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum {DEBUG_OFF, DEBUG_STDERR, DEBUG_SYSLOG} debug_t;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * These exist for the use of local sockets only when we are
 | 
			
		||||
 * collecting responses from all cluster nodes
 | 
			
		||||
 */
 | 
			
		||||
struct localsock_bits {
 | 
			
		||||
	struct node_reply *replies;
 | 
			
		||||
	int num_replies;
 | 
			
		||||
	int expected_replies;
 | 
			
		||||
	time_t sent_time;	/* So we can check for timeouts */
 | 
			
		||||
	int in_progress;	/* Only execute one cmd at a time per client */
 | 
			
		||||
	int sent_out;		/* Flag to indicate that a command was sent
 | 
			
		||||
				   to remote nodes */
 | 
			
		||||
	void *private;		/* Private area for command processor use */
 | 
			
		||||
	void *cmd;		/* Whole command as passed down local socket */
 | 
			
		||||
	int cmd_len;		/* Length of above */
 | 
			
		||||
	int pipe;		/* Pipe to send PRE completion status down */
 | 
			
		||||
	int finished;		/* Flag to tell subthread to exit */
 | 
			
		||||
	int all_success;	/* Set to 0 if any node (or the pre_command)
 | 
			
		||||
				   failed */
 | 
			
		||||
	int cleanup_needed;     /* helper for cleanup_zombie */
 | 
			
		||||
	struct local_client *pipe_client;
 | 
			
		||||
	pthread_t threadid;
 | 
			
		||||
	enum { PRE_COMMAND, POST_COMMAND } state;
 | 
			
		||||
	pthread_mutex_t mutex;	/* Main thread and worker synchronisation */
 | 
			
		||||
	pthread_cond_t cond;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Entries for PIPE clients */
 | 
			
		||||
struct pipe_bits {
 | 
			
		||||
	struct local_client *client;	/* Actual (localsock) client */
 | 
			
		||||
	pthread_t threadid;		/* Our own copy of the thread id */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Entries for Network socket clients */
 | 
			
		||||
struct netsock_bits {
 | 
			
		||||
	void *private;
 | 
			
		||||
	int flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef int (*fd_callback_t) (struct local_client * fd, char *buf, int len,
 | 
			
		||||
			      const char *csid,
 | 
			
		||||
			      struct local_client ** new_client);
 | 
			
		||||
 | 
			
		||||
/* One of these for each fd we are listening on */
 | 
			
		||||
struct local_client {
 | 
			
		||||
	int fd;
 | 
			
		||||
	enum { CLUSTER_MAIN_SOCK, CLUSTER_DATA_SOCK, LOCAL_RENDEZVOUS,
 | 
			
		||||
		    LOCAL_SOCK, THREAD_PIPE, CLUSTER_INTERNAL } type;
 | 
			
		||||
	struct local_client *next;
 | 
			
		||||
	unsigned short xid;
 | 
			
		||||
	fd_callback_t callback;
 | 
			
		||||
	uint8_t removeme;
 | 
			
		||||
 | 
			
		||||
	union {
 | 
			
		||||
		struct localsock_bits localsock;
 | 
			
		||||
		struct pipe_bits pipe;
 | 
			
		||||
		struct netsock_bits net;
 | 
			
		||||
	} bits;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define DEBUGLOG(fmt, args...) debuglog(fmt, ## args)
 | 
			
		||||
 | 
			
		||||
#ifndef max
 | 
			
		||||
#define max(a,b) ((a)>(b)?(a):(b))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* The real command processor is in clvmd-command.c */
 | 
			
		||||
extern int do_command(struct local_client *client, struct clvm_header *msg,
 | 
			
		||||
		      int msglen, char **buf, int buflen, int *retlen);
 | 
			
		||||
 | 
			
		||||
/* Pre and post command routines are called only on the local node */
 | 
			
		||||
extern int do_pre_command(struct local_client *client);
 | 
			
		||||
extern int do_post_command(struct local_client *client);
 | 
			
		||||
extern void cmd_client_cleanup(struct local_client *client);
 | 
			
		||||
extern int add_client(struct local_client *new_client);
 | 
			
		||||
 | 
			
		||||
extern void clvmd_cluster_init_completed(void);
 | 
			
		||||
extern void process_message(struct local_client *client, char *buf,
 | 
			
		||||
			    int len, const char *csid);
 | 
			
		||||
extern void debuglog(const char *fmt, ... )
 | 
			
		||||
  __attribute__ ((format(printf, 1, 2)));
 | 
			
		||||
 | 
			
		||||
void clvmd_set_debug(debug_t new_de);
 | 
			
		||||
debug_t clvmd_get_debug(void);
 | 
			
		||||
int clvmd_get_foreground(void);
 | 
			
		||||
 | 
			
		||||
int sync_lock(const char *resource, int mode, int flags, int *lockid);
 | 
			
		||||
int sync_unlock(const char *resource, int lockid);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										939
									
								
								daemons/clvmd/lvm-functions.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										939
									
								
								daemons/clvmd/lvm-functions.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,939 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#include "clvm.h"
 | 
			
		||||
#include "clvmd-comms.h"
 | 
			
		||||
#include "clvmd.h"
 | 
			
		||||
#include "lvm-functions.h"
 | 
			
		||||
 | 
			
		||||
/* LVM2 headers */
 | 
			
		||||
#include "toolcontext.h"
 | 
			
		||||
#include "lvmcache.h"
 | 
			
		||||
#include "lvm-globals.h"
 | 
			
		||||
#include "activate.h"
 | 
			
		||||
#include "archiver.h"
 | 
			
		||||
#include "memlock.h"
 | 
			
		||||
 | 
			
		||||
#include <syslog.h>
 | 
			
		||||
 | 
			
		||||
static struct cmd_context *cmd = NULL;
 | 
			
		||||
static struct dm_hash_table *lv_hash = NULL;
 | 
			
		||||
static pthread_mutex_t lv_hash_lock;
 | 
			
		||||
static pthread_mutex_t lvm_lock;
 | 
			
		||||
static char last_error[1024];
 | 
			
		||||
 | 
			
		||||
struct lv_info {
 | 
			
		||||
	int lock_id;
 | 
			
		||||
	int lock_mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *decode_full_locking_cmd(uint32_t cmdl)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[128];
 | 
			
		||||
	const char *type;
 | 
			
		||||
	const char *scope;
 | 
			
		||||
	const char *command;
 | 
			
		||||
 | 
			
		||||
	switch (cmdl & LCK_TYPE_MASK) {
 | 
			
		||||
	case LCK_NULL:
 | 
			
		||||
		type = "NULL";
 | 
			
		||||
		break;
 | 
			
		||||
	case LCK_READ:
 | 
			
		||||
		type = "READ";
 | 
			
		||||
		break;
 | 
			
		||||
	case LCK_PREAD:
 | 
			
		||||
		type = "PREAD";
 | 
			
		||||
		break;
 | 
			
		||||
	case LCK_WRITE:
 | 
			
		||||
		type = "WRITE";
 | 
			
		||||
		break;
 | 
			
		||||
	case LCK_EXCL:
 | 
			
		||||
		type = "EXCL";
 | 
			
		||||
		break;
 | 
			
		||||
	case LCK_UNLOCK:
 | 
			
		||||
		type = "UNLOCK";
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		type = "unknown";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (cmdl & LCK_SCOPE_MASK) {
 | 
			
		||||
	case LCK_VG:
 | 
			
		||||
		scope = "VG";
 | 
			
		||||
		command = "LCK_VG";
 | 
			
		||||
		break;
 | 
			
		||||
	case LCK_LV:
 | 
			
		||||
		scope = "LV";
 | 
			
		||||
		switch (cmdl & LCK_MASK) {
 | 
			
		||||
		case LCK_LV_EXCLUSIVE & LCK_MASK:
 | 
			
		||||
			command = "LCK_LV_EXCLUSIVE";
 | 
			
		||||
			break;
 | 
			
		||||
		case LCK_LV_SUSPEND & LCK_MASK:
 | 
			
		||||
			command = "LCK_LV_SUSPEND";
 | 
			
		||||
			break;
 | 
			
		||||
		case LCK_LV_RESUME & LCK_MASK:
 | 
			
		||||
			command = "LCK_LV_RESUME";
 | 
			
		||||
			break;
 | 
			
		||||
		case LCK_LV_ACTIVATE & LCK_MASK:
 | 
			
		||||
			command = "LCK_LV_ACTIVATE";
 | 
			
		||||
			break;
 | 
			
		||||
		case LCK_LV_DEACTIVATE & LCK_MASK:
 | 
			
		||||
			command = "LCK_LV_DEACTIVATE";
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			command = "unknown";
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		scope = "unknown";
 | 
			
		||||
		command = "unknown";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sprintf(buf, "0x%x %s (%s|%s%s%s%s%s)", cmdl, command, type, scope,
 | 
			
		||||
		cmdl & LCK_NONBLOCK   ? "|NONBLOCK" : "",
 | 
			
		||||
		cmdl & LCK_HOLD       ? "|HOLD" : "",
 | 
			
		||||
		cmdl & LCK_CLUSTER_VG ? "|CLUSTER_VG" : "",
 | 
			
		||||
		cmdl & LCK_CACHE      ? "|CACHE" : "");
 | 
			
		||||
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Only processes 8 bits: excludes LCK_CACHE.
 | 
			
		||||
 */
 | 
			
		||||
static const char *decode_locking_cmd(unsigned char cmdl)
 | 
			
		||||
{
 | 
			
		||||
	return decode_full_locking_cmd((uint32_t) cmdl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *decode_flags(unsigned char flags)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[128];
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	len = sprintf(buf, "0x%x ( %s%s%s%s%s%s%s%s)", flags,
 | 
			
		||||
		flags & LCK_PARTIAL_MODE	  ? "PARTIAL_MODE|" : "",
 | 
			
		||||
		flags & LCK_MIRROR_NOSYNC_MODE	  ? "MIRROR_NOSYNC|" : "",
 | 
			
		||||
		flags & LCK_DMEVENTD_MONITOR_MODE ? "DMEVENTD_MONITOR|" : "",
 | 
			
		||||
		flags & LCK_ORIGIN_ONLY_MODE ? "ORIGIN_ONLY|" : "",
 | 
			
		||||
		flags & LCK_TEST_MODE ? "TEST|" : "",
 | 
			
		||||
		flags & LCK_CONVERT_MODE ? "CONVERT|" : "",
 | 
			
		||||
		flags & LCK_DMEVENTD_MONITOR_IGNORE ? "DMEVENTD_MONITOR_IGNORE|" : "",
 | 
			
		||||
		flags & LCK_REVERT_MODE ? "REVERT|" : "");
 | 
			
		||||
 | 
			
		||||
	if (len > 1)
 | 
			
		||||
		buf[len - 2] = ' ';
 | 
			
		||||
	else
 | 
			
		||||
		buf[0] = '\0';
 | 
			
		||||
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *get_last_lvm_error(void)
 | 
			
		||||
{
 | 
			
		||||
	return last_error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Hash lock info helpers
 | 
			
		||||
 */
 | 
			
		||||
static struct lv_info *lookup_info(const char *resource)
 | 
			
		||||
{
 | 
			
		||||
	struct lv_info *lvi;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lv_hash_lock);
 | 
			
		||||
	lvi = dm_hash_lookup(lv_hash, resource);
 | 
			
		||||
	pthread_mutex_unlock(&lv_hash_lock);
 | 
			
		||||
 | 
			
		||||
	return lvi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int insert_info(const char *resource, struct lv_info *lvi)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lv_hash_lock);
 | 
			
		||||
	ret = dm_hash_insert(lv_hash, resource, lvi);
 | 
			
		||||
	pthread_mutex_unlock(&lv_hash_lock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void remove_info(const char *resource)
 | 
			
		||||
{
 | 
			
		||||
	int num_open;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lv_hash_lock);
 | 
			
		||||
	dm_hash_remove(lv_hash, resource);
 | 
			
		||||
 | 
			
		||||
	/* When last lock is remove, validate there are not left opened devices */
 | 
			
		||||
	if (!dm_hash_get_first(lv_hash)) {
 | 
			
		||||
		if (critical_section())
 | 
			
		||||
			log_error(INTERNAL_ERROR "No volumes are locked however clvmd is in activation mode critical section.");
 | 
			
		||||
		if ((num_open = dev_cache_check_for_open_devices()))
 | 
			
		||||
			log_error(INTERNAL_ERROR "No volumes are locked however %d devices are still open.", num_open);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&lv_hash_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return the mode a lock is currently held at (or -1 if not held)
 | 
			
		||||
 */
 | 
			
		||||
static int get_current_lock(char *resource)
 | 
			
		||||
{
 | 
			
		||||
	struct lv_info *lvi;
 | 
			
		||||
 | 
			
		||||
	if ((lvi = lookup_info(resource)))
 | 
			
		||||
		return lvi->lock_mode;
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void init_lvhash(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Create hash table for keeping LV locks & status */
 | 
			
		||||
	lv_hash = dm_hash_create(1024);
 | 
			
		||||
	pthread_mutex_init(&lv_hash_lock, NULL);
 | 
			
		||||
	pthread_mutex_init(&lvm_lock, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called at shutdown to tidy the lockspace */
 | 
			
		||||
void destroy_lvhash(void)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_hash_node *v;
 | 
			
		||||
	struct lv_info *lvi;
 | 
			
		||||
	char *resource;
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lv_hash_lock);
 | 
			
		||||
 | 
			
		||||
	dm_hash_iterate(v, lv_hash) {
 | 
			
		||||
		lvi = dm_hash_get_data(lv_hash, v);
 | 
			
		||||
		resource = dm_hash_get_key(lv_hash, v);
 | 
			
		||||
 | 
			
		||||
		if ((status = sync_unlock(resource, lvi->lock_id)))
 | 
			
		||||
			DEBUGLOG("unlock_all. unlock failed(%d): %s\n",
 | 
			
		||||
				 status,  strerror(errno));
 | 
			
		||||
		dm_free(lvi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm_hash_destroy(lv_hash);
 | 
			
		||||
	lv_hash = NULL;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&lv_hash_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Gets a real lock and keeps the info in the hash table */
 | 
			
		||||
static int hold_lock(char *resource, int mode, int flags)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
	int saved_errno;
 | 
			
		||||
	struct lv_info *lvi;
 | 
			
		||||
 | 
			
		||||
	/* Mask off invalid options */
 | 
			
		||||
	flags &= LCKF_NOQUEUE | LCKF_CONVERT;
 | 
			
		||||
 | 
			
		||||
	lvi = lookup_info(resource);
 | 
			
		||||
 | 
			
		||||
	if (lvi) {
 | 
			
		||||
		if (lvi->lock_mode == mode) {
 | 
			
		||||
			DEBUGLOG("hold_lock, lock mode %d already held\n",
 | 
			
		||||
				 mode);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		if ((lvi->lock_mode == LCK_EXCL) && (mode == LCK_WRITE)) {
 | 
			
		||||
			DEBUGLOG("hold_lock, lock already held LCK_EXCL, "
 | 
			
		||||
				 "ignoring LCK_WRITE request\n");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Only allow explicit conversions */
 | 
			
		||||
	if (lvi && !(flags & LCKF_CONVERT)) {
 | 
			
		||||
		errno = EBUSY;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (lvi) {
 | 
			
		||||
		/* Already exists - convert it */
 | 
			
		||||
		status = sync_lock(resource, mode, flags, &lvi->lock_id);
 | 
			
		||||
		saved_errno = errno;
 | 
			
		||||
		if (!status)
 | 
			
		||||
			lvi->lock_mode = mode;
 | 
			
		||||
		else
 | 
			
		||||
			DEBUGLOG("hold_lock. convert to %d failed: %s\n", mode,
 | 
			
		||||
				 strerror(errno));
 | 
			
		||||
		errno = saved_errno;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!(lvi = dm_malloc(sizeof(struct lv_info)))) {
 | 
			
		||||
			errno = ENOMEM;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lvi->lock_mode = mode;
 | 
			
		||||
		lvi->lock_id = 0;
 | 
			
		||||
		status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id);
 | 
			
		||||
		saved_errno = errno;
 | 
			
		||||
		if (status) {
 | 
			
		||||
			dm_free(lvi);
 | 
			
		||||
			DEBUGLOG("hold_lock. lock at %d failed: %s\n", mode,
 | 
			
		||||
				 strerror(errno));
 | 
			
		||||
		} else
 | 
			
		||||
			if (!insert_info(resource, lvi)) {
 | 
			
		||||
				errno = ENOMEM;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		errno = saved_errno;
 | 
			
		||||
	}
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Unlock and remove it from the hash table */
 | 
			
		||||
static int hold_unlock(char *resource)
 | 
			
		||||
{
 | 
			
		||||
	struct lv_info *lvi;
 | 
			
		||||
	int status;
 | 
			
		||||
	int saved_errno;
 | 
			
		||||
 | 
			
		||||
	if (!(lvi = lookup_info(resource))) {
 | 
			
		||||
		DEBUGLOG("hold_unlock, lock not already held\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status = sync_unlock(resource, lvi->lock_id);
 | 
			
		||||
	saved_errno = errno;
 | 
			
		||||
	if (!status) {
 | 
			
		||||
		remove_info(resource);
 | 
			
		||||
		dm_free(lvi);
 | 
			
		||||
	} else {
 | 
			
		||||
		DEBUGLOG("hold_unlock. unlock failed(%d): %s\n", status,
 | 
			
		||||
			 strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errno = saved_errno;
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Watch the return codes here.
 | 
			
		||||
   liblvm API functions return 1(true) for success, 0(false) for failure and don't set errno.
 | 
			
		||||
   libdlm API functions return 0 for success, -1 for failure and do set errno.
 | 
			
		||||
   These functions here return 0 for success or >0 for failure (where the retcode is errno)
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* Activate LV exclusive or non-exclusive */
 | 
			
		||||
static int do_activate_lv(char *resource, unsigned char command, unsigned char lock_flags, int mode)
 | 
			
		||||
{
 | 
			
		||||
	int oldmode;
 | 
			
		||||
	int status;
 | 
			
		||||
	int activate_lv;
 | 
			
		||||
	int exclusive = 0;
 | 
			
		||||
	struct lvinfo lvi;
 | 
			
		||||
 | 
			
		||||
	/* Is it already open ? */
 | 
			
		||||
	oldmode = get_current_lock(resource);
 | 
			
		||||
	if (oldmode == mode && (command & LCK_CLUSTER_VG)) {
 | 
			
		||||
		DEBUGLOG("do_activate_lv, lock already held at %d\n", oldmode);
 | 
			
		||||
		return 0;	/* Nothing to do */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Does the config file want us to activate this LV ? */
 | 
			
		||||
	if (!lv_activation_filter(cmd, resource, &activate_lv, NULL))
 | 
			
		||||
		return EIO;
 | 
			
		||||
 | 
			
		||||
	if (!activate_lv)
 | 
			
		||||
		return 0;	/* Success, we did nothing! */
 | 
			
		||||
 | 
			
		||||
	/* Do we need to activate exclusively? */
 | 
			
		||||
	if ((activate_lv == 2) || (mode == LCK_EXCL)) {
 | 
			
		||||
		exclusive = 1;
 | 
			
		||||
		mode = LCK_EXCL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Try to get the lock if it's a clustered volume group.
 | 
			
		||||
	 * Use lock conversion only if requested, to prevent implicit conversion
 | 
			
		||||
	 * of exclusive lock to shared one during activation.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!test_mode() && command & LCK_CLUSTER_VG) {
 | 
			
		||||
		status = hold_lock(resource, mode, LCKF_NOQUEUE | ((lock_flags & LCK_CONVERT_MODE) ? LCKF_CONVERT:0));
 | 
			
		||||
		if (status) {
 | 
			
		||||
			/* Return an LVM-sensible error for this.
 | 
			
		||||
			 * Forcing EIO makes the upper level return this text
 | 
			
		||||
			 * rather than the strerror text for EAGAIN.
 | 
			
		||||
			 */
 | 
			
		||||
			if (errno == EAGAIN) {
 | 
			
		||||
				sprintf(last_error, "Volume is busy on another node");
 | 
			
		||||
				errno = EIO;
 | 
			
		||||
			}
 | 
			
		||||
			return errno;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If it's suspended then resume it */
 | 
			
		||||
	if (!lv_info_by_lvid(cmd, resource, 0, &lvi, 0, 0))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (lvi.suspended) {
 | 
			
		||||
		critical_section_inc(cmd, "resuming");
 | 
			
		||||
		if (!lv_resume(cmd, resource, 0, NULL)) {
 | 
			
		||||
			critical_section_dec(cmd, "resumed");
 | 
			
		||||
			goto error;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Now activate it */
 | 
			
		||||
	if (!lv_activate(cmd, resource, exclusive, 0, 0, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	if (!test_mode() && (oldmode == -1 || oldmode != mode))
 | 
			
		||||
		(void)hold_unlock(resource);
 | 
			
		||||
	return EIO;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resume the LV if it was active */
 | 
			
		||||
static int do_resume_lv(char *resource, unsigned char command, unsigned char lock_flags)
 | 
			
		||||
{
 | 
			
		||||
	int oldmode, origin_only, exclusive, revert;
 | 
			
		||||
 | 
			
		||||
	/* Is it open ? */
 | 
			
		||||
	oldmode = get_current_lock(resource);
 | 
			
		||||
	if (oldmode == -1 && (command & LCK_CLUSTER_VG)) {
 | 
			
		||||
		DEBUGLOG("do_resume_lv, lock not already held\n");
 | 
			
		||||
		return 0;	/* We don't need to do anything */
 | 
			
		||||
	}
 | 
			
		||||
	origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
 | 
			
		||||
	exclusive = (oldmode == LCK_EXCL) ? 1 : 0;
 | 
			
		||||
	revert = (lock_flags & LCK_REVERT_MODE) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	if (!lv_resume_if_active(cmd, resource, origin_only, exclusive, revert, NULL))
 | 
			
		||||
		return EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Suspend the device if active */
 | 
			
		||||
static int do_suspend_lv(char *resource, unsigned char command, unsigned char lock_flags)
 | 
			
		||||
{
 | 
			
		||||
	int oldmode;
 | 
			
		||||
	unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
 | 
			
		||||
	unsigned exclusive;
 | 
			
		||||
 | 
			
		||||
	/* Is it open ? */
 | 
			
		||||
	oldmode = get_current_lock(resource);
 | 
			
		||||
	if (oldmode == -1 && (command & LCK_CLUSTER_VG)) {
 | 
			
		||||
		DEBUGLOG("do_suspend_lv, lock not already held\n");
 | 
			
		||||
		return 0; /* Not active, so it's OK */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	exclusive = (oldmode == LCK_EXCL) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	/* Always call lv_suspend to read commited and precommited data */
 | 
			
		||||
	if (!lv_suspend_if_active(cmd, resource, origin_only, exclusive, NULL, NULL))
 | 
			
		||||
		return EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int do_deactivate_lv(char *resource, unsigned char command, unsigned char lock_flags)
 | 
			
		||||
{
 | 
			
		||||
	int oldmode;
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	/* Is it open ? */
 | 
			
		||||
	oldmode = get_current_lock(resource);
 | 
			
		||||
	if (oldmode == -1 && (command & LCK_CLUSTER_VG)) {
 | 
			
		||||
		DEBUGLOG("do_deactivate_lock, lock not already held\n");
 | 
			
		||||
		return 0;	/* We don't need to do anything */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!lv_deactivate(cmd, resource, NULL))
 | 
			
		||||
		return EIO;
 | 
			
		||||
 | 
			
		||||
	if (!test_mode() && command & LCK_CLUSTER_VG) {
 | 
			
		||||
		status = hold_unlock(resource);
 | 
			
		||||
		if (status)
 | 
			
		||||
			return errno;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *do_lock_query(char *resource)
 | 
			
		||||
{
 | 
			
		||||
	int mode;
 | 
			
		||||
	const char *type;
 | 
			
		||||
 | 
			
		||||
	mode = get_current_lock(resource);
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case LCK_NULL: type = "NL"; break;
 | 
			
		||||
	case LCK_READ: type = "CR"; break;
 | 
			
		||||
	case LCK_PREAD:type = "PR"; break;
 | 
			
		||||
	case LCK_WRITE:type = "PW"; break;
 | 
			
		||||
	case LCK_EXCL: type = "EX"; break;
 | 
			
		||||
	default: type = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("do_lock_query: resource '%s', mode %i (%s)\n", resource, mode, type ?: "--");
 | 
			
		||||
 | 
			
		||||
	return type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is the LOCK_LV part that happens on all nodes in the cluster -
 | 
			
		||||
   it is responsible for the interaction with device-mapper and LVM */
 | 
			
		||||
int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
 | 
			
		||||
{
 | 
			
		||||
	int status = 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("do_lock_lv: resource '%s', cmd = %s, flags = %s, critical_section = %d\n",
 | 
			
		||||
		 resource, decode_locking_cmd(command), decode_flags(lock_flags), critical_section());
 | 
			
		||||
 | 
			
		||||
	if (!cmd->initialized.config || config_files_changed(cmd)) {
 | 
			
		||||
		/* Reinitialise various settings inc. logging, filters */
 | 
			
		||||
		if (do_refresh_cache()) {
 | 
			
		||||
			log_error("Updated config file invalid. Aborting.");
 | 
			
		||||
			return EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
	init_test((lock_flags & LCK_TEST_MODE) ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
	if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
 | 
			
		||||
		init_mirror_in_sync(1);
 | 
			
		||||
 | 
			
		||||
	if (lock_flags & LCK_DMEVENTD_MONITOR_IGNORE)
 | 
			
		||||
		init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE);
 | 
			
		||||
	else {
 | 
			
		||||
		if (lock_flags & LCK_DMEVENTD_MONITOR_MODE)
 | 
			
		||||
			init_dmeventd_monitor(1);
 | 
			
		||||
		else
 | 
			
		||||
			init_dmeventd_monitor(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->partial_activation = (lock_flags & LCK_PARTIAL_MODE) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	/* clvmd should never try to read suspended device */
 | 
			
		||||
	init_ignore_suspended_devices(1);
 | 
			
		||||
 | 
			
		||||
	switch (command & LCK_MASK) {
 | 
			
		||||
	case LCK_LV_EXCLUSIVE:
 | 
			
		||||
		status = do_activate_lv(resource, command, lock_flags, LCK_EXCL);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case LCK_LV_SUSPEND:
 | 
			
		||||
		status = do_suspend_lv(resource, command, lock_flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case LCK_UNLOCK:
 | 
			
		||||
	case LCK_LV_RESUME:	/* if active */
 | 
			
		||||
		status = do_resume_lv(resource, command, lock_flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case LCK_LV_ACTIVATE:
 | 
			
		||||
		status = do_activate_lv(resource, command, lock_flags, LCK_READ);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case LCK_LV_DEACTIVATE:
 | 
			
		||||
		status = do_deactivate_lv(resource, command, lock_flags);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		DEBUGLOG("Invalid LV command 0x%x\n", command);
 | 
			
		||||
		status = EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
 | 
			
		||||
		init_mirror_in_sync(0);
 | 
			
		||||
 | 
			
		||||
	cmd->partial_activation = 0;
 | 
			
		||||
 | 
			
		||||
	/* clean the pool for another command */
 | 
			
		||||
	dm_pool_empty(cmd->mem);
 | 
			
		||||
	init_test(0);
 | 
			
		||||
	pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Command return is %d, critical_section is %d\n", status, critical_section());
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Functions to do on the local node only BEFORE the cluster-wide stuff above happens */
 | 
			
		||||
int pre_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
 | 
			
		||||
{
 | 
			
		||||
	/* Nearly all the stuff happens cluster-wide. Apart from SUSPEND. Here we get the
 | 
			
		||||
	   lock out on this node (because we are the node modifying the metadata)
 | 
			
		||||
	   before suspending cluster-wide.
 | 
			
		||||
	   LCKF_CONVERT is used always, local node is going to modify metadata
 | 
			
		||||
	 */
 | 
			
		||||
	if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_SUSPEND &&
 | 
			
		||||
	    (command & LCK_CLUSTER_VG)) {
 | 
			
		||||
		DEBUGLOG("pre_lock_lv: resource '%s', cmd = %s, flags = %s\n",
 | 
			
		||||
			 resource, decode_locking_cmd(command), decode_flags(lock_flags));
 | 
			
		||||
 | 
			
		||||
		if (!(lock_flags & LCK_TEST_MODE) &&
 | 
			
		||||
		    hold_lock(resource, LCK_WRITE, LCKF_NOQUEUE | LCKF_CONVERT))
 | 
			
		||||
			return errno;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Functions to do on the local node only AFTER the cluster-wide stuff above happens */
 | 
			
		||||
int post_lock_lv(unsigned char command, unsigned char lock_flags,
 | 
			
		||||
		 char *resource)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
	unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	/* Opposite of above, done on resume after a metadata update */
 | 
			
		||||
	if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_RESUME &&
 | 
			
		||||
	    (command & LCK_CLUSTER_VG)) {
 | 
			
		||||
		int oldmode;
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("post_lock_lv: resource '%s', cmd = %s, flags = %s\n",
 | 
			
		||||
			 resource, decode_locking_cmd(command), decode_flags(lock_flags));
 | 
			
		||||
 | 
			
		||||
		/* If the lock state is PW then restore it to what it was */
 | 
			
		||||
		oldmode = get_current_lock(resource);
 | 
			
		||||
		if (oldmode == LCK_WRITE) {
 | 
			
		||||
			struct lvinfo lvi;
 | 
			
		||||
 | 
			
		||||
			pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
			status = lv_info_by_lvid(cmd, resource, origin_only, &lvi, 0, 0);
 | 
			
		||||
			pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
			if (!status)
 | 
			
		||||
				return EIO;
 | 
			
		||||
 | 
			
		||||
			if (!(lock_flags & LCK_TEST_MODE)) {
 | 
			
		||||
				if (lvi.exists) {
 | 
			
		||||
					if (hold_lock(resource, LCK_READ, LCKF_CONVERT))
 | 
			
		||||
						return errno;
 | 
			
		||||
				} else if (hold_unlock(resource))
 | 
			
		||||
					return errno;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a VG is in use by LVM1 so we don't stomp on it */
 | 
			
		||||
int do_check_lvm1(const char *vgname)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	status = check_lvm1_vg_inactive(cmd, vgname);
 | 
			
		||||
 | 
			
		||||
	return status == 1 ? 0 : EBUSY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int do_refresh_cache(void)
 | 
			
		||||
{
 | 
			
		||||
	DEBUGLOG("Refreshing context\n");
 | 
			
		||||
	log_notice("Refreshing context");
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
 | 
			
		||||
	if (!refresh_toolcontext(cmd)) {
 | 
			
		||||
		pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	init_full_scan_done(0);
 | 
			
		||||
	init_ignore_suspended_devices(1);
 | 
			
		||||
	lvmcache_force_next_label_scan();
 | 
			
		||||
	lvmcache_label_scan(cmd);
 | 
			
		||||
	dm_pool_empty(cmd->mem);
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handle VG lock - drop metadata or update lvmcache state
 | 
			
		||||
 */
 | 
			
		||||
void do_lock_vg(unsigned char command, unsigned char lock_flags, char *resource)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t lock_cmd = command;
 | 
			
		||||
	char *vgname = resource + 2;
 | 
			
		||||
 | 
			
		||||
	lock_cmd &= (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_HOLD);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check if LCK_CACHE should be set. All P_ locks except # are cache related.
 | 
			
		||||
	 */
 | 
			
		||||
	if (strncmp(resource, "P_#", 3) && !strncmp(resource, "P_", 2))
 | 
			
		||||
		lock_cmd |= LCK_CACHE;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("do_lock_vg: resource '%s', cmd = %s, flags = %s, critical_section = %d\n",
 | 
			
		||||
		 resource, decode_full_locking_cmd(lock_cmd), decode_flags(lock_flags), critical_section());
 | 
			
		||||
 | 
			
		||||
	/* P_#global causes a full cache refresh */
 | 
			
		||||
	if (!strcmp(resource, "P_" VG_GLOBAL)) {
 | 
			
		||||
		do_refresh_cache();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
	init_test((lock_flags & LCK_TEST_MODE) ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
	switch (lock_cmd) {
 | 
			
		||||
		case LCK_VG_COMMIT:
 | 
			
		||||
			DEBUGLOG("vg_commit notification for VG %s\n", vgname);
 | 
			
		||||
			lvmcache_commit_metadata(vgname);
 | 
			
		||||
			break;
 | 
			
		||||
		case LCK_VG_REVERT:
 | 
			
		||||
			DEBUGLOG("vg_revert notification for VG %s\n", vgname);
 | 
			
		||||
			lvmcache_drop_metadata(vgname, 1);
 | 
			
		||||
			break;
 | 
			
		||||
		case LCK_VG_DROP_CACHE:
 | 
			
		||||
		default:
 | 
			
		||||
			DEBUGLOG("Invalidating cached metadata for VG %s\n", vgname);
 | 
			
		||||
			lvmcache_drop_metadata(vgname, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	init_test(0);
 | 
			
		||||
	pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Ideally, clvmd should be started before any LVs are active
 | 
			
		||||
 * but this may not be the case...
 | 
			
		||||
 * I suppose this also comes in handy if clvmd crashes, not that it would!
 | 
			
		||||
 */
 | 
			
		||||
static int get_initial_state(struct dm_hash_table *excl_uuid)
 | 
			
		||||
{
 | 
			
		||||
	int lock_mode;
 | 
			
		||||
	char lv[65], vg[65], flags[26], vg_flags[26]; /* with space for '\0' */
 | 
			
		||||
	char uuid[65];
 | 
			
		||||
	char line[255];
 | 
			
		||||
	char *lvs_cmd;
 | 
			
		||||
	const char *lvm_binary = getenv("LVM_BINARY") ? : LVM_PATH;
 | 
			
		||||
	FILE *lvs;
 | 
			
		||||
 | 
			
		||||
	if (dm_asprintf(&lvs_cmd, "%s lvs  --config 'log{command_names=0 prefix=\"\"}' "
 | 
			
		||||
			"--nolocking --noheadings -o vg_uuid,lv_uuid,lv_attr,vg_attr",
 | 
			
		||||
			lvm_binary) < 0)
 | 
			
		||||
		return_0;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Maybe link and use liblvm2cmd directly instead of fork */
 | 
			
		||||
	if (!(lvs = popen(lvs_cmd, "r"))) {
 | 
			
		||||
		dm_free(lvs_cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (fgets(line, sizeof(line), lvs)) {
 | 
			
		||||
	        if (sscanf(line, "%64s %64s %25s %25s\n", vg, lv, flags, vg_flags) == 4) {
 | 
			
		||||
 | 
			
		||||
			/* States: s:suspended a:active S:dropped snapshot I:invalid snapshot */
 | 
			
		||||
		        if (strlen(vg) == 38 &&                         /* is is a valid UUID ? */
 | 
			
		||||
			    (flags[4] == 'a' || flags[4] == 's') &&	/* is it active or suspended? */
 | 
			
		||||
			    vg_flags[5] == 'c') {			/* is it clustered ? */
 | 
			
		||||
				/* Convert hyphen-separated UUIDs into one */
 | 
			
		||||
				memcpy(&uuid[0], &vg[0], 6);
 | 
			
		||||
				memcpy(&uuid[6], &vg[7], 4);
 | 
			
		||||
				memcpy(&uuid[10], &vg[12], 4);
 | 
			
		||||
				memcpy(&uuid[14], &vg[17], 4);
 | 
			
		||||
				memcpy(&uuid[18], &vg[22], 4);
 | 
			
		||||
				memcpy(&uuid[22], &vg[27], 4);
 | 
			
		||||
				memcpy(&uuid[26], &vg[32], 6);
 | 
			
		||||
				memcpy(&uuid[32], &lv[0], 6);
 | 
			
		||||
				memcpy(&uuid[38], &lv[7], 4);
 | 
			
		||||
				memcpy(&uuid[42], &lv[12], 4);
 | 
			
		||||
				memcpy(&uuid[46], &lv[17], 4);
 | 
			
		||||
				memcpy(&uuid[50], &lv[22], 4);
 | 
			
		||||
				memcpy(&uuid[54], &lv[27], 4);
 | 
			
		||||
				memcpy(&uuid[58], &lv[32], 6);
 | 
			
		||||
				uuid[64] = '\0';
 | 
			
		||||
 | 
			
		||||
				/* Look for this lock in the list of EX locks
 | 
			
		||||
				   we were passed on the command-line */
 | 
			
		||||
				lock_mode = (dm_hash_lookup(excl_uuid, uuid)) ?
 | 
			
		||||
					LCK_EXCL : LCK_READ;
 | 
			
		||||
 | 
			
		||||
				DEBUGLOG("getting initial lock for %s\n", uuid);
 | 
			
		||||
				if (hold_lock(uuid, lock_mode, LCKF_NOQUEUE))
 | 
			
		||||
					DEBUGLOG("Failed to hold lock %s\n", uuid);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (pclose(lvs))
 | 
			
		||||
		DEBUGLOG("lvs pclose failed: %s\n", strerror(errno));
 | 
			
		||||
 | 
			
		||||
	dm_free(lvs_cmd);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
 | 
			
		||||
			const char *message)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/* Send messages to the normal LVM2 logging system too,
 | 
			
		||||
	   so we get debug output when it's asked for.
 | 
			
		||||
 	   We need to NULL the function ptr otherwise it will just call
 | 
			
		||||
	   back into here! */
 | 
			
		||||
	init_log_fn(NULL);
 | 
			
		||||
	print_log(level, file, line, dm_errno, "%s", message);
 | 
			
		||||
	init_log_fn(lvm2_log_fn);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Ignore non-error messages, but store the latest one for returning
 | 
			
		||||
	 * to the user.
 | 
			
		||||
	 */
 | 
			
		||||
	if (level != _LOG_ERR && level != _LOG_FATAL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	strncpy(last_error, message, sizeof(last_error));
 | 
			
		||||
	last_error[sizeof(last_error)-1] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This checks some basic cluster-LVM configuration stuff */
 | 
			
		||||
static void check_config(void)
 | 
			
		||||
{
 | 
			
		||||
	int locking_type;
 | 
			
		||||
 | 
			
		||||
	locking_type = find_config_tree_int(cmd, global_locking_type_CFG, NULL);
 | 
			
		||||
 | 
			
		||||
	if (locking_type == 3) /* compiled-in cluster support */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (locking_type == 2) { /* External library, check name */
 | 
			
		||||
		const char *libname;
 | 
			
		||||
 | 
			
		||||
		libname = find_config_tree_str(cmd, global_locking_library_CFG, NULL);
 | 
			
		||||
		if (libname && strstr(libname, "liblvm2clusterlock.so"))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		log_error("Incorrect LVM locking library specified in lvm.conf, cluster operations may not work.");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	log_error("locking_type not set correctly in lvm.conf, cluster operations will not work.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Backups up the LVM metadata if it's changed */
 | 
			
		||||
void lvm_do_backup(const char *vgname)
 | 
			
		||||
{
 | 
			
		||||
	struct volume_group * vg;
 | 
			
		||||
	int consistent = 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("Triggering backup of VG metadata for %s.\n", vgname);
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
 | 
			
		||||
	vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, WARN_PV_READ, &consistent);
 | 
			
		||||
 | 
			
		||||
	if (vg && consistent)
 | 
			
		||||
		check_current_backup(vg);
 | 
			
		||||
	else
 | 
			
		||||
		log_error("Error backing up metadata, can't find VG for group %s", vgname);
 | 
			
		||||
 | 
			
		||||
	release_vg(vg);
 | 
			
		||||
	dm_pool_empty(cmd->mem);
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name)
 | 
			
		||||
{
 | 
			
		||||
	struct lv_info *lvi;
 | 
			
		||||
 | 
			
		||||
	*name = NULL;
 | 
			
		||||
	if (!v)
 | 
			
		||||
		v = dm_hash_get_first(lv_hash);
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		if (v) {
 | 
			
		||||
			lvi = dm_hash_get_data(lv_hash, v);
 | 
			
		||||
			DEBUGLOG("Looking for EX locks. found %x mode %d\n", lvi->lock_id, lvi->lock_mode);
 | 
			
		||||
 | 
			
		||||
			if (lvi->lock_mode == LCK_EXCL) {
 | 
			
		||||
				*name = dm_hash_get_key(lv_hash, v);
 | 
			
		||||
			}
 | 
			
		||||
			v = dm_hash_get_next(lv_hash, v);
 | 
			
		||||
		}
 | 
			
		||||
	} while (v && !*name);
 | 
			
		||||
 | 
			
		||||
	if (*name)
 | 
			
		||||
		DEBUGLOG("returning EXclusive UUID %s\n", *name);
 | 
			
		||||
	return v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void lvm_do_fs_unlock(void)
 | 
			
		||||
{
 | 
			
		||||
	pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
	DEBUGLOG("Syncing device names\n");
 | 
			
		||||
	fs_unlock();
 | 
			
		||||
	pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called to initialise the LVM context of the daemon */
 | 
			
		||||
int init_clvm(struct dm_hash_table *excl_uuid)
 | 
			
		||||
{
 | 
			
		||||
	/* Use LOG_DAEMON for syslog messages instead of LOG_USER */
 | 
			
		||||
	init_syslog(LOG_DAEMON);
 | 
			
		||||
	openlog("clvmd", LOG_PID, LOG_DAEMON);
 | 
			
		||||
 | 
			
		||||
	/* Initialise already held locks */
 | 
			
		||||
	if (!get_initial_state(excl_uuid))
 | 
			
		||||
		log_error("Cannot load initial lock states.");
 | 
			
		||||
 | 
			
		||||
	if (!udev_init_library_context())
 | 
			
		||||
		stack;
 | 
			
		||||
 | 
			
		||||
	if (!(cmd = create_toolcontext(1, NULL, 0, 1, 1, 1))) {
 | 
			
		||||
		log_error("Failed to allocate command context");
 | 
			
		||||
		udev_fin_library_context();
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (stored_errno()) {
 | 
			
		||||
		destroy_toolcontext(cmd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->cmd_line = "clvmd";
 | 
			
		||||
 | 
			
		||||
	/* Check lvm.conf is setup for cluster-LVM */
 | 
			
		||||
	check_config();
 | 
			
		||||
	init_ignore_suspended_devices(1);
 | 
			
		||||
 | 
			
		||||
	/* Trap log messages so we can pass them back to the user */
 | 
			
		||||
	init_log_fn(lvm2_log_fn);
 | 
			
		||||
	memlock_inc_daemon(cmd);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void destroy_lvm(void)
 | 
			
		||||
{
 | 
			
		||||
	if (cmd) {
 | 
			
		||||
		memlock_dec_daemon(cmd);
 | 
			
		||||
		destroy_toolcontext(cmd);
 | 
			
		||||
		udev_fin_library_context();
 | 
			
		||||
		cmd = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								daemons/clvmd/lvm-functions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								daemons/clvmd/lvm-functions.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Functions in lvm-functions.c */
 | 
			
		||||
 | 
			
		||||
#ifndef _LVM_FUNCTIONS_H
 | 
			
		||||
#define _LVM_FUNCTIONS_H
 | 
			
		||||
 | 
			
		||||
extern int pre_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
 | 
			
		||||
		       char *resource);
 | 
			
		||||
extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
 | 
			
		||||
		      char *resource);
 | 
			
		||||
extern const char *do_lock_query(char *resource);
 | 
			
		||||
extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
 | 
			
		||||
			char *resource);
 | 
			
		||||
extern int do_check_lvm1(const char *vgname);
 | 
			
		||||
extern int do_refresh_cache(void);
 | 
			
		||||
extern int init_clvm(struct dm_hash_table *excl_uuid);
 | 
			
		||||
extern void destroy_lvm(void);
 | 
			
		||||
extern void init_lvhash(void);
 | 
			
		||||
extern void destroy_lvhash(void);
 | 
			
		||||
extern void lvm_do_backup(const char *vgname);
 | 
			
		||||
extern char *get_last_lvm_error(void);
 | 
			
		||||
extern void do_lock_vg(unsigned char command, unsigned char lock_flags,
 | 
			
		||||
		      char *resource);
 | 
			
		||||
extern struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name);
 | 
			
		||||
void lvm_do_fs_unlock(void);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										382
									
								
								daemons/clvmd/refresh_clvmd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								daemons/clvmd/refresh_clvmd.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,382 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* FIXME Remove duplicated functions from this file. */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Send a command to a running clvmd from the command-line
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
 | 
			
		||||
#include "clvm.h"
 | 
			
		||||
#include "refresh_clvmd.h"
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
typedef struct lvm_response {
 | 
			
		||||
	char node[255];
 | 
			
		||||
	char *response;
 | 
			
		||||
	int status;
 | 
			
		||||
	int len;
 | 
			
		||||
} lvm_response_t;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This gets stuck at the start of memory we allocate so we
 | 
			
		||||
 * can sanity-check it at deallocation time
 | 
			
		||||
 */
 | 
			
		||||
#define LVM_SIGNATURE 0x434C564D
 | 
			
		||||
 | 
			
		||||
static int _clvmd_sock = -1;
 | 
			
		||||
 | 
			
		||||
/* Open connection to the clvm daemon */
 | 
			
		||||
static int _open_local_sock(void)
 | 
			
		||||
{
 | 
			
		||||
	int local_socket;
 | 
			
		||||
	struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
 | 
			
		||||
 | 
			
		||||
	if (!dm_strncpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(sockaddr.sun_path))) {
 | 
			
		||||
		fprintf(stderr, "%s: clvmd socket name too long.", CLVMD_SOCKNAME);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Open local socket */
 | 
			
		||||
	if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
 | 
			
		||||
		fprintf(stderr, "Local socket creation failed: %s", strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (connect(local_socket,(struct sockaddr *) &sockaddr,
 | 
			
		||||
		    sizeof(sockaddr))) {
 | 
			
		||||
		int saved_errno = errno;
 | 
			
		||||
 | 
			
		||||
		fprintf(stderr, "connect() failed on local socket: %s\n",
 | 
			
		||||
			  strerror(errno));
 | 
			
		||||
		if (close(local_socket))
 | 
			
		||||
			return -1;
 | 
			
		||||
 | 
			
		||||
		errno = saved_errno;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return local_socket;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send a request and return the status */
 | 
			
		||||
static int _send_request(const char *inbuf, int inlen, char **retbuf, int no_response)
 | 
			
		||||
{
 | 
			
		||||
	char outbuf[PIPE_BUF];
 | 
			
		||||
	struct clvm_header *outheader = (struct clvm_header *) outbuf;
 | 
			
		||||
	int len;
 | 
			
		||||
	unsigned off;
 | 
			
		||||
	int buflen;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	/* Send it to CLVMD */
 | 
			
		||||
 rewrite:
 | 
			
		||||
	if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
 | 
			
		||||
	        if (err == -1 && errno == EINTR)
 | 
			
		||||
		        goto rewrite;
 | 
			
		||||
		fprintf(stderr, "Error writing data to clvmd: %s", strerror(errno));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (no_response)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* Get the response */
 | 
			
		||||
 reread:
 | 
			
		||||
	if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
 | 
			
		||||
	        if (errno == EINTR)
 | 
			
		||||
		        goto reread;
 | 
			
		||||
		fprintf(stderr, "Error reading data from clvmd: %s", strerror(errno));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (len == 0) {
 | 
			
		||||
		fprintf(stderr, "EOF reading CLVMD");
 | 
			
		||||
		errno = ENOTCONN;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Allocate buffer */
 | 
			
		||||
	buflen = len + outheader->arglen;
 | 
			
		||||
	*retbuf = dm_malloc(buflen);
 | 
			
		||||
	if (!*retbuf) {
 | 
			
		||||
		errno = ENOMEM;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Copy the header */
 | 
			
		||||
	memcpy(*retbuf, outbuf, len);
 | 
			
		||||
	outheader = (struct clvm_header *) *retbuf;
 | 
			
		||||
 | 
			
		||||
	/* Read the returned values */
 | 
			
		||||
	off = 1;		/* we've already read the first byte */
 | 
			
		||||
	while (off <= outheader->arglen && len > 0) {
 | 
			
		||||
		len = read(_clvmd_sock, outheader->args + off,
 | 
			
		||||
			   buflen - off - offsetof(struct clvm_header, args));
 | 
			
		||||
		if (len > 0)
 | 
			
		||||
			off += len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Was it an error ? */
 | 
			
		||||
	if (outheader->status != 0) {
 | 
			
		||||
		errno = outheader->status;
 | 
			
		||||
 | 
			
		||||
		/* Only return an error here if there are no node-specific
 | 
			
		||||
		   errors present in the message that might have more detail */
 | 
			
		||||
		if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
 | 
			
		||||
			fprintf(stderr, "cluster request failed: %s\n", strerror(errno));
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Build the structure header and parse-out wildcard node names */
 | 
			
		||||
static void _build_header(struct clvm_header *head, int cmd, const char *node,
 | 
			
		||||
			  unsigned int len)
 | 
			
		||||
{
 | 
			
		||||
	head->cmd = cmd;
 | 
			
		||||
	head->status = 0;
 | 
			
		||||
	head->flags = 0;
 | 
			
		||||
	head->xid = 0;
 | 
			
		||||
	head->clientid = 0;
 | 
			
		||||
	if (len)
 | 
			
		||||
		/* 1 byte is used from struct clvm_header.args[1], so -> len - 1 */
 | 
			
		||||
		head->arglen = len - 1;
 | 
			
		||||
	else {
 | 
			
		||||
		head->arglen = 0;
 | 
			
		||||
		*head->args = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Translate special node names.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!node || !strcmp(node, NODE_ALL))
 | 
			
		||||
		head->node[0] = '\0';
 | 
			
		||||
	else if (!strcmp(node, NODE_LOCAL)) {
 | 
			
		||||
		head->node[0] = '\0';
 | 
			
		||||
		head->flags = CLVMD_FLAG_LOCAL;
 | 
			
		||||
	} else
 | 
			
		||||
		strcpy(head->node, node);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Send a message to a(or all) node(s) in the cluster and wait for replies
 | 
			
		||||
 */
 | 
			
		||||
static int _cluster_request(char cmd, const char *node, void *data, int len,
 | 
			
		||||
			    lvm_response_t ** response, int *num, int no_response)
 | 
			
		||||
{
 | 
			
		||||
	char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
 | 
			
		||||
	char *inptr;
 | 
			
		||||
	char *retbuf = NULL;
 | 
			
		||||
	int status;
 | 
			
		||||
	int i;
 | 
			
		||||
	int num_responses = 0;
 | 
			
		||||
	struct clvm_header *head = (struct clvm_header *) outbuf;
 | 
			
		||||
	lvm_response_t *rarray;
 | 
			
		||||
 | 
			
		||||
	*num = 0;
 | 
			
		||||
 | 
			
		||||
	if (_clvmd_sock == -1)
 | 
			
		||||
		_clvmd_sock = _open_local_sock();
 | 
			
		||||
 | 
			
		||||
	if (_clvmd_sock == -1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	_build_header(head, cmd, node, len);
 | 
			
		||||
	if (len)
 | 
			
		||||
		memcpy(head->node + strlen(head->node) + 1, data, len);
 | 
			
		||||
 | 
			
		||||
	status = _send_request(outbuf, sizeof(struct clvm_header) +
 | 
			
		||||
			       strlen(head->node) + len, &retbuf, no_response);
 | 
			
		||||
	if (!status || no_response)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* Count the number of responses we got */
 | 
			
		||||
	head = (struct clvm_header *) retbuf;
 | 
			
		||||
	inptr = head->args;
 | 
			
		||||
	while (inptr[0]) {
 | 
			
		||||
		num_responses++;
 | 
			
		||||
		inptr += strlen(inptr) + 1;
 | 
			
		||||
		inptr += sizeof(int);
 | 
			
		||||
		inptr += strlen(inptr) + 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Allocate response array.
 | 
			
		||||
	 * With an extra pair of INTs on the front to sanity
 | 
			
		||||
	 * check the pointer when we are given it back to free
 | 
			
		||||
	 */
 | 
			
		||||
	*response = NULL;
 | 
			
		||||
	if (!(rarray = dm_malloc(sizeof(lvm_response_t) * num_responses +
 | 
			
		||||
				 sizeof(int) * 2))) {
 | 
			
		||||
		errno = ENOMEM;
 | 
			
		||||
		status = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Unpack the response into an lvm_response_t array */
 | 
			
		||||
	inptr = head->args;
 | 
			
		||||
	i = 0;
 | 
			
		||||
	while (inptr[0]) {
 | 
			
		||||
		strcpy(rarray[i].node, inptr);
 | 
			
		||||
		inptr += strlen(inptr) + 1;
 | 
			
		||||
 | 
			
		||||
		memcpy(&rarray[i].status, inptr, sizeof(int));
 | 
			
		||||
		inptr += sizeof(int);
 | 
			
		||||
 | 
			
		||||
		rarray[i].response = dm_malloc(strlen(inptr) + 1);
 | 
			
		||||
		if (rarray[i].response == NULL) {
 | 
			
		||||
			/* Free up everything else and return error */
 | 
			
		||||
			int j;
 | 
			
		||||
			for (j = 0; j < i; j++)
 | 
			
		||||
				dm_free(rarray[i].response);
 | 
			
		||||
			dm_free(rarray);
 | 
			
		||||
			errno = ENOMEM;
 | 
			
		||||
			status = 0;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		strcpy(rarray[i].response, inptr);
 | 
			
		||||
		rarray[i].len = strlen(inptr);
 | 
			
		||||
		inptr += strlen(inptr) + 1;
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
	*num = num_responses;
 | 
			
		||||
	*response = rarray;
 | 
			
		||||
 | 
			
		||||
      out:
 | 
			
		||||
	dm_free(retbuf);
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free reply array */
 | 
			
		||||
static int _cluster_free_request(lvm_response_t * response, int num)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num; i++) {
 | 
			
		||||
		dm_free(response[i].response);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm_free(response);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int refresh_clvmd(int all_nodes)
 | 
			
		||||
{
 | 
			
		||||
	int num_responses;
 | 
			
		||||
	char args[1]; // No args really.
 | 
			
		||||
	lvm_response_t *response = NULL;
 | 
			
		||||
	int saved_errno;
 | 
			
		||||
	int status;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	status = _cluster_request(CLVMD_CMD_REFRESH, all_nodes ? NODE_ALL : NODE_LOCAL, args, 0, &response, &num_responses, 0);
 | 
			
		||||
 | 
			
		||||
	/* If any nodes were down then display them and return an error */
 | 
			
		||||
	for (i = 0; i < num_responses; i++) {
 | 
			
		||||
		if (response[i].status == EHOSTDOWN) {
 | 
			
		||||
			fprintf(stderr, "clvmd not running on node %s",
 | 
			
		||||
				  response[i].node);
 | 
			
		||||
			status = 0;
 | 
			
		||||
			errno = response[i].status;
 | 
			
		||||
		} else if (response[i].status) {
 | 
			
		||||
			fprintf(stderr, "Error resetting node %s: %s",
 | 
			
		||||
				  response[i].node,
 | 
			
		||||
				  response[i].response[0] ?
 | 
			
		||||
				  	response[i].response :
 | 
			
		||||
				  	strerror(response[i].status));
 | 
			
		||||
			status = 0;
 | 
			
		||||
			errno = response[i].status;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	saved_errno = errno;
 | 
			
		||||
	_cluster_free_request(response, num_responses);
 | 
			
		||||
	errno = saved_errno;
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int restart_clvmd(int all_nodes)
 | 
			
		||||
{
 | 
			
		||||
	int dummy, status;
 | 
			
		||||
 | 
			
		||||
	status = _cluster_request(CLVMD_CMD_RESTART, all_nodes ? NODE_ALL : NODE_LOCAL, NULL, 0, NULL, &dummy, 1);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * FIXME: we cannot receive response, clvmd re-exec before it.
 | 
			
		||||
	 *        but also should not close socket too early (the whole rq is dropped then).
 | 
			
		||||
	 * FIXME: This should be handled this way:
 | 
			
		||||
	 *  - client waits for RESTART ack (and socket close)
 | 
			
		||||
	 *  - server restarts
 | 
			
		||||
	 *  - client checks that server is ready again (VERSION command?)
 | 
			
		||||
	 */
 | 
			
		||||
	usleep(500000);
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int debug_clvmd(int level, int clusterwide)
 | 
			
		||||
{
 | 
			
		||||
	int num_responses;
 | 
			
		||||
	char args[1];
 | 
			
		||||
	const char *nodes;
 | 
			
		||||
	lvm_response_t *response = NULL;
 | 
			
		||||
	int saved_errno;
 | 
			
		||||
	int status;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	args[0] = level;
 | 
			
		||||
	if (clusterwide)
 | 
			
		||||
		nodes = NODE_ALL;
 | 
			
		||||
	else
 | 
			
		||||
		nodes = NODE_LOCAL;
 | 
			
		||||
 | 
			
		||||
	status = _cluster_request(CLVMD_CMD_SET_DEBUG, nodes, args, 1, &response, &num_responses, 0);
 | 
			
		||||
 | 
			
		||||
	/* If any nodes were down then display them and return an error */
 | 
			
		||||
	for (i = 0; i < num_responses; i++) {
 | 
			
		||||
		if (response[i].status == EHOSTDOWN) {
 | 
			
		||||
			fprintf(stderr, "clvmd not running on node %s",
 | 
			
		||||
				  response[i].node);
 | 
			
		||||
			status = 0;
 | 
			
		||||
			errno = response[i].status;
 | 
			
		||||
		} else if (response[i].status) {
 | 
			
		||||
			fprintf(stderr, "Error setting debug on node %s: %s",
 | 
			
		||||
				  response[i].node,
 | 
			
		||||
				  response[i].response[0] ?
 | 
			
		||||
				  	response[i].response :
 | 
			
		||||
				  	strerror(response[i].status));
 | 
			
		||||
			status = 0;
 | 
			
		||||
			errno = response[i].status;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	saved_errno = errno;
 | 
			
		||||
	_cluster_free_request(response, num_responses);
 | 
			
		||||
	errno = saved_errno;
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								daemons/clvmd/refresh_clvmd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								daemons/clvmd/refresh_clvmd.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int refresh_clvmd(int all_nodes);
 | 
			
		||||
int restart_clvmd(int all_nodes);
 | 
			
		||||
int debug_clvmd(int level, int clusterwide);
 | 
			
		||||
 | 
			
		||||
@@ -17,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)
 | 
			
		||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
 | 
			
		||||
LIBS += -ldevmapper
 | 
			
		||||
LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
 | 
			
		||||
CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS)
 | 
			
		||||
 | 
			
		||||
cmirrord: $(OBJECTS)
 | 
			
		||||
	@echo "    [CC] $@"
 | 
			
		||||
	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
 | 
			
		||||
		$(LMLIBS) -L$(top_builddir)/libdm -ldevmapper $(LIBS)
 | 
			
		||||
cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
 | 
			
		||||
		$(LVMLIBS) $(LMLIBS) $(LIBS)
 | 
			
		||||
 | 
			
		||||
install_cluster: $(TARGETS)
 | 
			
		||||
	@echo "    [INSTALL] $<"
 | 
			
		||||
	$(Q) $(INSTALL_PROGRAM) -D $< $(usrsbindir)/$(<F)
 | 
			
		||||
 | 
			
		||||
install: install_cluster
 | 
			
		||||
install: $(TARGETS)
 | 
			
		||||
	$(INSTALL_PROGRAM) -D cmirrord $(usrsbindir)/cmirrord
 | 
			
		||||
 
 | 
			
		||||
@@ -245,7 +245,6 @@ static void daemonize(void)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_OPEN("cmirrord", LOG_PID, LOG_DAEMON);
 | 
			
		||||
	/* coverity[leaked_handle] devnull cannot leak here */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -16,11 +16,7 @@
 | 
			
		||||
#include "functions.h"
 | 
			
		||||
#include "link_mon.h"
 | 
			
		||||
#include "local.h"
 | 
			
		||||
#include "lib/mm/xlate.h"
 | 
			
		||||
#include "base/memory/zalloc.h"
 | 
			
		||||
 | 
			
		||||
/* FIXME: remove this and the code */
 | 
			
		||||
#define CMIRROR_HAS_CHECKPOINT 0
 | 
			
		||||
#include "xlate.h"
 | 
			
		||||
 | 
			
		||||
#include <corosync/cpg.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
@@ -170,9 +166,6 @@ int cluster_send(struct clog_request *rq)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	int found = 0;
 | 
			
		||||
#if CMIRROR_HAS_CHECKPOINT
 | 
			
		||||
	int count = 0;
 | 
			
		||||
#endif
 | 
			
		||||
	struct iovec iov;
 | 
			
		||||
	struct clog_cpg *entry;
 | 
			
		||||
 | 
			
		||||
@@ -189,7 +182,7 @@ int cluster_send(struct clog_request *rq)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Once the request heads for the cluster, the luid loses
 | 
			
		||||
	 * Once the request heads for the cluster, the luid looses
 | 
			
		||||
	 * all its meaning.
 | 
			
		||||
	 */
 | 
			
		||||
	rq->u_rq.luid = 0;
 | 
			
		||||
@@ -210,6 +203,8 @@ int cluster_send(struct clog_request *rq)
 | 
			
		||||
 | 
			
		||||
#if CMIRROR_HAS_CHECKPOINT
 | 
			
		||||
	do {
 | 
			
		||||
		int count = 0;
 | 
			
		||||
 | 
			
		||||
		r = cpg_mcast_joined(entry->handle, CPG_TYPE_AGREED, &iov, 1);
 | 
			
		||||
		if (r != SA_AIS_ERR_TRY_AGAIN)
 | 
			
		||||
			break;
 | 
			
		||||
@@ -403,12 +398,13 @@ static struct checkpoint_data *prepare_checkpoint(struct clog_cpg *entry,
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new = zalloc(sizeof(*new));
 | 
			
		||||
	new = malloc(sizeof(*new));
 | 
			
		||||
	if (!new) {
 | 
			
		||||
		LOG_ERROR("Unable to create checkpoint data for %u",
 | 
			
		||||
			  cp_requester);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	memset(new, 0, sizeof(*new));
 | 
			
		||||
	new->requester = cp_requester;
 | 
			
		||||
	strncpy(new->uuid, entry->name.value, entry->name.length);
 | 
			
		||||
 | 
			
		||||
@@ -643,12 +639,13 @@ static int export_checkpoint(struct checkpoint_data *cp)
 | 
			
		||||
	rq_size += RECOVERING_REGION_SECTION_SIZE;
 | 
			
		||||
	rq_size += cp->bitmap_size * 2; /* clean|sync_bits */
 | 
			
		||||
 | 
			
		||||
	rq = zalloc(rq_size);
 | 
			
		||||
	rq = malloc(rq_size);
 | 
			
		||||
	if (!rq) {
 | 
			
		||||
		LOG_ERROR("export_checkpoint: "
 | 
			
		||||
			  "Unable to allocate transfer structs");
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
	memset(rq, 0, rq_size);
 | 
			
		||||
 | 
			
		||||
	dm_list_init(&rq->u.list);
 | 
			
		||||
	rq->u_rq.request_type = DM_ULOG_CHECKPOINT_READY;
 | 
			
		||||
@@ -1383,7 +1380,7 @@ static void cpg_leave_callback(struct clog_cpg *match,
 | 
			
		||||
			       size_t member_list_entries)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	int j, fd = -1;
 | 
			
		||||
	int j, fd;
 | 
			
		||||
	uint32_t lowest = match->lowest_id;
 | 
			
		||||
	struct clog_request *rq, *n;
 | 
			
		||||
	struct checkpoint_data *p_cp, *c_cp;
 | 
			
		||||
@@ -1548,7 +1545,7 @@ static void cpg_config_callback(cpg_handle_t handle, const struct cpg_name *gnam
 | 
			
		||||
				   member_list, member_list_entries);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cpg_callbacks_t cpg_callbacks = {
 | 
			
		||||
cpg_callbacks_t cpg_callbacks = {
 | 
			
		||||
	.cpg_deliver_fn = cpg_message_callback,
 | 
			
		||||
	.cpg_confchg_fn = cpg_config_callback,
 | 
			
		||||
};
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -39,7 +39,7 @@ struct clog_request {
 | 
			
		||||
	 * machine.  If the two are equal, there is no need
 | 
			
		||||
	 * to do endian conversions.
 | 
			
		||||
	 */
 | 
			
		||||
	union version_u {
 | 
			
		||||
	union {
 | 
			
		||||
		uint64_t version[2]; /* LE version and native version */
 | 
			
		||||
		struct dm_list list;
 | 
			
		||||
	} u;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "cluster.h"
 | 
			
		||||
#include "compat.h"
 | 
			
		||||
#include "lib/mm/xlate.h"
 | 
			
		||||
#include "xlate.h"
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "functions.h"
 | 
			
		||||
#include "base/memory/zalloc.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/sysmacros.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
@@ -378,7 +377,7 @@ static int _clog_ctr(char *uuid, uint64_t luid,
 | 
			
		||||
	uint32_t block_on_error = 0;
 | 
			
		||||
 | 
			
		||||
	int disk_log;
 | 
			
		||||
	char disk_path[PATH_MAX] = { 0 };
 | 
			
		||||
	char disk_path[128];
 | 
			
		||||
	int unlink_path = 0;
 | 
			
		||||
	long page_size;
 | 
			
		||||
	int pages;
 | 
			
		||||
@@ -436,7 +435,7 @@ static int _clog_ctr(char *uuid, uint64_t luid,
 | 
			
		||||
			block_on_error = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lc = zalloc(sizeof(*lc));
 | 
			
		||||
	lc = dm_zalloc(sizeof(*lc));
 | 
			
		||||
	if (!lc) {
 | 
			
		||||
		LOG_ERROR("Unable to allocate cluster log context");
 | 
			
		||||
		r = -ENOMEM;
 | 
			
		||||
@@ -452,19 +451,15 @@ static int _clog_ctr(char *uuid, uint64_t luid,
 | 
			
		||||
	lc->skip_bit_warning = region_count;
 | 
			
		||||
	lc->disk_fd = -1;
 | 
			
		||||
	lc->log_dev_failed = 0;
 | 
			
		||||
	if (!dm_strncpy(lc->uuid, uuid, DM_UUID_LEN)) {
 | 
			
		||||
		LOG_ERROR("Cannot use too long UUID %s.", uuid);
 | 
			
		||||
		r = -EINVAL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
	strncpy(lc->uuid, uuid, DM_UUID_LEN);
 | 
			
		||||
	lc->luid = luid;
 | 
			
		||||
 | 
			
		||||
	if (get_log(lc->uuid, lc->luid) ||
 | 
			
		||||
	    get_pending_log(lc->uuid, lc->luid)) {
 | 
			
		||||
		LOG_ERROR("[%s/%" PRIu64 "u] Log already exists, unable to create.",
 | 
			
		||||
			  SHORT_UUID(lc->uuid), lc->luid);
 | 
			
		||||
		r = -EINVAL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
		dm_free(lc);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm_list_init(&lc->mark_list);
 | 
			
		||||
@@ -533,9 +528,9 @@ fail:
 | 
			
		||||
			LOG_ERROR("Close device error, %s: %s",
 | 
			
		||||
				  disk_path, strerror(errno));
 | 
			
		||||
		free(lc->disk_buffer);
 | 
			
		||||
		free(lc->sync_bits);
 | 
			
		||||
		free(lc->clean_bits);
 | 
			
		||||
		free(lc);
 | 
			
		||||
		dm_free(lc->sync_bits);
 | 
			
		||||
		dm_free(lc->clean_bits);
 | 
			
		||||
		dm_free(lc);
 | 
			
		||||
	}
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
@@ -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 \
 | 
			
		||||
@@ -47,7 +37,6 @@ endif
 | 
			
		||||
 | 
			
		||||
LIB_VERSION = $(LIB_VERSION_DM)
 | 
			
		||||
LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
 | 
			
		||||
LIBS = $(PTHREAD_LIBS) -L$(interfacebuilddir) -ldevmapper
 | 
			
		||||
 | 
			
		||||
CLEAN_TARGETS = dmeventd.static $(LIB_NAME).a
 | 
			
		||||
 | 
			
		||||
@@ -57,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
 | 
			
		||||
@@ -65,47 +55,51 @@ include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
all: device-mapper
 | 
			
		||||
device-mapper: $(TARGETS)
 | 
			
		||||
plugins.device-mapper: $(LIB_SHARED)
 | 
			
		||||
 | 
			
		||||
LIBS += -ldevmapper
 | 
			
		||||
LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
 | 
			
		||||
 | 
			
		||||
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
 | 
			
		||||
dmeventd: $(LIB_SHARED) dmeventd.o
 | 
			
		||||
	@echo "    [CC] $@"
 | 
			
		||||
	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) dmeventd.o \
 | 
			
		||||
		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS)
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) -L. -o $@ dmeventd.o \
 | 
			
		||||
	$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
 | 
			
		||||
 | 
			
		||||
dmeventd.static: $(LIB_STATIC) dmeventd.o
 | 
			
		||||
	@echo "    [CC] $@"
 | 
			
		||||
	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static dmeventd.o \
 | 
			
		||||
		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS)
 | 
			
		||||
dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
 | 
			
		||||
	dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS)
 | 
			
		||||
 | 
			
		||||
ifeq ("@PKGCONFIG@", "yes")
 | 
			
		||||
  INSTALL_LIB_TARGETS += install_pkgconfig
 | 
			
		||||
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. */
 | 
			
		||||
@@ -101,28 +99,13 @@ static time_t _idle_since = 0;
 | 
			
		||||
static char **_initial_registrations = 0;
 | 
			
		||||
 | 
			
		||||
/* FIXME Make configurable at runtime */
 | 
			
		||||
 | 
			
		||||
/* All libdm messages */
 | 
			
		||||
__attribute__((format(printf, 5, 6)))
 | 
			
		||||
static void _libdm_log(int level, const char *file, int line,
 | 
			
		||||
		       int dm_errno_or_class, const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, format);
 | 
			
		||||
	dm_event_log("#dm", level, file, line, dm_errno_or_class, format, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* All dmeventd messages */
 | 
			
		||||
#undef LOG_MESG
 | 
			
		||||
#define LOG_MESG(l, f, ln, e, x...) _dmeventd_log(l, f, ln, e, ## x)
 | 
			
		||||
__attribute__((format(printf, 5, 6)))
 | 
			
		||||
__attribute__((format(printf, 4, 5)))
 | 
			
		||||
static void _dmeventd_log(int level, const char *file, int line,
 | 
			
		||||
			  int dm_errno_or_class, const char *format, ...)
 | 
			
		||||
			  const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, format);
 | 
			
		||||
	dm_event_log("dmeventd", level, file, line, dm_errno_or_class, format, ap);
 | 
			
		||||
	dm_event_log("dm", level, file, line, 0, format, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -264,19 +247,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 +380,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 +391,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 +405,11 @@ static struct thread_status *_alloc_thread_status(const struct message_data *dat
 | 
			
		||||
	if (!dm_task_set_uuid(thread->wait_task, data->device_uuid))
 | 
			
		||||
		goto_out;
 | 
			
		||||
 | 
			
		||||
	if (!(thread->device.uuid = strdup(data->device_uuid)))
 | 
			
		||||
	if (!(thread->device.uuid = dm_strdup(data->device_uuid)))
 | 
			
		||||
		goto_out;
 | 
			
		||||
 | 
			
		||||
	/* Until real name resolved, use UUID */
 | 
			
		||||
	if (!(thread->device.name = strdup(data->device_uuid)))
 | 
			
		||||
	if (!(thread->device.name = dm_strdup(data->device_uuid)))
 | 
			
		||||
		goto_out;
 | 
			
		||||
 | 
			
		||||
	/* runs ioctl and may register lvm2 pluging */
 | 
			
		||||
@@ -470,7 +453,7 @@ static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void *
 | 
			
		||||
	/*
 | 
			
		||||
	 * We use a smaller stack since it gets preallocated in its entirety
 | 
			
		||||
	 */
 | 
			
		||||
	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE + getpagesize());
 | 
			
		||||
	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If no-one will be waiting, we need to detach.
 | 
			
		||||
@@ -515,7 +498,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 +508,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 +521,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 +557,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 +591,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))
 | 
			
		||||
@@ -678,9 +661,6 @@ static int _get_status(struct message_data *message_data)
 | 
			
		||||
	char **buffers;
 | 
			
		||||
	char *message;
 | 
			
		||||
 | 
			
		||||
	if (!message_data->id)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	_lock_mutex();
 | 
			
		||||
	count = dm_list_size(&_thread_registry);
 | 
			
		||||
	buffers = alloca(sizeof(char*) * count);
 | 
			
		||||
@@ -699,8 +679,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);
 | 
			
		||||
@@ -715,7 +695,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -724,7 +704,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",
 | 
			
		||||
@@ -755,9 +735,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);
 | 
			
		||||
@@ -766,16 +745,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) {
 | 
			
		||||
@@ -788,10 +758,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();
 | 
			
		||||
			}
 | 
			
		||||
@@ -881,7 +848,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) {
 | 
			
		||||
@@ -1075,7 +1041,6 @@ out:
 | 
			
		||||
	 * "label at end of compound statement" */
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
	/* coverity[lock_order] _global_mutex is kept locked */
 | 
			
		||||
	pthread_cleanup_pop(1);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
@@ -1238,7 +1203,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,
 | 
			
		||||
@@ -1378,7 +1343,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);
 | 
			
		||||
 | 
			
		||||
@@ -1498,34 +1463,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -1540,7 +1508,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) {
 | 
			
		||||
@@ -1570,7 +1538,7 @@ static int _client_write(struct dm_event_fifos *fifos,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (header != temp)
 | 
			
		||||
		free(header);
 | 
			
		||||
		dm_free(header);
 | 
			
		||||
 | 
			
		||||
	return (bytes == size);
 | 
			
		||||
}
 | 
			
		||||
@@ -1632,7 +1600,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;
 | 
			
		||||
@@ -1674,7 +1642,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))
 | 
			
		||||
@@ -1746,8 +1714,7 @@ static void _init_thread_signals(void)
 | 
			
		||||
	sigset_t my_sigset;
 | 
			
		||||
	struct sigaction act = { .sa_handler = _sig_alarm };
 | 
			
		||||
 | 
			
		||||
	if (sigaction(SIGALRM, &act, NULL))
 | 
			
		||||
		log_sys_debug("sigaction", "SIGLARM");
 | 
			
		||||
	sigaction(SIGALRM, &act, NULL);
 | 
			
		||||
	sigfillset(&my_sigset);
 | 
			
		||||
 | 
			
		||||
	/* These are used for exiting */
 | 
			
		||||
@@ -1756,8 +1723,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -1769,7 +1735,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__
 | 
			
		||||
@@ -1987,7 +1953,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) {
 | 
			
		||||
@@ -2033,8 +1999,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
 | 
			
		||||
@@ -2073,18 +2039,19 @@ static void _restart_dmeventd(void)
 | 
			
		||||
			++count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if (!(_initial_registrations = zalloc(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;
 | 
			
		||||
		}
 | 
			
		||||
		message += strlen(message) + 1;
 | 
			
		||||
	}
 | 
			
		||||
	_initial_registrations[count] = NULL;
 | 
			
		||||
 | 
			
		||||
	if (version >= 2) {
 | 
			
		||||
		if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_PARAMETERS, "-", "-", 0, 0)) {
 | 
			
		||||
@@ -2224,7 +2191,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
		openlog("dmeventd", LOG_PID, LOG_DAEMON);
 | 
			
		||||
 | 
			
		||||
	dm_event_log_set(_debug_level, _use_syslog);
 | 
			
		||||
	dm_log_with_errno_init(_libdm_log);
 | 
			
		||||
	dm_log_init(_dmeventd_log);
 | 
			
		||||
 | 
			
		||||
	(void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
 | 
			
		||||
	if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
 | 
			
		||||
@@ -2247,8 +2214,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);
 | 
			
		||||
@@ -2267,14 +2233,11 @@ int main(int argc, char *argv[])
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		if (_idle_since) {
 | 
			
		||||
			if (_exit_now) {
 | 
			
		||||
				if (_exit_now == DM_SCHEDULED_EXIT)
 | 
			
		||||
					break; /* Only prints shutdown message */
 | 
			
		||||
				log_info("dmeventd detected break while being idle "
 | 
			
		||||
					 "for %ld second(s), exiting.",
 | 
			
		||||
					 (long) (time(NULL) - _idle_since));
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			if (idle_exit_timeout) {
 | 
			
		||||
			} else if (idle_exit_timeout) {
 | 
			
		||||
				now = time(NULL);
 | 
			
		||||
				if (now < _idle_since)
 | 
			
		||||
					_idle_since = now; /* clock change? */
 | 
			
		||||
@@ -2285,14 +2248,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,50 +234,44 @@ 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);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			if ((errno == EINTR) || (errno == EAGAIN))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			log_error("Unable to read from event server.");
 | 
			
		||||
			goto bad;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bytes += ret;
 | 
			
		||||
		if (!msg->data && (bytes == 2 * sizeof(uint32_t))) {
 | 
			
		||||
			msg->cmd = ntohl(header[0]);
 | 
			
		||||
			bytes = 0;
 | 
			
		||||
 | 
			
		||||
			if (!(size = msg->size = ntohl(header[1])))
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			if (!(buf = msg->data = malloc(msg->size))) {
 | 
			
		||||
				log_error("Unable to allocate message data.");
 | 
			
		||||
			else {
 | 
			
		||||
				log_error("Unable to read from event server.");
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bytes += ret;
 | 
			
		||||
		if (header && (bytes == 2 * sizeof(uint32_t))) {
 | 
			
		||||
			msg->cmd = ntohl(header[0]);
 | 
			
		||||
			msg->size = ntohl(header[1]);
 | 
			
		||||
			buf = msg->data = dm_malloc(msg->size);
 | 
			
		||||
			size = msg->size;
 | 
			
		||||
			bytes = 0;
 | 
			
		||||
			header = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bytes == size)
 | 
			
		||||
		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. */
 | 
			
		||||
@@ -338,9 +329,10 @@ static int _daemon_write(struct dm_event_fifos *fifos,
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			if ((errno == EINTR) || (errno == EAGAIN))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			log_error("Unable to talk to event daemon.");
 | 
			
		||||
			return 0;
 | 
			
		||||
			else {
 | 
			
		||||
				log_error("Unable to talk to event daemon.");
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bytes += ret;
 | 
			
		||||
@@ -380,13 +372,13 @@ int daemon_talk(struct dm_event_fifos *fifos,
 | 
			
		||||
	 */
 | 
			
		||||
	if (!_daemon_write(fifos, msg)) {
 | 
			
		||||
		stack;
 | 
			
		||||
		free(msg->data);
 | 
			
		||||
		dm_free(msg->data);
 | 
			
		||||
		msg->data = NULL;
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		free(msg->data);
 | 
			
		||||
		dm_free(msg->data);
 | 
			
		||||
		msg->data = NULL;
 | 
			
		||||
 | 
			
		||||
		if (!_daemon_read(fifos, msg)) {
 | 
			
		||||
@@ -462,8 +454,7 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
 | 
			
		||||
		if (close(fifos->client))
 | 
			
		||||
			log_sys_debug("close", fifos->client_path);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (errno != ENXIO && errno != ENOENT)  {
 | 
			
		||||
	} else if (errno != ENXIO && errno != ENOENT)  {
 | 
			
		||||
		/* problem */
 | 
			
		||||
		log_sys_error("open", fifos->client_path);
 | 
			
		||||
		return 0;
 | 
			
		||||
@@ -615,8 +606,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 +620,7 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag
 | 
			
		||||
 | 
			
		||||
	ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
 | 
			
		||||
 | 
			
		||||
	free(msg->data);
 | 
			
		||||
	dm_free(msg->data);
 | 
			
		||||
	msg->data = 0;
 | 
			
		||||
 | 
			
		||||
	if (!ret)
 | 
			
		||||
@@ -655,7 +646,6 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh)
 | 
			
		||||
	uuid = dm_task_get_uuid(dmt);
 | 
			
		||||
 | 
			
		||||
	if (!strstr(dmevh->dso, "libdevmapper-event-lvm2thin.so") &&
 | 
			
		||||
	    !strstr(dmevh->dso, "libdevmapper-event-lvm2vdo.so") &&
 | 
			
		||||
	    !strstr(dmevh->dso, "libdevmapper-event-lvm2snapshot.so") &&
 | 
			
		||||
	    !strstr(dmevh->dso, "libdevmapper-event-lvm2mirror.so") &&
 | 
			
		||||
	    !strstr(dmevh->dso, "libdevmapper-event-lvm2raid.so"))
 | 
			
		||||
@@ -670,7 +660,7 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh)
 | 
			
		||||
		ret = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(msg.data);
 | 
			
		||||
	dm_free(msg.data);
 | 
			
		||||
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
 | 
			
		||||
@@ -697,7 +687,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
 | 
			
		||||
		ret = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(msg.data);
 | 
			
		||||
	dm_free(msg.data);
 | 
			
		||||
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
 | 
			
		||||
@@ -709,11 +699,15 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
 | 
			
		||||
static char *_fetch_string(char **src, const int delimiter)
 | 
			
		||||
{
 | 
			
		||||
	char *p, *ret;
 | 
			
		||||
	size_t len = (p = strchr(*src, delimiter)) ?
 | 
			
		||||
		(size_t)(p - *src) : strlen(*src);
 | 
			
		||||
 | 
			
		||||
	if ((ret = strndup(*src, len)))
 | 
			
		||||
		*src += len + 1;
 | 
			
		||||
	if ((p = strchr(*src, delimiter)))
 | 
			
		||||
		*p = 0;
 | 
			
		||||
 | 
			
		||||
	if ((ret = dm_strdup(*src)))
 | 
			
		||||
		*src += strlen(ret) + 1;
 | 
			
		||||
 | 
			
		||||
	if (p)
 | 
			
		||||
		*p = delimiter;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -729,11 +723,11 @@ static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
 | 
			
		||||
	    (*dso_name = _fetch_string(&p, ' ')) &&
 | 
			
		||||
	    (*uuid = _fetch_string(&p, ' '))) {
 | 
			
		||||
		*evmask = atoi(p);
 | 
			
		||||
		free(id);
 | 
			
		||||
		dm_free(id);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(id);
 | 
			
		||||
	dm_free(id);
 | 
			
		||||
	return -ENOMEM;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -761,10 +755,11 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
 | 
			
		||||
	uuid = dm_task_get_uuid(dmt);
 | 
			
		||||
 | 
			
		||||
	/* FIXME Distinguish errors connecting to daemon */
 | 
			
		||||
	if ((ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
 | 
			
		||||
			    DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
 | 
			
		||||
			    &msg, dmevh->dso, uuid, dmevh->mask, 0))) {
 | 
			
		||||
	if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
 | 
			
		||||
		      DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
 | 
			
		||||
		      &msg, dmevh->dso, uuid, dmevh->mask, 0)) {
 | 
			
		||||
		log_debug("%s: device not registered.", dm_task_get_name(dmt));
 | 
			
		||||
		ret = -ENOENT;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -775,7 +770,7 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
	dmt = NULL;
 | 
			
		||||
 | 
			
		||||
	free(msg.data);
 | 
			
		||||
	dm_free(msg.data);
 | 
			
		||||
	msg.data = NULL;
 | 
			
		||||
 | 
			
		||||
	_dm_event_handler_clear_dev_info(dmevh);
 | 
			
		||||
@@ -784,7 +779,7 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(dmevh->uuid = strdup(reply_uuid))) {
 | 
			
		||||
	if (!(dmevh->uuid = dm_strdup(reply_uuid))) {
 | 
			
		||||
		ret = -ENOMEM;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
@@ -797,13 +792,13 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
 | 
			
		||||
	dm_event_handler_set_dso(dmevh, reply_dso);
 | 
			
		||||
	dm_event_handler_set_event_mask(dmevh, reply_mask);
 | 
			
		||||
 | 
			
		||||
	free(reply_dso);
 | 
			
		||||
	dm_free(reply_dso);
 | 
			
		||||
	reply_dso = NULL;
 | 
			
		||||
 | 
			
		||||
	free(reply_uuid);
 | 
			
		||||
	dm_free(reply_uuid);
 | 
			
		||||
	reply_uuid = NULL;
 | 
			
		||||
 | 
			
		||||
	if (!(dmevh->dev_name = strdup(dm_task_get_name(dmt)))) {
 | 
			
		||||
	if (!(dmevh->dev_name = dm_strdup(dm_task_get_name(dmt)))) {
 | 
			
		||||
		ret = -ENOMEM;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
@@ -821,9 +816,9 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
 | 
			
		||||
	return ret;
 | 
			
		||||
 | 
			
		||||
 fail:
 | 
			
		||||
	free(msg.data);
 | 
			
		||||
	free(reply_dso);
 | 
			
		||||
	free(reply_uuid);
 | 
			
		||||
	dm_free(msg.data);
 | 
			
		||||
	dm_free(reply_dso);
 | 
			
		||||
	dm_free(reply_uuid);
 | 
			
		||||
	_dm_event_handler_clear_dev_info(dmevh);
 | 
			
		||||
	if (dmt)
 | 
			
		||||
		dm_task_destroy(dmt);
 | 
			
		||||
@@ -870,38 +865,28 @@ void dm_event_log(const char *subsys, int level, const char *file,
 | 
			
		||||
		  int line, int dm_errno_or_class,
 | 
			
		||||
		  const char *format, va_list ap)
 | 
			
		||||
{
 | 
			
		||||
	static int _abort_on_internal_errors = -1;
 | 
			
		||||
	static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
	static time_t start = 0;
 | 
			
		||||
	const char *indent = "";
 | 
			
		||||
	FILE *stream = log_stderr(level) ? stderr : stdout;
 | 
			
		||||
	FILE *stream = stdout;
 | 
			
		||||
	int prio;
 | 
			
		||||
	time_t now;
 | 
			
		||||
	int log_with_debug = 0;
 | 
			
		||||
 | 
			
		||||
	if (subsys[0] == '#') {
 | 
			
		||||
		/* Subsystems starting with '#' are logged
 | 
			
		||||
		 * only when debugging is enabled. */
 | 
			
		||||
		log_with_debug++;
 | 
			
		||||
		subsys++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (log_level(level)) {
 | 
			
		||||
	switch (level & ~(_LOG_STDERR | _LOG_ONCE)) {
 | 
			
		||||
	case _LOG_DEBUG:
 | 
			
		||||
		/* Never shown without -ddd */
 | 
			
		||||
		if (_debug_level < 3)
 | 
			
		||||
			return;
 | 
			
		||||
		prio = LOG_DEBUG;
 | 
			
		||||
		indent = "      ";
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_INFO:
 | 
			
		||||
		if (log_with_debug && _debug_level < 2)
 | 
			
		||||
		if (_debug_level < 2)
 | 
			
		||||
			return;
 | 
			
		||||
		prio = LOG_INFO;
 | 
			
		||||
		indent = "    ";
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_NOTICE:
 | 
			
		||||
		if (log_with_debug && _debug_level < 1)
 | 
			
		||||
		if (_debug_level < 1)
 | 
			
		||||
			return;
 | 
			
		||||
		prio = LOG_NOTICE;
 | 
			
		||||
		indent = "  ";
 | 
			
		||||
@@ -927,13 +912,12 @@ void dm_event_log(const char *subsys, int level, const char *file,
 | 
			
		||||
		if (!start)
 | 
			
		||||
			start = now;
 | 
			
		||||
		now -= start;
 | 
			
		||||
		if (_debug_level)
 | 
			
		||||
			fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
 | 
			
		||||
				(int)now / 60, (int)now % 60,
 | 
			
		||||
				// TODO: Maybe use shorter ID
 | 
			
		||||
				// ((int)(pthread_self()) >> 6) & 0xffff,
 | 
			
		||||
				(int)pthread_self(), subsys,
 | 
			
		||||
				(_debug_level > 3) ? "" : indent);
 | 
			
		||||
		fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
 | 
			
		||||
			(int)now / 60, (int)now % 60,
 | 
			
		||||
			// TODO: Maybe use shorter ID
 | 
			
		||||
			// ((int)(pthread_self()) >> 6) & 0xffff,
 | 
			
		||||
			(int)pthread_self(), subsys,
 | 
			
		||||
			(_debug_level > 3) ? "" : indent);
 | 
			
		||||
		if (_debug_level > 3)
 | 
			
		||||
			fprintf(stream, "%28s:%4d %s", file, line, indent);
 | 
			
		||||
		vfprintf(stream, _(format), ap);
 | 
			
		||||
@@ -942,15 +926,6 @@ void dm_event_log(const char *subsys, int level, const char *file,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&_log_mutex);
 | 
			
		||||
 | 
			
		||||
	if (_abort_on_internal_errors < 0)
 | 
			
		||||
		/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
 | 
			
		||||
		_abort_on_internal_errors =
 | 
			
		||||
			strcmp(getenv("DM_ABORT_ON_INTERNAL_ERRORS") ? : "0", "0");
 | 
			
		||||
 | 
			
		||||
	if (_abort_on_internal_errors &&
 | 
			
		||||
	    !strncmp(format, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
 | 
			
		||||
		abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0				/* left out for now */
 | 
			
		||||
@@ -988,12 +963,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,15 +31,8 @@ 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")
 | 
			
		||||
DM_EVENT_LOG_FN("lvm")
 | 
			
		||||
 | 
			
		||||
static void _lvm2_print_log(int level, const char *file, int line,
 | 
			
		||||
			    int dm_errno_or_class, const char *msg)
 | 
			
		||||
@@ -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.");
 | 
			
		||||
@@ -129,11 +121,8 @@ int dmeventd_lvm2_run(const char *cmdline)
 | 
			
		||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
 | 
			
		||||
			  const char *cmd, const char *device)
 | 
			
		||||
{
 | 
			
		||||
	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.",
 | 
			
		||||
@@ -146,39 +135,6 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
 | 
			
		||||
	    (layer = strstr(lv, "_mlog")))
 | 
			
		||||
		*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;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
		}
 | 
			
		||||
		cmd = env;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
 | 
			
		||||
 | 
			
		||||
	dm_pool_free(mem, vg);
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -12,10 +12,10 @@
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib/misc/lib.h"
 | 
			
		||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
 | 
			
		||||
#include "daemons/dmeventd/libdevmapper-event.h"
 | 
			
		||||
#include "lib/activate/activate.h"
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "activate.h"	/* For TARGET_NAME* */
 | 
			
		||||
 | 
			
		||||
/* FIXME Reformat to 80 char lines. */
 | 
			
		||||
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
 | 
			
		||||
struct dso_state {
 | 
			
		||||
	struct dm_pool *mem;
 | 
			
		||||
	char cmd_lvscan[512];
 | 
			
		||||
	char cmd_lvconvert[512];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -72,10 +73,8 @@ static int _get_mirror_event(struct dso_state *state, char *params)
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	struct dm_status_mirror *ms;
 | 
			
		||||
 | 
			
		||||
	if (!dm_get_status_mirror(state->mem, params, &ms)) {
 | 
			
		||||
		log_error("Unable to parse mirror status string.");
 | 
			
		||||
		return ME_IGNORE;
 | 
			
		||||
	}
 | 
			
		||||
	if (!dm_get_status_mirror(state->mem, params, &ms))
 | 
			
		||||
		goto_out;
 | 
			
		||||
 | 
			
		||||
	/* Check for bad mirror devices */
 | 
			
		||||
	for (i = 0; i < ms->dev_count; ++i)
 | 
			
		||||
@@ -96,19 +95,27 @@ static int _get_mirror_event(struct dso_state *state, char *params)
 | 
			
		||||
	dm_pool_free(state->mem, ms);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	log_error("Unable to parse mirror status string.");
 | 
			
		||||
 | 
			
		||||
	return ME_IGNORE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _remove_failed_devices(const char *cmd_lvconvert, const char *device)
 | 
			
		||||
static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_run_with_lock(cmd_lvscan))
 | 
			
		||||
		log_info("Re-scan of mirrored device failed.");
 | 
			
		||||
 | 
			
		||||
	/* if repair goes OK, report success even if lvscan has failed */
 | 
			
		||||
	if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) {
 | 
			
		||||
		log_error("Repair of mirrored device %s failed.", device);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	r = dmeventd_lvm2_run_with_lock(cmd_lvconvert);
 | 
			
		||||
 | 
			
		||||
	log_info("Repair of mirrored device %s finished successfully.", device);
 | 
			
		||||
	log_info("Repair of mirrored device %s.",
 | 
			
		||||
		 (r) ? "finished successfully" : "failed");
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void process_event(struct dm_task *dmt,
 | 
			
		||||
@@ -146,7 +153,8 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
			break;
 | 
			
		||||
		case ME_FAILURE:
 | 
			
		||||
			log_error("Device failure in %s.", device);
 | 
			
		||||
			if (!_remove_failed_devices(state->cmd_lvconvert, device))
 | 
			
		||||
			if (!_remove_failed_devices(state->cmd_lvscan,
 | 
			
		||||
						    state->cmd_lvconvert))
 | 
			
		||||
				/* FIXME Why are all the error return codes unused? Get rid of them? */
 | 
			
		||||
				log_error("Failed to remove faulty devices in %s.",
 | 
			
		||||
					  device);
 | 
			
		||||
@@ -160,7 +168,7 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* FIXME Provide value then! */
 | 
			
		||||
			log_warn("WARNING: %s received unknown event.", device);
 | 
			
		||||
			log_info("Unknown event received.");
 | 
			
		||||
		}
 | 
			
		||||
	} while (next);
 | 
			
		||||
}
 | 
			
		||||
@@ -176,10 +184,17 @@ int register_device(const char *device,
 | 
			
		||||
	if (!dmeventd_lvm2_init_with_pool("mirror_state", state))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
        /* CANNOT use --config as this disables cached content */
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
 | 
			
		||||
				   "lvconvert --repair --use-policies", device))
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan),
 | 
			
		||||
				   "lvscan --cache", device)) {
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
 | 
			
		||||
				   "lvconvert --repair --use-policies", device)) {
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
@@ -189,9 +204,6 @@ int register_device(const char *device,
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor mirror %s.", device);
 | 
			
		||||
 | 
			
		||||
	if (state)
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,8 @@ srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
LIBS += -ldevmapper-event-lvm2
 | 
			
		||||
 | 
			
		||||
SOURCES = dmeventd_raid.c
 | 
			
		||||
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2005-2016 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -12,16 +12,17 @@
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib/misc/lib.h"
 | 
			
		||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
 | 
			
		||||
#include "daemons/dmeventd/libdevmapper-event.h"
 | 
			
		||||
#include "lib/config/defaults.h"
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
 | 
			
		||||
/* Hold enough elements for the mximum number of RAID images */
 | 
			
		||||
#define	RAID_DEVS_ELEMS	((DEFAULT_RAID_MAX_IMAGES + 63) / 64)
 | 
			
		||||
 | 
			
		||||
struct dso_state {
 | 
			
		||||
	struct dm_pool *mem;
 | 
			
		||||
	char cmd_lvscan[512];
 | 
			
		||||
	char cmd_lvconvert[512];
 | 
			
		||||
	uint64_t raid_devs[RAID_DEVS_ELEMS];
 | 
			
		||||
	int failed;
 | 
			
		||||
@@ -37,7 +38,6 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
 | 
			
		||||
	struct dm_status_raid *status;
 | 
			
		||||
	const char *d;
 | 
			
		||||
	int dead = 0, r = 1;
 | 
			
		||||
	uint32_t dev;
 | 
			
		||||
 | 
			
		||||
	if (!dm_get_status_raid(state->mem, params, &status)) {
 | 
			
		||||
		log_error("Failed to process status line for %s.", device);
 | 
			
		||||
@@ -46,63 +46,41 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
 | 
			
		||||
 | 
			
		||||
	d = status->dev_health;
 | 
			
		||||
	while ((d = strchr(d, 'D'))) {
 | 
			
		||||
		dev = (uint32_t)(d - status->dev_health);
 | 
			
		||||
		uint32_t dev = (uint32_t)(d - status->dev_health);
 | 
			
		||||
 | 
			
		||||
		if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64)))) {
 | 
			
		||||
			state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64));
 | 
			
		||||
			log_warn("WARNING: Device #%u of %s array, %s, has failed.",
 | 
			
		||||
				 dev, status->raid_type, device);
 | 
			
		||||
		}
 | 
			
		||||
		if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64))))
 | 
			
		||||
			log_error("Device #%u of %s array, %s, has failed.",
 | 
			
		||||
				  dev, status->raid_type, device);
 | 
			
		||||
 | 
			
		||||
		state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64));
 | 
			
		||||
		d++;
 | 
			
		||||
		dead = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * if we are converting from non-RAID to RAID (e.g. linear -> raid1)
 | 
			
		||||
	 * and too many original devices die, such that we cannot continue
 | 
			
		||||
	 * the "recover" operation, the sync action will go to "idle", the
 | 
			
		||||
	 * unsynced devs will remain at 'a', and the original devices will
 | 
			
		||||
	 * NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived.
 | 
			
		||||
	 *
 | 
			
		||||
	 * This is simply the way the kernel works...
 | 
			
		||||
	 */
 | 
			
		||||
	if (!strcmp(status->sync_action, "idle") &&
 | 
			
		||||
	    (status->dev_health[0] == 'a') &&
 | 
			
		||||
	    (status->insync_regions < status->total_regions)) {
 | 
			
		||||
		log_error("Primary sources for new RAID, %s, have failed.",
 | 
			
		||||
			  device);
 | 
			
		||||
		dead = 1; /* run it through LVM repair */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dead) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Use the first event to run a repair ignoring any 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) {
 | 
			
		||||
		if (status->insync_regions < status->total_regions) {
 | 
			
		||||
			if (!state->warned)
 | 
			
		||||
				log_warn("WARNING: waiting for resynchronization to finish "
 | 
			
		||||
					 "before initiating repair on RAID device %s", device);
 | 
			
		||||
 | 
			
		||||
			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. */
 | 
			
		||||
			goto out; /* Not yet done syncing with accessible devices */
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (state->failed)
 | 
			
		||||
			goto out; /* already reported */
 | 
			
		||||
 | 
			
		||||
		state->failed = 1;
 | 
			
		||||
		if (!dmeventd_lvm2_run_with_lock(state->cmd_lvscan))
 | 
			
		||||
			log_warn("WARNING: Re-scan of RAID device %s failed.", device);
 | 
			
		||||
 | 
			
		||||
		/* if repair goes OK, report success even if lvscan has failed */
 | 
			
		||||
		if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
 | 
			
		||||
			log_error("Repair of RAID device %s failed.", device);
 | 
			
		||||
			log_info("Repair of RAID device %s failed.", device);
 | 
			
		||||
			r = 0;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		state->failed = 0;
 | 
			
		||||
		if (status->insync_regions == status->total_regions)
 | 
			
		||||
			memset(&state->raid_devs, 0, sizeof(state->raid_devs));
 | 
			
		||||
		log_info("%s array, %s, is %s in-sync.",
 | 
			
		||||
			 status->raid_type, device,
 | 
			
		||||
			 (status->insync_regions == status->total_regions) ? "now" : "not");
 | 
			
		||||
@@ -155,9 +133,14 @@ int register_device(const char *device,
 | 
			
		||||
	if (!dmeventd_lvm2_init_with_pool("raid_state", state))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
 | 
			
		||||
				   "lvconvert --repair --use-policies", device))
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan),
 | 
			
		||||
				   "lvscan --cache", device) ||
 | 
			
		||||
	    !dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
 | 
			
		||||
				   "lvconvert --config devices{ignore_suspended_devices=1} "
 | 
			
		||||
				   "--repair --use-policies", device)) {
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
@@ -167,9 +150,6 @@ int register_device(const char *device,
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor RAID %s.", device);
 | 
			
		||||
 | 
			
		||||
	if (state)
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,8 @@ srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
LIBS += -ldevmapper-event-lvm2
 | 
			
		||||
 | 
			
		||||
SOURCES = dmeventd_snapshot.c
 | 
			
		||||
 | 
			
		||||
@@ -26,6 +26,8 @@ LIB_VERSION = $(LIB_VERSION_LVM)
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
LIBS += -ldevmapper-event-lvm2 -ldevmapper
 | 
			
		||||
 | 
			
		||||
install_lvm2: install_dm_plugin
 | 
			
		||||
 | 
			
		||||
install: install_lvm2
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,9 @@
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib/misc/lib.h"
 | 
			
		||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
 | 
			
		||||
#include "daemons/dmeventd/libdevmapper-event.h"
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/sysmacros.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
@@ -175,7 +175,6 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
	const char *device = dm_task_get_name(dmt);
 | 
			
		||||
	int percent;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* No longer monitoring, waiting for remove */
 | 
			
		||||
	if (!state->percent_check)
 | 
			
		||||
@@ -206,8 +205,7 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
		/* Maybe configurable ? */
 | 
			
		||||
		_remove(dm_task_get_uuid(dmt));
 | 
			
		||||
#endif
 | 
			
		||||
		if ((ret = pthread_kill(pthread_self(), SIGALRM)) && (ret != ESRCH))
 | 
			
		||||
			log_sys_error("pthread_kill", "self");
 | 
			
		||||
		pthread_kill(pthread_self(), SIGALRM);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -215,8 +213,7 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
		/* TODO eventually recognize earlier when room is enough */
 | 
			
		||||
		log_info("Dropping monitoring of fully provisioned snapshot %s.",
 | 
			
		||||
			 device);
 | 
			
		||||
		if ((ret = pthread_kill(pthread_self(), SIGALRM)) && (ret != ESRCH))
 | 
			
		||||
			log_sys_error("pthread_kill", "self");
 | 
			
		||||
		pthread_kill(pthread_self(), SIGALRM);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -234,7 +231,7 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
 | 
			
		||||
		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
 | 
			
		||||
			log_warn("WARNING: Snapshot %s is now %.2f%% full.",
 | 
			
		||||
				 device, dm_percent_to_round_float(percent, 2));
 | 
			
		||||
				 device, dm_percent_to_float(percent));
 | 
			
		||||
 | 
			
		||||
		/* Try to extend the snapshot, in accord with user-set policies */
 | 
			
		||||
		if (!_extend(state->cmd_lvextend))
 | 
			
		||||
@@ -257,8 +254,10 @@ int register_device(const char *device,
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend,
 | 
			
		||||
				   sizeof(state->cmd_lvextend),
 | 
			
		||||
				   "lvextend --use-policies", device))
 | 
			
		||||
				   "lvextend --use-policies", device)) {
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->percent_check = CHECK_MINIMUM;
 | 
			
		||||
	*user = state;
 | 
			
		||||
@@ -269,9 +268,6 @@ int register_device(const char *device,
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor snapshot %s.", device);
 | 
			
		||||
 | 
			
		||||
	if (state)
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,8 @@ srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
LIBS += -ldevmapper-event-lvm2
 | 
			
		||||
 | 
			
		||||
SOURCES = dmeventd_thin.c
 | 
			
		||||
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -12,16 +12,17 @@
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib/misc/lib.h"
 | 
			
		||||
#include "daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h"
 | 
			
		||||
#include "daemons/dmeventd/libdevmapper-event.h"
 | 
			
		||||
#include "lib.h"	/* using here lvm log */
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
/* TODO - move this mountinfo code into library to be reusable */
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#  include "libdm/misc/kdev_t.h"
 | 
			
		||||
#  include "kdev_t.h"
 | 
			
		||||
#else
 | 
			
		||||
#  define MAJOR(x) major((x))
 | 
			
		||||
#  define MINOR(x) minor((x))
 | 
			
		||||
@@ -39,118 +40,277 @@
 | 
			
		||||
 | 
			
		||||
#define UMOUNT_COMMAND "/bin/umount"
 | 
			
		||||
 | 
			
		||||
#define MAX_FAILS	(256)  /* ~42 mins between cmd call retry with 10s delay */
 | 
			
		||||
#define MAX_FAILS	(10)
 | 
			
		||||
 | 
			
		||||
#define THIN_DEBUG 0
 | 
			
		||||
 | 
			
		||||
struct dso_state {
 | 
			
		||||
	struct dm_pool *mem;
 | 
			
		||||
	int metadata_percent_check;
 | 
			
		||||
	int metadata_percent;
 | 
			
		||||
	int data_percent_check;
 | 
			
		||||
	int data_percent;
 | 
			
		||||
	uint64_t known_metadata_size;
 | 
			
		||||
	uint64_t known_data_size;
 | 
			
		||||
	unsigned fails;
 | 
			
		||||
	unsigned max_fails;
 | 
			
		||||
	int restore_sigset;
 | 
			
		||||
	sigset_t old_sigset;
 | 
			
		||||
	pid_t pid;
 | 
			
		||||
	char *argv[3];
 | 
			
		||||
	char *cmd_str;
 | 
			
		||||
	char cmd_str[1024];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DM_EVENT_LOG_FN("thin")
 | 
			
		||||
 | 
			
		||||
static int _run_command(struct dso_state *state)
 | 
			
		||||
#define UUID_PREFIX "LVM-"
 | 
			
		||||
 | 
			
		||||
/* Figure out device UUID has LVM- prefix and is OPEN */
 | 
			
		||||
static int _has_unmountable_prefix(int major, int minor)
 | 
			
		||||
{
 | 
			
		||||
	char val[16];
 | 
			
		||||
	int i;
 | 
			
		||||
	struct dm_task *dmt;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	const char *uuid;
 | 
			
		||||
	int r = 0;
 | 
			
		||||
 | 
			
		||||
	/* 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 (!(dmt = dm_task_create(DM_DEVICE_INFO)))
 | 
			
		||||
		return_0;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* For an error event it's for a user to check status and decide */
 | 
			
		||||
		log_debug("Error event processing.");
 | 
			
		||||
	if (!dm_task_set_major_minor(dmt, major, minor, 1))
 | 
			
		||||
		goto_out;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_no_flush(dmt))
 | 
			
		||||
		stack;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_run(dmt))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_get_info(dmt, &info))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!info.exists || !info.open_count)
 | 
			
		||||
		goto out; /* Not open -> not mounted */
 | 
			
		||||
 | 
			
		||||
	if (!(uuid = dm_task_get_uuid(dmt)))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* Check it's public mountable LV
 | 
			
		||||
	 * has prefix  LVM-  and UUID size is 68 chars */
 | 
			
		||||
	if (memcmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) ||
 | 
			
		||||
	    strlen(uuid) != 68)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
#if THIN_DEBUG
 | 
			
		||||
	log_debug("Found logical volume %s (%u:%u).", uuid, major, minor);
 | 
			
		||||
#endif
 | 
			
		||||
	r = 1;
 | 
			
		||||
out:
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get dependencies for device, and try to find matching device */
 | 
			
		||||
static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_task *dmt;
 | 
			
		||||
	const struct dm_deps *deps;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	int major, minor;
 | 
			
		||||
	int r = 0;
 | 
			
		||||
 | 
			
		||||
	if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_set_name(dmt, name))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_no_open_count(dmt))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_run(dmt))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_get_info(dmt, &info))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!(deps = dm_task_get_deps(dmt)))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!info.exists || deps->count != 1)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	major = (int) MAJOR(deps->device[0]);
 | 
			
		||||
	minor = (int) MINOR(deps->device[0]);
 | 
			
		||||
	if ((major != tp_major) || (minor != tp_minor))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	*dev_minor = info.minor;
 | 
			
		||||
 | 
			
		||||
	if (!_has_unmountable_prefix(major, info.minor))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
#if THIN_DEBUG
 | 
			
		||||
	{
 | 
			
		||||
		char dev_name[PATH_MAX];
 | 
			
		||||
		if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name)))
 | 
			
		||||
			log_debug("Found %s (%u:%u) depends on %s.",
 | 
			
		||||
				  name, major, *dev_minor, dev_name);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	r = 1;
 | 
			
		||||
out:
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get all active devices */
 | 
			
		||||
static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_task *dmt;
 | 
			
		||||
	struct dm_names *names;
 | 
			
		||||
	unsigned next = 0;
 | 
			
		||||
	int minor, r = 1;
 | 
			
		||||
 | 
			
		||||
	if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_run(dmt)) {
 | 
			
		||||
		r = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log_verbose("Executing command: %s", state->cmd_str);
 | 
			
		||||
	if (!(names = dm_task_get_names(dmt))) {
 | 
			
		||||
		r = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	if (!names->dev)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		names = (struct dm_names *)((char *) names + next);
 | 
			
		||||
		if (_has_deps(names->name, tp_major, tp_minor, &minor))
 | 
			
		||||
			dm_bit_set(bs, minor);
 | 
			
		||||
		next = names->next;
 | 
			
		||||
	} while (next);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _run(const char *cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int argc = 1; /* for argv[0], i.e. cmd */
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	const char **argv;
 | 
			
		||||
	pid_t pid = fork();
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	if (pid == 0) { /* child */
 | 
			
		||||
		va_start(ap, cmd);
 | 
			
		||||
		while (va_arg(ap, const char *))
 | 
			
		||||
			++argc;
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
 | 
			
		||||
		/* + 1 for the terminating NULL */
 | 
			
		||||
		argv = alloca(sizeof(const char *) * (argc + 1));
 | 
			
		||||
 | 
			
		||||
		argv[0] = cmd;
 | 
			
		||||
		va_start(ap, cmd);
 | 
			
		||||
		while ((argv[++i] = va_arg(ap, const char *)));
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
 | 
			
		||||
		execvp(cmd, (char **)argv);
 | 
			
		||||
		log_sys_error("exec", cmd);
 | 
			
		||||
		exit(127);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pid > 0) { /* parent */
 | 
			
		||||
		if (waitpid(pid, &status, 0) != pid)
 | 
			
		||||
			return 0; /* waitpid failed */
 | 
			
		||||
		if (!WIFEXITED(status) || WEXITSTATUS(status))
 | 
			
		||||
			return 0; /* the child failed */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pid < 0)
 | 
			
		||||
		return 0; /* fork failed */
 | 
			
		||||
 | 
			
		||||
	return 1; /* all good */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mountinfo_s {
 | 
			
		||||
	const char *device;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	dm_bitset_t minors; /* Bitset for active thin pool minors */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _umount_device(char *buffer, unsigned major, unsigned minor,
 | 
			
		||||
			  char *target, void *cb_data)
 | 
			
		||||
{
 | 
			
		||||
	struct mountinfo_s *data = cb_data;
 | 
			
		||||
	char *words[10];
 | 
			
		||||
 | 
			
		||||
	if ((major == data->info.major) && dm_bit(data->minors, minor)) {
 | 
			
		||||
		if (dm_split_words(buffer, DM_ARRAY_SIZE(words), 0, words) < DM_ARRAY_SIZE(words))
 | 
			
		||||
			words[9] = NULL; /* just don't show device name */
 | 
			
		||||
		log_info("Unmounting thin %s (%d:%d) of thin pool %s (%u:%u) from mount point \"%s\".",
 | 
			
		||||
			 words[9] ? : "", major, minor, data->device,
 | 
			
		||||
			 data->info.major, data->info.minor,
 | 
			
		||||
			 target);
 | 
			
		||||
		if (!_run(UMOUNT_COMMAND, "-fl", target, NULL))
 | 
			
		||||
			log_error("Failed to lazy umount thin %s (%d:%d) from %s: %s.",
 | 
			
		||||
				  words[9], major, minor, target, strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Find all thin pool LV users and try to umount them.
 | 
			
		||||
 * TODO: work with read-only thin pool support
 | 
			
		||||
 */
 | 
			
		||||
static void _umount(struct dm_task *dmt)
 | 
			
		||||
{
 | 
			
		||||
	/* TODO: Convert to use hash to reduce memory usage */
 | 
			
		||||
	static const size_t MINORS = (1U << 20); /* 20 bit */
 | 
			
		||||
	struct mountinfo_s data = { NULL };
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_get_info(dmt, &data.info))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	data.device = dm_task_get_name(dmt);
 | 
			
		||||
 | 
			
		||||
	if (!(data.minors = dm_bitset_create(NULL, MINORS))) {
 | 
			
		||||
		log_error("Failed to allocate bitset. Not unmounting %s.", data.device);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_find_all_devs(data.minors, data.info.major, data.info.minor)) {
 | 
			
		||||
		log_error("Failed to detect mounted volumes for %s.", data.device);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!dm_mountinfo_read(_umount_device, &data)) {
 | 
			
		||||
		log_error("Could not parse mountinfo file.");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (data.minors)
 | 
			
		||||
		dm_bitset_destroy(data.minors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
 | 
			
		||||
{
 | 
			
		||||
#if THIN_DEBUG
 | 
			
		||||
	log_debug("dmeventd executes: %s.", state->cmd_str);
 | 
			
		||||
	log_info("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;
 | 
			
		||||
		log_error("Failed to extend thin pool %s.",
 | 
			
		||||
			  dm_task_get_name(dmt));
 | 
			
		||||
		state->fails++;
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -159,6 +319,7 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
		   void **user)
 | 
			
		||||
{
 | 
			
		||||
	const char *device = dm_task_get_name(dmt);
 | 
			
		||||
	int percent;
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	struct dm_status_thin_pool *tps = NULL;
 | 
			
		||||
	void *next = NULL;
 | 
			
		||||
@@ -166,48 +327,25 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
	char *target_type = NULL;
 | 
			
		||||
	char *params;
 | 
			
		||||
	int needs_policy = 0;
 | 
			
		||||
	struct dm_task *new_dmt = NULL;
 | 
			
		||||
	int needs_umount = 0;
 | 
			
		||||
 | 
			
		||||
#if THIN_DEBUG
 | 
			
		||||
	log_debug("Watch for tp-data:%.2f%%  tp-metadata:%.2f%%.",
 | 
			
		||||
		  dm_percent_to_round_float(state->data_percent_check, 2),
 | 
			
		||||
		  dm_percent_to_round_float(state->metadata_percent_check, 2));
 | 
			
		||||
		  dm_percent_to_float(state->data_percent_check),
 | 
			
		||||
		  dm_percent_to_float(state->metadata_percent_check));
 | 
			
		||||
#endif
 | 
			
		||||
	if (!_wait_for_pid(state)) {
 | 
			
		||||
		log_warn("WARNING: Skipping event, child %d is still running (%s).",
 | 
			
		||||
			 state->pid, state->cmd_str);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	/* No longer monitoring, waiting for remove */
 | 
			
		||||
	if (!state->meta_percent_check && !state->data_percent_check)
 | 
			
		||||
		return;
 | 
			
		||||
#endif
 | 
			
		||||
	if (event & DM_EVENT_DEVICE_ERROR) {
 | 
			
		||||
		/* Error -> no need to check and do instant resize */
 | 
			
		||||
		state->data_percent = state->metadata_percent = 0;
 | 
			
		||||
		if (_use_policy(dmt, state))
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		stack;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Rather update oldish status
 | 
			
		||||
		 * since after 'command' processing
 | 
			
		||||
		 * percentage info could have changed a lot.
 | 
			
		||||
		 * If we would get above UMOUNT_THRESH
 | 
			
		||||
		 * we would wait for next sigalarm.
 | 
			
		||||
		 */
 | 
			
		||||
		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);
 | 
			
		||||
@@ -219,6 +357,7 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
 | 
			
		||||
	if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
 | 
			
		||||
		log_error("Failed to parse status.");
 | 
			
		||||
		needs_umount = 1;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -233,110 +372,67 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
	if (state->known_metadata_size != tps->total_metadata_blocks) {
 | 
			
		||||
		state->metadata_percent_check = CHECK_MINIMUM;
 | 
			
		||||
		state->known_metadata_size = tps->total_metadata_blocks;
 | 
			
		||||
		state->fails = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (state->known_data_size != tps->total_data_blocks) {
 | 
			
		||||
		state->data_percent_check = CHECK_MINIMUM;
 | 
			
		||||
		state->known_data_size = tps->total_data_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%
 | 
			
		||||
	 */
 | 
			
		||||
	state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
 | 
			
		||||
	if ((state->metadata_percent > WARNING_THRESH) &&
 | 
			
		||||
	    (state->metadata_percent > state->metadata_percent_check))
 | 
			
		||||
		log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
 | 
			
		||||
			 device, dm_percent_to_round_float(state->metadata_percent, 2));
 | 
			
		||||
	if (state->metadata_percent > CHECK_MINIMUM) {
 | 
			
		||||
		/* Run action when usage raised more than CHECK_STEP since the last time */
 | 
			
		||||
		if (state->metadata_percent > state->metadata_percent_check)
 | 
			
		||||
			needs_policy = 1;
 | 
			
		||||
		state->metadata_percent_check = (state->metadata_percent / CHECK_STEP + 1) * CHECK_STEP;
 | 
			
		||||
		if (state->metadata_percent_check == DM_PERCENT_100)
 | 
			
		||||
			state->metadata_percent_check--; /* Can't get bigger then 100% */
 | 
			
		||||
	} else
 | 
			
		||||
		state->metadata_percent_check = CHECK_MINIMUM;
 | 
			
		||||
	percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
 | 
			
		||||
	if (percent >= state->metadata_percent_check) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Usage has raised more than CHECK_STEP since the last
 | 
			
		||||
		 * time. Run actions.
 | 
			
		||||
		 */
 | 
			
		||||
		state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
 | 
			
		||||
 | 
			
		||||
	state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
 | 
			
		||||
	if ((state->data_percent > WARNING_THRESH) &&
 | 
			
		||||
	    (state->data_percent > state->data_percent_check))
 | 
			
		||||
		log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
 | 
			
		||||
			 device, dm_percent_to_round_float(state->data_percent, 2));
 | 
			
		||||
	if (state->data_percent > CHECK_MINIMUM) {
 | 
			
		||||
		/* Run action when usage raised more than CHECK_STEP since the last time */
 | 
			
		||||
		if (state->data_percent > state->data_percent_check)
 | 
			
		||||
			needs_policy = 1;
 | 
			
		||||
		state->data_percent_check = (state->data_percent / CHECK_STEP + 1) * CHECK_STEP;
 | 
			
		||||
		if (state->data_percent_check == DM_PERCENT_100)
 | 
			
		||||
			state->data_percent_check--; /* Can't get bigger then 100% */
 | 
			
		||||
	} else
 | 
			
		||||
		state->data_percent_check = CHECK_MINIMUM;
 | 
			
		||||
		/* FIXME: extension of metadata needs to be written! */
 | 
			
		||||
		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
 | 
			
		||||
			log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
 | 
			
		||||
				 device, dm_percent_to_float(percent));
 | 
			
		||||
		needs_policy = 1;
 | 
			
		||||
 | 
			
		||||
	/* 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 (percent >= UMOUNT_THRESH)
 | 
			
		||||
			needs_umount = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (needs_policy)
 | 
			
		||||
		_use_policy(dmt, state);
 | 
			
		||||
	percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
 | 
			
		||||
	if (percent >= state->data_percent_check) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Usage has raised more than CHECK_STEP since
 | 
			
		||||
		 * the last time. Run actions.
 | 
			
		||||
		 */
 | 
			
		||||
		state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
 | 
			
		||||
 | 
			
		||||
		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
 | 
			
		||||
			log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
 | 
			
		||||
				 device, dm_percent_to_float(percent));
 | 
			
		||||
		needs_policy = 1;
 | 
			
		||||
 | 
			
		||||
		if (percent >= UMOUNT_THRESH)
 | 
			
		||||
			needs_umount = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (needs_policy &&
 | 
			
		||||
	    _use_policy(dmt, state))
 | 
			
		||||
		needs_umount = 0; /* No umount when command was successful */
 | 
			
		||||
out:
 | 
			
		||||
	if (needs_umount) {
 | 
			
		||||
		_umount(dmt);
 | 
			
		||||
		/* Until something changes, do not retry any more actions */
 | 
			
		||||
		state->data_percent_check = state->metadata_percent_check = (DM_PERCENT_1 * 101);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tps)
 | 
			
		||||
		dm_pool_free(state->mem, tps);
 | 
			
		||||
 | 
			
		||||
	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.");
 | 
			
		||||
	if (state->fails >= MAX_FAILS) {
 | 
			
		||||
		log_warn("WARNING: Dropping monitoring of %s. "
 | 
			
		||||
			 "lvm2 command fails too often (%u times in row).",
 | 
			
		||||
			 device, state->fails);
 | 
			
		||||
		pthread_kill(pthread_self(), SIGALRM);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int register_device(const char *device,
 | 
			
		||||
@@ -346,56 +442,28 @@ int register_device(const char *device,
 | 
			
		||||
		    void **user)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state;
 | 
			
		||||
	char *str;
 | 
			
		||||
	char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str),
 | 
			
		||||
				   "_dmeventd_thin_command", device))
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_str,
 | 
			
		||||
				   sizeof(state->cmd_str),
 | 
			
		||||
				   "lvextend --use-policies",
 | 
			
		||||
				   device)) {
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strncmp(cmd_str, "lvm ", 4) == 0) {
 | 
			
		||||
		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
 | 
			
		||||
			log_error("Failed to copy lvm command.");
 | 
			
		||||
			goto bad;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (cmd_str[0] == '/') {
 | 
			
		||||
		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
 | 
			
		||||
			log_error("Failed to copy thin command.");
 | 
			
		||||
			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 /* Unuspported command format */
 | 
			
		||||
		goto inval;
 | 
			
		||||
 | 
			
		||||
	state->pid = -1;
 | 
			
		||||
	state->metadata_percent_check = CHECK_MINIMUM;
 | 
			
		||||
	state->data_percent_check = CHECK_MINIMUM;
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
	log_info("Monitoring thin pool %s.", device);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
inval:
 | 
			
		||||
	log_error("Invalid command for monitoring: %s.", cmd_str);
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor thin pool %s.", device);
 | 
			
		||||
 | 
			
		||||
	if (state)
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -406,28 +474,6 @@ int unregister_device(const char *device,
 | 
			
		||||
		      void **user)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	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 thin pool %s.", device);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
process_event
 | 
			
		||||
register_device
 | 
			
		||||
unregister_device
 | 
			
		||||
@@ -1,413 +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
 | 
			
		||||
 */
 | 
			
		||||
/* coverity[unnecessary_header] used for parsing */
 | 
			
		||||
#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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
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,51 +23,45 @@ 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 \
 | 
			
		||||
	refresh.py \
 | 
			
		||||
	request.py \
 | 
			
		||||
	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)
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ class AutomatedProperties(dbus.service.Object):
 | 
			
		||||
		props = {}
 | 
			
		||||
 | 
			
		||||
		for i in self.interface():
 | 
			
		||||
			props[i] = AutomatedProperties._get_all_prop(self, i)
 | 
			
		||||
			props[i] = self.GetAll(i)
 | 
			
		||||
 | 
			
		||||
		return self._ap_o_path, props
 | 
			
		||||
 | 
			
		||||
@@ -65,52 +65,31 @@ class AutomatedProperties(dbus.service.Object):
 | 
			
		||||
 | 
			
		||||
		return self._ap_interface
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _get_prop(obj, interface_name, property_name):
 | 
			
		||||
		value = getattr(obj, property_name)
 | 
			
		||||
	# Properties
 | 
			
		||||
	# noinspection PyUnusedLocal
 | 
			
		||||
	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
 | 
			
		||||
							in_signature='ss', out_signature='v')
 | 
			
		||||
	def Get(self, interface_name, property_name):
 | 
			
		||||
		value = getattr(self, property_name)
 | 
			
		||||
		# Note: If we get an exception in this handler we won't know about it,
 | 
			
		||||
		# only the side effect of no returned value!
 | 
			
		||||
		log_debug('Get (%s), type (%s), value(%s)' %
 | 
			
		||||
					(property_name, str(type(value)), str(value)))
 | 
			
		||||
		return value
 | 
			
		||||
 | 
			
		||||
	# Properties
 | 
			
		||||
	# noinspection PyUnusedLocal
 | 
			
		||||
	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
 | 
			
		||||
							in_signature='ss', out_signature='v',
 | 
			
		||||
							async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Get(self, interface_name, property_name, cb, cbe):
 | 
			
		||||
		# Note: If we get an exception in this handler we won't know about it,
 | 
			
		||||
		# only the side effect of no returned value!
 | 
			
		||||
		r = cfg.create_request_entry(
 | 
			
		||||
			-1, AutomatedProperties._get_prop,
 | 
			
		||||
			(self, interface_name, property_name),
 | 
			
		||||
			cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _get_all_prop(obj, interface_name):
 | 
			
		||||
		if interface_name in obj.interface(True):
 | 
			
		||||
							in_signature='s', out_signature='a{sv}')
 | 
			
		||||
	def GetAll(self, interface_name):
 | 
			
		||||
		if interface_name in self.interface(True):
 | 
			
		||||
			# Using introspection, lets build this dynamically
 | 
			
		||||
			properties = get_properties(obj)
 | 
			
		||||
			properties = get_properties(self)
 | 
			
		||||
			if interface_name in properties:
 | 
			
		||||
				return properties[interface_name][1]
 | 
			
		||||
			return {}
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			obj._ap_interface,
 | 
			
		||||
			self._ap_interface,
 | 
			
		||||
			'The object %s does not implement the %s interface'
 | 
			
		||||
			% (obj.__class__, interface_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
 | 
			
		||||
							in_signature='s', out_signature='a{sv}',
 | 
			
		||||
							async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def GetAll(self, interface_name, cb, cbe):
 | 
			
		||||
		r = cfg.create_request_entry(
 | 
			
		||||
			-1, AutomatedProperties._get_all_prop,
 | 
			
		||||
			(self, interface_name),
 | 
			
		||||
			cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
			% (self.__class__, interface_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
 | 
			
		||||
							in_signature='ssv')
 | 
			
		||||
@@ -155,17 +134,16 @@ 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:
 | 
			
		||||
			search = search_key
 | 
			
		||||
 | 
			
		||||
		# Either we have the new object state or we need to go fetch it
 | 
			
		||||
		if object_state:
 | 
			
		||||
			new_state = object_state
 | 
			
		||||
		else:
 | 
			
		||||
			if search_key:
 | 
			
		||||
				search = search_key
 | 
			
		||||
			else:
 | 
			
		||||
				search = self.lvm_id
 | 
			
		||||
 | 
			
		||||
			new_state = self._ap_search_method([search])[0]
 | 
			
		||||
			assert isinstance(new_state, State)
 | 
			
		||||
 | 
			
		||||
@@ -181,7 +159,10 @@ class AutomatedProperties(dbus.service.Object):
 | 
			
		||||
			cfg.om.lookup_update(self, new_id[0], new_id[1])
 | 
			
		||||
 | 
			
		||||
		# Grab the properties values, then replace the state of the object
 | 
			
		||||
		# and retrieve the new values.
 | 
			
		||||
		# and retrieve the new values
 | 
			
		||||
		# TODO: We need to add locking to prevent concurrent access to the
 | 
			
		||||
		# properties so that a client is not accessing while we are
 | 
			
		||||
		# replacing.
 | 
			
		||||
		o_prop = get_properties(self)
 | 
			
		||||
		self.state = new_state
 | 
			
		||||
		n_prop = get_properties(self)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,16 +7,18 @@
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
import threading
 | 
			
		||||
import subprocess
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .cmdhandler import options_to_cli_args, LvmExecutionMeta, call_lvm
 | 
			
		||||
import dbus
 | 
			
		||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\
 | 
			
		||||
					mt_async_call
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
import threading
 | 
			
		||||
import time
 | 
			
		||||
from .cmdhandler import options_to_cli_args
 | 
			
		||||
import dbus
 | 
			
		||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug
 | 
			
		||||
import traceback
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
_rlock = threading.RLock()
 | 
			
		||||
_thread_list = list()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_move_lv_cmd(move_options, lv_full_name,
 | 
			
		||||
@@ -40,50 +42,15 @@ def lv_merge_cmd(merge_options, lv_full_name):
 | 
			
		||||
	return cmd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _load_wrapper(ignored):
 | 
			
		||||
	cfg.load()
 | 
			
		||||
def _move_merge(interface_name, cmd, job_state):
 | 
			
		||||
	add(cmd, job_state)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _move_callback(job_state, line_str):
 | 
			
		||||
	try:
 | 
			
		||||
		if line_str.count(':') == 2:
 | 
			
		||||
			(device, ignore, percentage) = line_str.split(':')
 | 
			
		||||
 | 
			
		||||
			job_state.Percent = int(round(
 | 
			
		||||
				float(percentage.strip()[:-1]), 1))
 | 
			
		||||
 | 
			
		||||
			# While the move is in progress we need to periodically update
 | 
			
		||||
			# the state to reflect where everything is at.  we will do this
 | 
			
		||||
			# by scheduling the load to occur in the main work queue.
 | 
			
		||||
			r = RequestEntry(
 | 
			
		||||
				-1, _load_wrapper, ("_move_callback: load",), None, None, False)
 | 
			
		||||
			cfg.worker_q.put(r)
 | 
			
		||||
	except ValueError:
 | 
			
		||||
		log_error("Trying to parse percentage which failed for %s" % line_str)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _move_merge(interface_name, command, job_state):
 | 
			
		||||
	# We need to execute these command stand alone by forking & exec'ing
 | 
			
		||||
	# the command always as we will be getting periodic output from them on
 | 
			
		||||
	# the status of the long running operation.
 | 
			
		||||
 | 
			
		||||
	meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None)
 | 
			
		||||
	cfg.blackbox.add(meta)
 | 
			
		||||
 | 
			
		||||
	ec, stdout, stderr = call_lvm(command, line_cb=_move_callback,
 | 
			
		||||
									cb_data=job_state)
 | 
			
		||||
 | 
			
		||||
	with meta.lock:
 | 
			
		||||
		meta.ended = time.time()
 | 
			
		||||
		meta.ec = ec
 | 
			
		||||
		meta.stderr_txt = stderr
 | 
			
		||||
 | 
			
		||||
	if ec == 0:
 | 
			
		||||
		job_state.Percent = 100
 | 
			
		||||
	else:
 | 
			
		||||
	done = job_state.Wait(-1)
 | 
			
		||||
	if not done:
 | 
			
		||||
		ec, err_msg = job_state.GetError
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			interface_name,
 | 
			
		||||
			'Exit code %s, stderr = %s' % (str(ec), stderr))
 | 
			
		||||
			'Exit code %s, stderr = %s' % (str(ec), err_msg))
 | 
			
		||||
 | 
			
		||||
	cfg.load()
 | 
			
		||||
	return '/'
 | 
			
		||||
@@ -118,6 +85,8 @@ def move(interface_name, lv_name, pv_src_obj, pv_source_range,
 | 
			
		||||
 | 
			
		||||
				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
 | 
			
		||||
 | 
			
		||||
		# Generate the command line for this command, but don't
 | 
			
		||||
		# execute it.
 | 
			
		||||
		cmd = pv_move_lv_cmd(move_options,
 | 
			
		||||
								lv_name,
 | 
			
		||||
								pv_src.lvm_id,
 | 
			
		||||
@@ -142,15 +111,104 @@ def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
 | 
			
		||||
			'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _run_cmd(req):
 | 
			
		||||
	log_debug(
 | 
			
		||||
		"_run_cmd: Running method: %s with args %s" %
 | 
			
		||||
		(str(req.method), str(req.arguments)))
 | 
			
		||||
	req.run_cmd()
 | 
			
		||||
	log_debug("_run_cmd: complete!")
 | 
			
		||||
def background_reaper():
 | 
			
		||||
	while cfg.run.value != 0:
 | 
			
		||||
		with _rlock:
 | 
			
		||||
			num_threads = len(_thread_list) - 1
 | 
			
		||||
			if num_threads >= 0:
 | 
			
		||||
				for i in range(num_threads, -1, -1):
 | 
			
		||||
					_thread_list[i].join(0)
 | 
			
		||||
					if not _thread_list[i].is_alive():
 | 
			
		||||
						log_debug("Removing thread: %s" % _thread_list[i].name)
 | 
			
		||||
						_thread_list.pop(i)
 | 
			
		||||
 | 
			
		||||
		time.sleep(3)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cmd_runner(request):
 | 
			
		||||
	t = threading.Thread(target=_run_cmd, args=(request,),
 | 
			
		||||
							name="cmd_runner %s" % str(request.method))
 | 
			
		||||
def background_execute(command, background_job):
 | 
			
		||||
 | 
			
		||||
	# Wrap this whole operation in an exception handler, otherwise if we
 | 
			
		||||
	# hit a code bug we will silently exit this thread without anyone being
 | 
			
		||||
	# the wiser.
 | 
			
		||||
	try:
 | 
			
		||||
		# We need to execute these command stand alone by forking & exec'ing
 | 
			
		||||
		# the command always!
 | 
			
		||||
		command.insert(0, cfg.LVM_CMD)
 | 
			
		||||
		process = subprocess.Popen(command, stdout=subprocess.PIPE,
 | 
			
		||||
									env=os.environ,
 | 
			
		||||
									stderr=subprocess.PIPE, close_fds=True)
 | 
			
		||||
 | 
			
		||||
		log_debug("Background process for %s is %d" %
 | 
			
		||||
					(str(command), process.pid))
 | 
			
		||||
 | 
			
		||||
		lines_iterator = iter(process.stdout.readline, b"")
 | 
			
		||||
		for line in lines_iterator:
 | 
			
		||||
			line_str = line.decode("utf-8")
 | 
			
		||||
 | 
			
		||||
			# Check to see if the line has the correct number of separators
 | 
			
		||||
			try:
 | 
			
		||||
				if line_str.count(':') == 2:
 | 
			
		||||
					(device, ignore, percentage) = line_str.split(':')
 | 
			
		||||
					background_job.Percent = round(
 | 
			
		||||
						float(percentage.strip()[:-1]), 1)
 | 
			
		||||
			except ValueError:
 | 
			
		||||
				log_error("Trying to parse percentage which failed for %s" %
 | 
			
		||||
					line_str)
 | 
			
		||||
 | 
			
		||||
		out = process.communicate()
 | 
			
		||||
 | 
			
		||||
		if process.returncode == 0:
 | 
			
		||||
			background_job.Percent = 100
 | 
			
		||||
		else:
 | 
			
		||||
			log_error("Failed to execute background job %s, STDERR= %s"
 | 
			
		||||
						% (str(command), out[1]))
 | 
			
		||||
 | 
			
		||||
		background_job.set_result(process.returncode, out[1])
 | 
			
		||||
		log_debug("Background process %d complete!" % process.pid)
 | 
			
		||||
 | 
			
		||||
	except Exception:
 | 
			
		||||
		# In the unlikely event that we blow up, we need to unblock caller which
 | 
			
		||||
		# is waiting on an answer.
 | 
			
		||||
		st = traceback.format_exc()
 | 
			
		||||
		error = "Exception in background thread: \n%s" % st
 | 
			
		||||
		log_error(error)
 | 
			
		||||
		background_job.set_result(1, error)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add(command, reporting_job):
 | 
			
		||||
	# Create the thread, get it running and then add it to the list
 | 
			
		||||
	t = threading.Thread(
 | 
			
		||||
		target=background_execute,
 | 
			
		||||
		name="thread: " + ' '.join(command),
 | 
			
		||||
		args=(command, reporting_job))
 | 
			
		||||
	t.start()
 | 
			
		||||
 | 
			
		||||
	with _rlock:
 | 
			
		||||
		_thread_list.append(t)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wait_thread(job, timeout, cb, cbe):
 | 
			
		||||
	# We need to put the wait on it's own thread, so that we don't block the
 | 
			
		||||
	# entire dbus queue processing thread
 | 
			
		||||
	try:
 | 
			
		||||
		cb(job.state.Wait(timeout))
 | 
			
		||||
	except Exception as e:
 | 
			
		||||
		cbe("Wait exception: %s" % str(e))
 | 
			
		||||
	return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_wait(job, timeout, cb, cbe):
 | 
			
		||||
 | 
			
		||||
	if timeout == 0:
 | 
			
		||||
		# Users are basically polling, do not create thread
 | 
			
		||||
		cb(job.Complete)
 | 
			
		||||
	else:
 | 
			
		||||
		t = threading.Thread(
 | 
			
		||||
			target=wait_thread,
 | 
			
		||||
			name="thread job.Wait: %s" % job.dbus_object_path(),
 | 
			
		||||
			args=(job, timeout, cb, cbe)
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		t.start()
 | 
			
		||||
		with _rlock:
 | 
			
		||||
			_thread_list.append(t)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ bus = None
 | 
			
		||||
args = None
 | 
			
		||||
 | 
			
		||||
# Set to true if we are depending on external events for updates
 | 
			
		||||
got_external_event = False
 | 
			
		||||
ee = False
 | 
			
		||||
 | 
			
		||||
# Shared state variable across all processes
 | 
			
		||||
run = multiprocessing.Value('i', 1)
 | 
			
		||||
@@ -38,20 +38,18 @@ SHELL_IN_USE = None
 | 
			
		||||
# Lock used by pprint
 | 
			
		||||
stdout_lock = multiprocessing.Lock()
 | 
			
		||||
 | 
			
		||||
kick_q = multiprocessing.Queue()
 | 
			
		||||
worker_q = queue.Queue()
 | 
			
		||||
 | 
			
		||||
# Main event loop
 | 
			
		||||
loop = None
 | 
			
		||||
 | 
			
		||||
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,33 +71,15 @@ 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()
 | 
			
		||||
 | 
			
		||||
# Used to prevent circular imports...
 | 
			
		||||
load = None
 | 
			
		||||
event = None
 | 
			
		||||
 | 
			
		||||
# Boolean to denote if lvm supports VDO integration
 | 
			
		||||
vdo_support = False
 | 
			
		||||
 | 
			
		||||
# Global cached state
 | 
			
		||||
db = None
 | 
			
		||||
 | 
			
		||||
# lvm flight recorder
 | 
			
		||||
blackbox = None
 | 
			
		||||
 | 
			
		||||
# RequestEntry ctor
 | 
			
		||||
create_request_entry = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def exit_daemon():
 | 
			
		||||
    """
 | 
			
		||||
    Exit the daemon cleanly
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
    if run and loop:
 | 
			
		||||
        run.value = 0
 | 
			
		||||
        loop.quit()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
from subprocess import Popen, PIPE
 | 
			
		||||
import select
 | 
			
		||||
import time
 | 
			
		||||
import threading
 | 
			
		||||
from itertools import chain
 | 
			
		||||
@@ -16,10 +15,14 @@ import collections
 | 
			
		||||
import traceback
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from lvmdbusd import cfg
 | 
			
		||||
from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify,\
 | 
			
		||||
							make_non_block, read_decoded
 | 
			
		||||
from lvmdbusd.lvm_shell_proxy import LVMShellProxy
 | 
			
		||||
try:
 | 
			
		||||
	from . import cfg
 | 
			
		||||
	from .utils import pv_dest_ranges, log_debug, log_error
 | 
			
		||||
	from .lvm_shell_proxy import LVMShellProxy
 | 
			
		||||
except SystemError:
 | 
			
		||||
	import cfg
 | 
			
		||||
	from utils import pv_dest_ranges, log_debug, log_error
 | 
			
		||||
	from lvm_shell_proxy import LVMShellProxy
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
	import simplejson as json
 | 
			
		||||
@@ -39,7 +42,6 @@ cmd_lock = threading.RLock()
 | 
			
		||||
class LvmExecutionMeta(object):
 | 
			
		||||
 | 
			
		||||
	def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt):
 | 
			
		||||
		self.lock = threading.RLock()
 | 
			
		||||
		self.start = start
 | 
			
		||||
		self.ended = ended
 | 
			
		||||
		self.cmd = cmd
 | 
			
		||||
@@ -48,30 +50,28 @@ class LvmExecutionMeta(object):
 | 
			
		||||
		self.stderr_txt = stderr_txt
 | 
			
		||||
 | 
			
		||||
	def __str__(self):
 | 
			
		||||
		with self.lock:
 | 
			
		||||
			return "EC= %d for %s\n" \
 | 
			
		||||
				"STARTED: %f, ENDED: %f\n" \
 | 
			
		||||
				"STDOUT=%s\n" \
 | 
			
		||||
				"STDERR=%s\n" % \
 | 
			
		||||
				(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt,
 | 
			
		||||
				self.stderr_txt)
 | 
			
		||||
		return "EC= %d for %s\n" \
 | 
			
		||||
			"STARTED: %f, ENDED: %f\n" \
 | 
			
		||||
			"STDOUT=%s\n" \
 | 
			
		||||
			"STDERR=%s\n" % \
 | 
			
		||||
			(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt,
 | 
			
		||||
			self.stderr_txt)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LvmFlightRecorder(object):
 | 
			
		||||
 | 
			
		||||
	def __init__(self, size=16):
 | 
			
		||||
		self.queue = collections.deque(maxlen=size)
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		self.queue = collections.deque(maxlen=16)
 | 
			
		||||
 | 
			
		||||
	def add(self, lvm_exec_meta):
 | 
			
		||||
		self.queue.append(lvm_exec_meta)
 | 
			
		||||
 | 
			
		||||
	def dump(self):
 | 
			
		||||
		with cmd_lock:
 | 
			
		||||
			if len(self.queue):
 | 
			
		||||
				log_error("LVM dbus flight recorder START")
 | 
			
		||||
				for c in reversed(self.queue):
 | 
			
		||||
					log_error(str(c))
 | 
			
		||||
				log_error("LVM dbus flight recorder END")
 | 
			
		||||
			log_error("LVM dbus flight recorder START")
 | 
			
		||||
			for c in self.queue:
 | 
			
		||||
				log_error(str(c))
 | 
			
		||||
			log_error("LVM dbus flight recorder END")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cfg.blackbox = LvmFlightRecorder()
 | 
			
		||||
@@ -84,68 +84,26 @@ def _debug_c(cmd, exit_code, out):
 | 
			
		||||
	log_error(("STDERR=\n %s\n" % out[1]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def call_lvm(command, debug=False, line_cb=None,
 | 
			
		||||
			 cb_data=None):
 | 
			
		||||
def call_lvm(command, debug=False):
 | 
			
		||||
	"""
 | 
			
		||||
	Call an executable and return a tuple of exitcode, stdout, stderr
 | 
			
		||||
	:param command: Command to execute
 | 
			
		||||
	:param debug:   Dump debug to stdout
 | 
			
		||||
	:param line_cb:	Call the supplied function for each line read from
 | 
			
		||||
					stdin, CALL MUST EXECUTE QUICKLY and not *block*
 | 
			
		||||
					otherwise call_lvm function will fail to read
 | 
			
		||||
					stdin/stdout.  Return value of call back is ignored
 | 
			
		||||
	:param cb_data: Supplied to callback to allow caller access to
 | 
			
		||||
								its own data
 | 
			
		||||
 | 
			
		||||
	# Callback signature
 | 
			
		||||
	def my_callback(my_context, line_read_stdin)
 | 
			
		||||
		pass
 | 
			
		||||
	:param command:     Command to execute
 | 
			
		||||
	:param debug:       Dump debug to stdout
 | 
			
		||||
	"""
 | 
			
		||||
	# print 'STACK:'
 | 
			
		||||
	# for line in traceback.format_stack():
 | 
			
		||||
	#    print line.strip()
 | 
			
		||||
 | 
			
		||||
	# Prepend the full lvm executable so that we can run different versions
 | 
			
		||||
	# in different locations on the same box
 | 
			
		||||
	command.insert(0, cfg.LVM_CMD)
 | 
			
		||||
	command = add_no_notify(command)
 | 
			
		||||
 | 
			
		||||
	process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True,
 | 
			
		||||
					env=os.environ)
 | 
			
		||||
	out = process.communicate()
 | 
			
		||||
 | 
			
		||||
	stdout_text = ""
 | 
			
		||||
	stderr_text = ""
 | 
			
		||||
	stdout_index = 0
 | 
			
		||||
	make_non_block(process.stdout)
 | 
			
		||||
	make_non_block(process.stderr)
 | 
			
		||||
 | 
			
		||||
	while True:
 | 
			
		||||
		try:
 | 
			
		||||
			rd_fd = [process.stdout.fileno(), process.stderr.fileno()]
 | 
			
		||||
			ready = select.select(rd_fd, [], [], 2)
 | 
			
		||||
 | 
			
		||||
			for r in ready[0]:
 | 
			
		||||
				if r == process.stdout.fileno():
 | 
			
		||||
					stdout_text += read_decoded(process.stdout)
 | 
			
		||||
				elif r == process.stderr.fileno():
 | 
			
		||||
					stderr_text += read_decoded(process.stderr)
 | 
			
		||||
 | 
			
		||||
			if line_cb is not None:
 | 
			
		||||
				# Process the callback for each line read!
 | 
			
		||||
				while True:
 | 
			
		||||
					i = stdout_text.find("\n", stdout_index)
 | 
			
		||||
					if i != -1:
 | 
			
		||||
						try:
 | 
			
		||||
							line_cb(cb_data, stdout_text[stdout_index:i])
 | 
			
		||||
						except:
 | 
			
		||||
							st = traceback.format_exc()
 | 
			
		||||
							log_error("call_lvm: line_cb exception: \n %s" % st)
 | 
			
		||||
						stdout_index = i + 1
 | 
			
		||||
					else:
 | 
			
		||||
						break
 | 
			
		||||
 | 
			
		||||
			# Check to see if process has terminated, None when running
 | 
			
		||||
			if process.poll() is not None:
 | 
			
		||||
				break
 | 
			
		||||
		except IOError as ioe:
 | 
			
		||||
			log_debug("call_lvm:" + str(ioe))
 | 
			
		||||
			pass
 | 
			
		||||
	stdout_text = bytes(out[0]).decode("utf-8")
 | 
			
		||||
	stderr_text = bytes(out[1]).decode("utf-8")
 | 
			
		||||
 | 
			
		||||
	if debug or process.returncode != 0:
 | 
			
		||||
		_debug_c(command, process.returncode, (stdout_text, stderr_text))
 | 
			
		||||
@@ -159,7 +117,6 @@ _t_call = call_lvm
 | 
			
		||||
 | 
			
		||||
def _shell_cfg():
 | 
			
		||||
	global _t_call
 | 
			
		||||
	# noinspection PyBroadException
 | 
			
		||||
	try:
 | 
			
		||||
		lvm_shell = LVMShellProxy()
 | 
			
		||||
		_t_call = lvm_shell.call_lvm
 | 
			
		||||
@@ -260,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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -309,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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -326,8 +280,8 @@ def vg_remove(vg_name, remove_options):
 | 
			
		||||
def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
	cmd.extend(['--size', '%dB' % size_bytes])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name, '--yes'])
 | 
			
		||||
	cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name])
 | 
			
		||||
	pv_dest_ranges(cmd, pv_dests)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
@@ -338,38 +292,38 @@ 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool):
 | 
			
		||||
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	if not thin_pool:
 | 
			
		||||
		cmd.extend(['--size', '%dB' % size_bytes])
 | 
			
		||||
		cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
	else:
 | 
			
		||||
		cmd.extend(['--thin', '--size', '%dB' % size_bytes])
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--yes'])
 | 
			
		||||
	return cmd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
 | 
			
		||||
	cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
 | 
			
		||||
		cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	if not thin_pool:
 | 
			
		||||
		cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
	else:
 | 
			
		||||
		cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
@@ -382,15 +336,15 @@ def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--type', raid_type])
 | 
			
		||||
	cmd.extend(['--size', '%dB' % size_bytes])
 | 
			
		||||
	cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
 | 
			
		||||
	if num_stripes != 0:
 | 
			
		||||
		cmd.extend(['--stripes', str(int(num_stripes))])
 | 
			
		||||
		cmd.extend(['--stripes', str(num_stripes)])
 | 
			
		||||
 | 
			
		||||
	if stripe_size_kb != 0:
 | 
			
		||||
		cmd.extend(['--stripesize', str(int(stripe_size_kb))])
 | 
			
		||||
		cmd.extend(['--stripesize', str(stripe_size_kb)])
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--name', name, vg_name, '--yes'])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -403,15 +357,14 @@ def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
 | 
			
		||||
								size_bytes, num_stripes, stripe_size_kb)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_lv_create_mirror(
 | 
			
		||||
		vg_name, create_options, name, size_bytes, num_copies):
 | 
			
		||||
def vg_lv_create_mirror(vg_name, create_options, name, size_bytes, num_copies):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--type', 'mirror'])
 | 
			
		||||
	cmd.extend(['--mirrors', str(int(num_copies))])
 | 
			
		||||
	cmd.extend(['--size', '%dB' % size_bytes])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name, '--yes'])
 | 
			
		||||
	cmd.extend(['--mirrors', str(num_copies)])
 | 
			
		||||
	cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -431,24 +384,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))
 | 
			
		||||
@@ -482,8 +417,8 @@ def lv_resize(lv_full_name, size_change, pv_dests,
 | 
			
		||||
def lv_lv_create(lv_full_name, create_options, name, size_bytes):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
	cmd.extend(['--virtualsize', '%dB' % size_bytes, '-T'])
 | 
			
		||||
	cmd.extend(['--name', name, lv_full_name, '--yes'])
 | 
			
		||||
	cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T'])
 | 
			
		||||
	cmd.extend(['--name', name, lv_full_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -496,15 +431,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:
 | 
			
		||||
@@ -520,28 +446,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)
 | 
			
		||||
@@ -554,16 +458,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',
 | 
			
		||||
@@ -591,22 +485,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),
 | 
			
		||||
@@ -618,8 +496,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
 | 
			
		||||
@@ -627,13 +504,7 @@ def lvm_full_report_json():
 | 
			
		||||
			assert(type(out) == dict)
 | 
			
		||||
			return out
 | 
			
		||||
		else:
 | 
			
		||||
			try:
 | 
			
		||||
				return json.loads(out)
 | 
			
		||||
			except json.decoder.JSONDecodeError as joe:
 | 
			
		||||
				log_error("JSONDecodeError %s, \n JSON=\n%s\n" %
 | 
			
		||||
							(str(joe), out))
 | 
			
		||||
				raise joe
 | 
			
		||||
 | 
			
		||||
			return json.loads(out)
 | 
			
		||||
	return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -683,7 +554,7 @@ def pv_resize(device, size_bytes, create_options):
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	if size_bytes != 0:
 | 
			
		||||
		cmd.extend(['--yes', '--setphysicalvolumesize', '%dB' % size_bytes])
 | 
			
		||||
		cmd.extend(['--setphysicalvolumesize', str(size_bytes) + 'B'])
 | 
			
		||||
 | 
			
		||||
	cmd.extend([device])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
@@ -748,10 +619,10 @@ def vg_reduce(vg_name, missing, pv_devices, reduce_options):
 | 
			
		||||
	cmd = ['vgreduce']
 | 
			
		||||
	cmd.extend(options_to_cli_args(reduce_options))
 | 
			
		||||
 | 
			
		||||
	if len(pv_devices) == 0:
 | 
			
		||||
		cmd.append('--all')
 | 
			
		||||
	if missing:
 | 
			
		||||
		cmd.append('--removemissing')
 | 
			
		||||
	elif len(pv_devices) == 0:
 | 
			
		||||
		cmd.append('--all')
 | 
			
		||||
 | 
			
		||||
	cmd.append(vg_name)
 | 
			
		||||
	cmd.extend(pv_devices)
 | 
			
		||||
@@ -779,12 +650,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):
 | 
			
		||||
@@ -826,7 +697,6 @@ def activate_deactivate(op, name, activate, control_flags, options):
 | 
			
		||||
		op += 'n'
 | 
			
		||||
 | 
			
		||||
	cmd.append(op)
 | 
			
		||||
	cmd.append("-y")
 | 
			
		||||
	cmd.append(name)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
@@ -862,8 +732,8 @@ def lv_retrieve_with_segments():
 | 
			
		||||
				'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
 | 
			
		||||
				'metadata_lv', 'seg_pe_ranges', 'segtype', 'lv_parent',
 | 
			
		||||
				'lv_role', 'lv_layout',
 | 
			
		||||
				'snap_percent', 'metadata_percent', 'copy_percent',
 | 
			
		||||
				'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
 | 
			
		||||
			    'snap_percent', 'metadata_percent', 'copy_percent',
 | 
			
		||||
			    'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
 | 
			
		||||
 | 
			
		||||
	cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
 | 
			
		||||
	rc, out, err = call(cmd)
 | 
			
		||||
@@ -880,4 +750,4 @@ if __name__ == '__main__':
 | 
			
		||||
	pv_data = pv_retrieve_with_segs()
 | 
			
		||||
 | 
			
		||||
	for p in pv_data:
 | 
			
		||||
		print(str(p))
 | 
			
		||||
		log_debug(str(p))
 | 
			
		||||
 
 | 
			
		||||
@@ -11,211 +11,20 @@ from .pv import load_pvs
 | 
			
		||||
from .vg import load_vgs
 | 
			
		||||
from .lv import load_lvs
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .utils import MThreadRunner, log_debug, log_error
 | 
			
		||||
import threading
 | 
			
		||||
import queue
 | 
			
		||||
import time
 | 
			
		||||
import traceback
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _main_thread_load(refresh=True, emit_signal=True):
 | 
			
		||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True):
 | 
			
		||||
	num_total_changes = 0
 | 
			
		||||
	to_remove = []
 | 
			
		||||
 | 
			
		||||
	(changes, remove) = load_pvs(
 | 
			
		||||
		refresh=refresh,
 | 
			
		||||
		emit_signal=emit_signal,
 | 
			
		||||
		cache_refresh=False)[1:]
 | 
			
		||||
	num_total_changes += changes
 | 
			
		||||
	to_remove.extend(remove)
 | 
			
		||||
 | 
			
		||||
	(changes, remove) = load_vgs(
 | 
			
		||||
		refresh=refresh,
 | 
			
		||||
		emit_signal=emit_signal,
 | 
			
		||||
		cache_refresh=False)[1:]
 | 
			
		||||
 | 
			
		||||
	num_total_changes += changes
 | 
			
		||||
	to_remove.extend(remove)
 | 
			
		||||
 | 
			
		||||
	(lv_changes, remove) = load_lvs(
 | 
			
		||||
		refresh=refresh,
 | 
			
		||||
		emit_signal=emit_signal,
 | 
			
		||||
		cache_refresh=False)[1:]
 | 
			
		||||
 | 
			
		||||
	num_total_changes += lv_changes
 | 
			
		||||
	to_remove.extend(remove)
 | 
			
		||||
 | 
			
		||||
	# 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:
 | 
			
		||||
		(changes, remove) = load_vgs(
 | 
			
		||||
			refresh=refresh,
 | 
			
		||||
			emit_signal=emit_signal,
 | 
			
		||||
			cache_refresh=False)[1:]
 | 
			
		||||
 | 
			
		||||
	num_total_changes += changes
 | 
			
		||||
	to_remove.extend(remove)
 | 
			
		||||
 | 
			
		||||
	# Remove any objects that are no longer needed.  We do this after we process
 | 
			
		||||
	# all the objects to ensure that references still exist for objects that
 | 
			
		||||
	# are processed after them.
 | 
			
		||||
	to_remove.reverse()
 | 
			
		||||
	for i in to_remove:
 | 
			
		||||
		dbus_obj = cfg.om.get_object_by_path(i)
 | 
			
		||||
		if dbus_obj:
 | 
			
		||||
			cfg.om.remove_object(dbus_obj, True)
 | 
			
		||||
			num_total_changes += 1
 | 
			
		||||
 | 
			
		||||
	return num_total_changes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True,
 | 
			
		||||
			need_main_thread=True):
 | 
			
		||||
	# Go through and load all the PVs, VGs and LVs
 | 
			
		||||
	if cache_refresh:
 | 
			
		||||
		cfg.db.refresh(log)
 | 
			
		||||
 | 
			
		||||
	if need_main_thread:
 | 
			
		||||
		rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
 | 
			
		||||
	else:
 | 
			
		||||
		rc = _main_thread_load(refresh, emit_signal)
 | 
			
		||||
	num_total_changes += load_pvs(refresh=refresh, emit_signal=emit_signal,
 | 
			
		||||
									cache_refresh=False)[1]
 | 
			
		||||
	num_total_changes += load_vgs(refresh=refresh, emit_signal=emit_signal,
 | 
			
		||||
									cache_refresh=False)[1]
 | 
			
		||||
	num_total_changes += load_lvs(refresh=refresh, emit_signal=emit_signal,
 | 
			
		||||
									cache_refresh=False)[1]
 | 
			
		||||
 | 
			
		||||
	return rc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Even though lvm can handle multiple changes concurrently it really doesn't
 | 
			
		||||
# make sense to make a 1-1 fetch of data for each change of lvm because when
 | 
			
		||||
# we fetch the data once all previous changes are reflected.
 | 
			
		||||
class StateUpdate(object):
 | 
			
		||||
 | 
			
		||||
	class UpdateRequest(object):
 | 
			
		||||
 | 
			
		||||
		def __init__(self, refresh, emit_signal, cache_refresh, log,
 | 
			
		||||
						need_main_thread):
 | 
			
		||||
			self.is_done = False
 | 
			
		||||
			self.refresh = refresh
 | 
			
		||||
			self.emit_signal = emit_signal
 | 
			
		||||
			self.cache_refresh = cache_refresh
 | 
			
		||||
			self.log = log
 | 
			
		||||
			self.need_main_thread = need_main_thread
 | 
			
		||||
			self.result = None
 | 
			
		||||
			self.cond = threading.Condition(threading.Lock())
 | 
			
		||||
 | 
			
		||||
		def done(self):
 | 
			
		||||
			with self.cond:
 | 
			
		||||
				if not self.is_done:
 | 
			
		||||
					self.cond.wait()
 | 
			
		||||
			return self.result
 | 
			
		||||
 | 
			
		||||
		def set_result(self, result):
 | 
			
		||||
			with self.cond:
 | 
			
		||||
				self.result = result
 | 
			
		||||
				self.is_done = True
 | 
			
		||||
				self.cond.notify_all()
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def update_thread(obj):
 | 
			
		||||
		exception_count = 0
 | 
			
		||||
 | 
			
		||||
		queued_requests = []
 | 
			
		||||
		while cfg.run.value != 0:
 | 
			
		||||
			# noinspection PyBroadException
 | 
			
		||||
			try:
 | 
			
		||||
				refresh = True
 | 
			
		||||
				emit_signal = True
 | 
			
		||||
				cache_refresh = True
 | 
			
		||||
				log = True
 | 
			
		||||
				need_main_thread = True
 | 
			
		||||
 | 
			
		||||
				with obj.lock:
 | 
			
		||||
					wait = not obj.deferred
 | 
			
		||||
					obj.deferred = False
 | 
			
		||||
 | 
			
		||||
				if len(queued_requests) == 0 and wait:
 | 
			
		||||
					queued_requests.append(obj.queue.get(True, 2))
 | 
			
		||||
 | 
			
		||||
				# Ok we have one or the deferred queue has some,
 | 
			
		||||
				# check if any others
 | 
			
		||||
				try:
 | 
			
		||||
					while True:
 | 
			
		||||
						queued_requests.append(obj.queue.get(False))
 | 
			
		||||
 | 
			
		||||
				except queue.Empty:
 | 
			
		||||
					pass
 | 
			
		||||
 | 
			
		||||
				if len(queued_requests) > 1:
 | 
			
		||||
					log_debug("Processing %d updates!" % len(queued_requests),
 | 
			
		||||
							'bg_black', 'fg_light_green')
 | 
			
		||||
 | 
			
		||||
				# We have what we can, run the update with the needed options
 | 
			
		||||
				for i in queued_requests:
 | 
			
		||||
					if not i.refresh:
 | 
			
		||||
						refresh = False
 | 
			
		||||
					if not i.emit_signal:
 | 
			
		||||
						emit_signal = False
 | 
			
		||||
					if not i.cache_refresh:
 | 
			
		||||
						cache_refresh = False
 | 
			
		||||
					if not i.log:
 | 
			
		||||
						log = False
 | 
			
		||||
					if not i.need_main_thread:
 | 
			
		||||
						need_main_thread = False
 | 
			
		||||
 | 
			
		||||
				num_changes = load(refresh, emit_signal, cache_refresh, log,
 | 
			
		||||
									need_main_thread)
 | 
			
		||||
				# Update is done, let everyone know!
 | 
			
		||||
				for i in queued_requests:
 | 
			
		||||
					i.set_result(num_changes)
 | 
			
		||||
 | 
			
		||||
				# Only clear out the requests after we have given them a result
 | 
			
		||||
				# otherwise we can orphan the waiting threads and they never
 | 
			
		||||
				# wake up if we get an exception
 | 
			
		||||
				queued_requests = []
 | 
			
		||||
 | 
			
		||||
				# We retrieved OK, clear exception count
 | 
			
		||||
				exception_count = 0
 | 
			
		||||
 | 
			
		||||
			except queue.Empty:
 | 
			
		||||
				pass
 | 
			
		||||
			except Exception as e:
 | 
			
		||||
				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()
 | 
			
		||||
		self.queue = queue.Queue()
 | 
			
		||||
		self.deferred = False
 | 
			
		||||
 | 
			
		||||
		# Do initial load
 | 
			
		||||
		load(refresh=False, emit_signal=False, need_main_thread=False)
 | 
			
		||||
 | 
			
		||||
		self.thread = threading.Thread(target=StateUpdate.update_thread,
 | 
			
		||||
										args=(self,),
 | 
			
		||||
										name="StateUpdate.update_thread")
 | 
			
		||||
 | 
			
		||||
	def load(self, refresh=True, emit_signal=True, cache_refresh=True,
 | 
			
		||||
					log=True, need_main_thread=True):
 | 
			
		||||
		# Place this request on the queue and wait for it to be completed
 | 
			
		||||
		req = StateUpdate.UpdateRequest(refresh, emit_signal, cache_refresh,
 | 
			
		||||
										log, need_main_thread)
 | 
			
		||||
		self.queue.put(req)
 | 
			
		||||
		return req.done()
 | 
			
		||||
 | 
			
		||||
	def event(self):
 | 
			
		||||
		with self.lock:
 | 
			
		||||
			self.deferred = True
 | 
			
		||||
	return num_total_changes
 | 
			
		||||
 
 | 
			
		||||
@@ -8,55 +8,12 @@
 | 
			
		||||
# 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
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .cfg import JOB_INTERFACE
 | 
			
		||||
import dbus
 | 
			
		||||
import threading
 | 
			
		||||
# noinspection PyUnresolvedReferences
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Class that handles a client waiting for something to be complete.  We either
 | 
			
		||||
# get a timeout or the operation is done.
 | 
			
		||||
class WaitingClient(object):
 | 
			
		||||
 | 
			
		||||
	# A timeout occurred
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _timeout(wc):
 | 
			
		||||
		with wc.rlock:
 | 
			
		||||
			if wc.in_use:
 | 
			
		||||
				wc.in_use = False
 | 
			
		||||
				# 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)
 | 
			
		||||
				wc.job_state = None
 | 
			
		||||
 | 
			
		||||
	def __init__(self, job_state, tmo, cb, cbe):
 | 
			
		||||
		self.rlock = threading.RLock()
 | 
			
		||||
		self.job_state = job_state
 | 
			
		||||
		self.cb = cb
 | 
			
		||||
		self.cbe = cbe
 | 
			
		||||
		self.in_use = True		# Indicates if object is in play
 | 
			
		||||
		self.timer_id = -1
 | 
			
		||||
		if tmo > 0:
 | 
			
		||||
			self.timer_id = GLib.timeout_add_seconds(
 | 
			
		||||
				tmo, WaitingClient._timeout, self)
 | 
			
		||||
 | 
			
		||||
	# The job finished before the timer popped and we are being notified that
 | 
			
		||||
	# it's done
 | 
			
		||||
	def notify(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			if self.in_use:
 | 
			
		||||
				self.in_use = False
 | 
			
		||||
				# Clear timer
 | 
			
		||||
				if self.timer_id != -1:
 | 
			
		||||
					GLib.source_remove(self.timer_id)
 | 
			
		||||
					self.timer_id = -1
 | 
			
		||||
 | 
			
		||||
				mt_async_call(self.cb, self.job_state.Complete)
 | 
			
		||||
				self.job_state = None
 | 
			
		||||
from . import background
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
@@ -67,9 +24,9 @@ class JobState(object):
 | 
			
		||||
		self._percent = 0
 | 
			
		||||
		self._complete = False
 | 
			
		||||
		self._request = request
 | 
			
		||||
		self._cond = threading.Condition(self.rlock)
 | 
			
		||||
		self._ec = 0
 | 
			
		||||
		self._stderr = ''
 | 
			
		||||
		self._waiting_clients = []
 | 
			
		||||
 | 
			
		||||
		# This is an lvm command that is just taking too long and doesn't
 | 
			
		||||
		# support background operation
 | 
			
		||||
@@ -100,7 +57,7 @@ class JobState(object):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			self._complete = value
 | 
			
		||||
			self._percent = 100
 | 
			
		||||
			self.notify_waiting_clients()
 | 
			
		||||
			self._cond.notify_all()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def GetError(self):
 | 
			
		||||
@@ -114,10 +71,29 @@ class JobState(object):
 | 
			
		||||
			else:
 | 
			
		||||
				return (-1, 'Job is not complete!')
 | 
			
		||||
 | 
			
		||||
	def set_result(self, ec, msg):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			self.Complete = True
 | 
			
		||||
			self._ec = ec
 | 
			
		||||
			self._stderr = msg
 | 
			
		||||
 | 
			
		||||
	def dtor(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			self._request = None
 | 
			
		||||
 | 
			
		||||
	def Wait(self, timeout):
 | 
			
		||||
		try:
 | 
			
		||||
			with self._cond:
 | 
			
		||||
				# Check to see if we are done, before we wait
 | 
			
		||||
				if not self.Complete:
 | 
			
		||||
					if timeout != -1:
 | 
			
		||||
						self._cond.wait(timeout)
 | 
			
		||||
					else:
 | 
			
		||||
						self._cond.wait()
 | 
			
		||||
				return self.Complete
 | 
			
		||||
		except RuntimeError:
 | 
			
		||||
			return False
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Result(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
@@ -125,36 +101,6 @@ class JobState(object):
 | 
			
		||||
				return self._request.result()
 | 
			
		||||
			return '/'
 | 
			
		||||
 | 
			
		||||
	def add_waiting_client(self, client):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			# Avoid race condition where it goes complete before we get added
 | 
			
		||||
			# to the list of waiting clients
 | 
			
		||||
			if self.Complete:
 | 
			
		||||
				client.notify()
 | 
			
		||||
			else:
 | 
			
		||||
				self._waiting_clients.append(client)
 | 
			
		||||
 | 
			
		||||
	def remove_waiting_client(self, client):
 | 
			
		||||
		# If a waiting client timer pops before the job is done we will allow
 | 
			
		||||
		# the client to remove themselves from the list.  As we have a lock
 | 
			
		||||
		# here and a lock in the waiting client too, and they can be obtained
 | 
			
		||||
		# in different orders, a dead lock can occur.
 | 
			
		||||
		# As this remove is really optional, we will try to acquire the lock
 | 
			
		||||
		# and remove.  If we are unsuccessful it's not fatal, we just delay
 | 
			
		||||
		# the time when the objects can be garbage collected by python
 | 
			
		||||
		if self.rlock.acquire(False):
 | 
			
		||||
			try:
 | 
			
		||||
				self._waiting_clients.remove(client)
 | 
			
		||||
			finally:
 | 
			
		||||
				self.rlock.release()
 | 
			
		||||
 | 
			
		||||
	def notify_waiting_clients(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			for c in self._waiting_clients:
 | 
			
		||||
				c.notify()
 | 
			
		||||
 | 
			
		||||
			self._waiting_clients = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class Job(AutomatedProperties):
 | 
			
		||||
@@ -176,24 +122,25 @@ class Job(AutomatedProperties):
 | 
			
		||||
	def Percent(self):
 | 
			
		||||
		return dbus.Double(float(self.state.Percent))
 | 
			
		||||
 | 
			
		||||
	@Percent.setter
 | 
			
		||||
	def Percent(self, value):
 | 
			
		||||
		self.state.Percent = value
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Complete(self):
 | 
			
		||||
		return dbus.Boolean(self.state.Complete)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _signal_complete(obj):
 | 
			
		||||
		obj.PropertiesChanged(
 | 
			
		||||
			JOB_INTERFACE, dict(Complete=dbus.Boolean(obj.state.Complete)), [])
 | 
			
		||||
 | 
			
		||||
	@Complete.setter
 | 
			
		||||
	def Complete(self, value):
 | 
			
		||||
		self.state.Complete = value
 | 
			
		||||
		mt_async_call(Job._signal_complete, self)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def GetError(self):
 | 
			
		||||
		return dbus.Struct(self.state.GetError, signature="(is)")
 | 
			
		||||
 | 
			
		||||
	def set_result(self, ec, msg):
 | 
			
		||||
		self.state.set_result(ec, msg)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(dbus_interface=JOB_INTERFACE)
 | 
			
		||||
	def Remove(self):
 | 
			
		||||
		if self.state.Complete:
 | 
			
		||||
@@ -208,11 +155,7 @@ class Job(AutomatedProperties):
 | 
			
		||||
							out_signature='b',
 | 
			
		||||
							async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Wait(self, timeout, cb, cbe):
 | 
			
		||||
		if timeout == 0 or self.state.Complete:
 | 
			
		||||
			cb(dbus.Boolean(self.state.Complete))
 | 
			
		||||
		else:
 | 
			
		||||
			self.state.add_waiting_client(
 | 
			
		||||
				WaitingClient(self.state, timeout, cb, cbe))
 | 
			
		||||
		background.add_wait(self, timeout, cb, cbe)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Result(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -75,10 +75,11 @@ def common(retrieve, o_type, search_keys,
 | 
			
		||||
 | 
			
		||||
		object_path = None
 | 
			
		||||
 | 
			
		||||
	to_remove = []
 | 
			
		||||
	if refresh:
 | 
			
		||||
		to_remove = list(existing_paths.keys())
 | 
			
		||||
		for k in list(existing_paths.keys()):
 | 
			
		||||
			cfg.om.remove_object(cfg.om.get_object_by_path(k), True)
 | 
			
		||||
			num_changes += 1
 | 
			
		||||
 | 
			
		||||
	num_changes += len(rc)
 | 
			
		||||
 | 
			
		||||
	return rc, num_changes, to_remove
 | 
			
		||||
	return rc, num_changes
 | 
			
		||||
 
 | 
			
		||||
@@ -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 .utils import round_size
 | 
			
		||||
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')
 | 
			
		||||
@@ -346,36 +272,6 @@ class LvCommon(AutomatedProperties):
 | 
			
		||||
		self.state = object_state
 | 
			
		||||
		self._move_pv = self._get_move_pv()
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def handle_execute(rc, out, err):
 | 
			
		||||
		_handle_execute(rc, out, err, LV_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def validate_dbus_object(lv_uuid, lv_name):
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
		if not dbo:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(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 +284,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 +303,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 +312,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 +336,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):
 | 
			
		||||
@@ -522,9 +408,25 @@ class Lv(LvCommon):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _remove(lv_uuid, lv_name, remove_options):
 | 
			
		||||
		# 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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			# Remove the LV, if successful then remove from the model
 | 
			
		||||
			rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
 | 
			
		||||
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.om.remove_object(dbo, True)
 | 
			
		||||
				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))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(lv_uuid, lv_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -542,10 +444,24 @@ class Lv(LvCommon):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _rename(lv_uuid, lv_name, new_name, rename_options):
 | 
			
		||||
		# 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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			# Rename the logical volume
 | 
			
		||||
			rc, out, err = cmdhandler.lv_rename(lv_name, new_name,
 | 
			
		||||
												rename_options)
 | 
			
		||||
			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))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(lv_uuid, lv_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -579,25 +495,44 @@ class Lv(LvCommon):
 | 
			
		||||
				pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
 | 
			
		||||
				job_state)
 | 
			
		||||
 | 
			
		||||
		background.cmd_runner(r)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _snap_shot(lv_uuid, lv_name, name, optional_size,
 | 
			
		||||
			snapshot_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
 | 
			
		||||
		# If you specify a size you get a 'thick' snapshot even if
 | 
			
		||||
		# it is a thin lv
 | 
			
		||||
		if not dbo.IsThinVolume:
 | 
			
		||||
			if optional_size == 0:
 | 
			
		||||
				space = dbo.SizeBytes // 80
 | 
			
		||||
				remainder = space % 512
 | 
			
		||||
				optional_size = space + 512 - remainder
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		LvCommon.handle_execute(*cmdhandler.vg_lv_snapshot(
 | 
			
		||||
			lv_name, snapshot_options,name, optional_size))
 | 
			
		||||
		full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(full_name)
 | 
			
		||||
		if dbo:
 | 
			
		||||
			# If you specify a size you get a 'thick' snapshot even if
 | 
			
		||||
			# it is a thin lv
 | 
			
		||||
			if not dbo.IsThinVolume:
 | 
			
		||||
				if optional_size == 0:
 | 
			
		||||
					space = dbo.SizeBytes / 80
 | 
			
		||||
					remainder = space % 512
 | 
			
		||||
					optional_size = space + 512 - remainder
 | 
			
		||||
 | 
			
		||||
			rc, out, err = cmdhandler.vg_lv_snapshot(
 | 
			
		||||
				lv_name, snapshot_options, name, optional_size)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				return_path = '/'
 | 
			
		||||
				full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
 | 
			
		||||
				lvs = load_lvs([full_name], emit_signal=True)[0]
 | 
			
		||||
				for l in lvs:
 | 
			
		||||
					return_path = l.dbus_object_path()
 | 
			
		||||
 | 
			
		||||
				# Refresh self and all included PVs
 | 
			
		||||
				cfg.load(cache_refresh=False)
 | 
			
		||||
				return return_path
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					LV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(lv_uuid, lv_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
@@ -620,23 +555,38 @@ class Lv(LvCommon):
 | 
			
		||||
				resize_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		pv_dests = []
 | 
			
		||||
		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		# If we have PVs, verify them
 | 
			
		||||
		if len(pv_dests_and_ranges):
 | 
			
		||||
			for pr in pv_dests_and_ranges:
 | 
			
		||||
				pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
 | 
			
		||||
				if not pv_dbus_obj:
 | 
			
		||||
					raise dbus.exceptions.DBusException(
 | 
			
		||||
						LV_INTERFACE,
 | 
			
		||||
						'PV Destination (%s) not found' % pr[0])
 | 
			
		||||
		if dbo:
 | 
			
		||||
			# If we have PVs, verify them
 | 
			
		||||
			if len(pv_dests_and_ranges):
 | 
			
		||||
				for pr in pv_dests_and_ranges:
 | 
			
		||||
					pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
 | 
			
		||||
					if not pv_dbus_obj:
 | 
			
		||||
						raise dbus.exceptions.DBusException(
 | 
			
		||||
							LV_INTERFACE,
 | 
			
		||||
							'PV Destination (%s) not found' % pr[0])
 | 
			
		||||
 | 
			
		||||
				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
 | 
			
		||||
					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))
 | 
			
		||||
		return "/"
 | 
			
		||||
			size_change = new_size_bytes - dbo.SizeBytes
 | 
			
		||||
 | 
			
		||||
			rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change,
 | 
			
		||||
												pv_dests, resize_options)
 | 
			
		||||
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				# Refresh what's changed
 | 
			
		||||
				cfg.load()
 | 
			
		||||
				return "/"
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					LV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(lv_uuid, lv_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
@@ -669,10 +619,23 @@ class Lv(LvCommon):
 | 
			
		||||
	def _lv_activate_deactivate(uuid, lv_name, activate, control_flags,
 | 
			
		||||
								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))
 | 
			
		||||
		return '/'
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.activate_deactivate(
 | 
			
		||||
				'lvchange', lv_name, activate, control_flags, options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				dbo.refresh()
 | 
			
		||||
				return '/'
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					LV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, lv_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
@@ -704,10 +667,25 @@ class Lv(LvCommon):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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))
 | 
			
		||||
		return '/'
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
 | 
			
		||||
			rc, out, err = cmdhandler.lv_tag(
 | 
			
		||||
				lv_name, tags_add, tags_del, tag_options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				dbo.refresh()
 | 
			
		||||
				return '/'
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					LV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, lv_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
@@ -743,152 +721,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):
 | 
			
		||||
@@ -911,11 +743,28 @@ class LvThinPool(Lv):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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))
 | 
			
		||||
		full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(full_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		lv_created = '/'
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.lv_lv_create(
 | 
			
		||||
				lv_name, create_options, name, size_bytes)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
 | 
			
		||||
				lvs = load_lvs([full_name], emit_signal=True)[0]
 | 
			
		||||
				for l in lvs:
 | 
			
		||||
					lv_created = l.dbus_object_path()
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					LV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(lv_uuid, lv_name))
 | 
			
		||||
		return lv_created
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=THIN_POOL_INTERFACE,
 | 
			
		||||
@@ -952,13 +801,14 @@ class LvCachePool(Lv):
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
 | 
			
		||||
 | 
			
		||||
		# Make sure we have a dbus object representing cache pool
 | 
			
		||||
		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(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:
 | 
			
		||||
		if dbo and lv_to_cache:
 | 
			
		||||
			fcn = lv_to_cache.lv_full_name()
 | 
			
		||||
			rc, out, err = cmdhandler.lv_cache_lv(
 | 
			
		||||
				dbo.lv_full_name(), fcn, cache_options)
 | 
			
		||||
@@ -966,18 +816,27 @@ class LvCachePool(Lv):
 | 
			
		||||
				# 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.om.remove_object(dbo, emit_signal=True)
 | 
			
		||||
				cfg.om.remove_object(lv_to_cache, emit_signal=True)
 | 
			
		||||
				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)
 | 
			
		||||
			msg = ""
 | 
			
		||||
			if not dbo:
 | 
			
		||||
				dbo += 'CachePool LV with uuid %s and name %s not present!' % \
 | 
			
		||||
					(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
			if not lv_to_cache:
 | 
			
		||||
				dbo += 'LV to cache with object path %s not present!' % \
 | 
			
		||||
					(lv_object_path)
 | 
			
		||||
 | 
			
		||||
			raise dbus.exceptions.DBusException(LV_INTERFACE, msg)
 | 
			
		||||
		return lv_converted
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -1008,25 +867,32 @@ class LvCacheLv(Lv):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache):
 | 
			
		||||
		# Make sure we have a dbus object representing cache pool
 | 
			
		||||
		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
 | 
			
		||||
		# Get current cache name
 | 
			
		||||
		cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
 | 
			
		||||
		if dbo:
 | 
			
		||||
 | 
			
		||||
		rc, out, err = cmdhandler.lv_detach_cache(
 | 
			
		||||
			dbo.lv_full_name(), detach_options, destroy_cache)
 | 
			
		||||
		if rc == 0:
 | 
			
		||||
			# The cache pool gets removed as hidden and put back to
 | 
			
		||||
			# visible, so lets delete
 | 
			
		||||
			mt_remove_dbus_objects((cache_pool, dbo))
 | 
			
		||||
			cfg.load()
 | 
			
		||||
			# Get current cache name
 | 
			
		||||
			cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
 | 
			
		||||
 | 
			
		||||
			uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)
 | 
			
		||||
			rc, out, err = cmdhandler.lv_detach_cache(
 | 
			
		||||
				dbo.lv_full_name(), detach_options, destroy_cache)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				# The cache pool gets removed as hidden and put back to
 | 
			
		||||
				# visible, so lets delete
 | 
			
		||||
				cfg.om.remove_object(cache_pool, emit_signal=True)
 | 
			
		||||
				cfg.om.remove_object(dbo, emit_signal=True)
 | 
			
		||||
				cfg.load()
 | 
			
		||||
 | 
			
		||||
				uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					LV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
				'LV with uuid %s and name %s not present!' %
 | 
			
		||||
				(lv_uuid, lv_name))
 | 
			
		||||
		return uncached_lv_path
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -1060,4 +926,4 @@ class LvSnapShot(Lv):
 | 
			
		||||
							(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
 | 
			
		||||
							merge_options, job_state), cb, cbe, False,
 | 
			
		||||
							job_state)
 | 
			
		||||
		background.cmd_runner(r)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										157
									
								
								daemons/lvmdbusd/lvm_shell_proxy.py.in → daemons/lvmdbusd/lvm_shell_proxy.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										157
									
								
								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.
 | 
			
		||||
#
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
import shlex
 | 
			
		||||
from fcntl import fcntl, F_GETFL, F_SETFL
 | 
			
		||||
import os
 | 
			
		||||
import traceback
 | 
			
		||||
import sys
 | 
			
		||||
@@ -28,8 +29,7 @@ except ImportError:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from lvmdbusd.cfg import LVM_CMD
 | 
			
		||||
from lvmdbusd.utils import log_debug, log_error, add_no_notify, make_non_block,\
 | 
			
		||||
							read_decoded
 | 
			
		||||
from lvmdbusd.utils import log_debug, log_error
 | 
			
		||||
 | 
			
		||||
SHELL_PROMPT = "lvm> "
 | 
			
		||||
 | 
			
		||||
@@ -42,74 +42,59 @@ def _quote_arg(arg):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LVMShellProxy(object):
 | 
			
		||||
 | 
			
		||||
	# Read until we get prompt back and a result
 | 
			
		||||
	# @param: no_output	Caller expects no output to report FD
 | 
			
		||||
	# Returns stdout, report, stderr (report is JSON!)
 | 
			
		||||
	def _read_until_prompt(self, no_output=False):
 | 
			
		||||
	def _read_until_prompt(self):
 | 
			
		||||
		stdout = ""
 | 
			
		||||
		report = ""
 | 
			
		||||
		stderr = ""
 | 
			
		||||
		keep_reading = True
 | 
			
		||||
		extra_passes = 3
 | 
			
		||||
		report_json = {}
 | 
			
		||||
		prev_report_len = 0
 | 
			
		||||
 | 
			
		||||
		# Try reading from all FDs to prevent one from filling up and causing
 | 
			
		||||
		# a hang.  Keep reading until we get the prompt back and the report
 | 
			
		||||
		# FD does not contain valid JSON
 | 
			
		||||
		while keep_reading:
 | 
			
		||||
		# a hang.  We are also assuming that we won't get the lvm prompt back
 | 
			
		||||
		# until we have already received all the output from stderr and the
 | 
			
		||||
		# report descriptor too.
 | 
			
		||||
		while not stdout.endswith(SHELL_PROMPT):
 | 
			
		||||
			try:
 | 
			
		||||
				rd_fd = [
 | 
			
		||||
					self.lvm_shell.stdout.fileno(),
 | 
			
		||||
					self.report_stream.fileno(),
 | 
			
		||||
					self.report_r,
 | 
			
		||||
					self.lvm_shell.stderr.fileno()]
 | 
			
		||||
				ready = select.select(rd_fd, [], [], 2)
 | 
			
		||||
 | 
			
		||||
				for r in ready[0]:
 | 
			
		||||
					if r == self.lvm_shell.stdout.fileno():
 | 
			
		||||
						stdout += read_decoded(self.lvm_shell.stdout)
 | 
			
		||||
					elif r == self.report_stream.fileno():
 | 
			
		||||
						report += read_decoded(self.report_stream)
 | 
			
		||||
						while True:
 | 
			
		||||
							tmp = self.lvm_shell.stdout.read()
 | 
			
		||||
							if tmp:
 | 
			
		||||
								stdout += tmp.decode("utf-8")
 | 
			
		||||
							else:
 | 
			
		||||
								break
 | 
			
		||||
 | 
			
		||||
					elif r == self.report_r:
 | 
			
		||||
						while True:
 | 
			
		||||
							tmp = os.read(self.report_r, 16384)
 | 
			
		||||
							if tmp:
 | 
			
		||||
								report += tmp.decode("utf-8")
 | 
			
		||||
								if len(tmp) != 16384:
 | 
			
		||||
									break
 | 
			
		||||
							else:
 | 
			
		||||
								break
 | 
			
		||||
 | 
			
		||||
					elif r == self.lvm_shell.stderr.fileno():
 | 
			
		||||
						stderr += read_decoded(self.lvm_shell.stderr)
 | 
			
		||||
						while True:
 | 
			
		||||
							tmp = self.lvm_shell.stderr.read()
 | 
			
		||||
							if tmp:
 | 
			
		||||
								stderr += tmp.decode("utf-8")
 | 
			
		||||
							else:
 | 
			
		||||
								break
 | 
			
		||||
 | 
			
		||||
				# Check to see if the lvm process died on us
 | 
			
		||||
				if self.lvm_shell.poll():
 | 
			
		||||
					raise Exception(self.lvm_shell.returncode, "%s" % stderr)
 | 
			
		||||
 | 
			
		||||
				if stdout.endswith(SHELL_PROMPT):
 | 
			
		||||
					if no_output:
 | 
			
		||||
						keep_reading = False
 | 
			
		||||
					else:
 | 
			
		||||
						cur_report_len = len(report)
 | 
			
		||||
						if cur_report_len != 0:
 | 
			
		||||
							# Only bother to parse if we have more data
 | 
			
		||||
							if prev_report_len != cur_report_len:
 | 
			
		||||
								prev_report_len = cur_report_len
 | 
			
		||||
								# Parse the JSON if it's good we are done,
 | 
			
		||||
								# if not we will try to read some more.
 | 
			
		||||
								try:
 | 
			
		||||
									report_json = json.loads(report)
 | 
			
		||||
									keep_reading = False
 | 
			
		||||
								except ValueError:
 | 
			
		||||
									pass
 | 
			
		||||
 | 
			
		||||
						if keep_reading:
 | 
			
		||||
							extra_passes -= 1
 | 
			
		||||
							if extra_passes <= 0:
 | 
			
		||||
								if len(report):
 | 
			
		||||
									raise ValueError("Invalid json: %s" %
 | 
			
		||||
														report)
 | 
			
		||||
								else:
 | 
			
		||||
									raise ValueError(
 | 
			
		||||
										"lvm returned no JSON output!")
 | 
			
		||||
 | 
			
		||||
			except IOError as ioe:
 | 
			
		||||
				log_debug(str(ioe))
 | 
			
		||||
				pass
 | 
			
		||||
 | 
			
		||||
		return stdout, report_json, stderr
 | 
			
		||||
		return stdout, report, stderr
 | 
			
		||||
 | 
			
		||||
	def _write_cmd(self, cmd):
 | 
			
		||||
		cmd_bytes = bytes(cmd, "utf-8")
 | 
			
		||||
@@ -129,10 +114,7 @@ class LVMShellProxy(object):
 | 
			
		||||
		except FileExistsError:
 | 
			
		||||
			pass
 | 
			
		||||
 | 
			
		||||
		# We have to open non-blocking as the other side isn't open until
 | 
			
		||||
		# we actually fork the process.
 | 
			
		||||
		self.report_fd = os.open(tmp_file, os.O_NONBLOCK)
 | 
			
		||||
		self.report_stream = os.fdopen(self.report_fd, 'rb', 0)
 | 
			
		||||
		self.report_r = os.open(tmp_file, os.O_NONBLOCK)
 | 
			
		||||
 | 
			
		||||
		# Setup the environment for using our own socket for reporting
 | 
			
		||||
		local_env = copy.deepcopy(os.environ)
 | 
			
		||||
@@ -143,6 +125,9 @@ class LVMShellProxy(object):
 | 
			
		||||
		# when utilizing the lvm shell.
 | 
			
		||||
		local_env["LVM_LOG_FILE_MAX_LINES"] = "0"
 | 
			
		||||
 | 
			
		||||
		flags = fcntl(self.report_r, F_GETFL)
 | 
			
		||||
		fcntl(self.report_r, F_SETFL, flags | os.O_NONBLOCK)
 | 
			
		||||
 | 
			
		||||
		# run the lvm shell
 | 
			
		||||
		self.lvm_shell = subprocess.Popen(
 | 
			
		||||
			[LVM_CMD + " 32>%s" % tmp_file],
 | 
			
		||||
@@ -150,18 +135,20 @@ class LVMShellProxy(object):
 | 
			
		||||
			stderr=subprocess.PIPE, close_fds=True, shell=True)
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			make_non_block(self.lvm_shell.stdout)
 | 
			
		||||
			make_non_block(self.lvm_shell.stderr)
 | 
			
		||||
			flags = fcntl(self.lvm_shell.stdout, F_GETFL)
 | 
			
		||||
			fcntl(self.lvm_shell.stdout, F_SETFL, flags | os.O_NONBLOCK)
 | 
			
		||||
			flags = fcntl(self.lvm_shell.stderr, F_GETFL)
 | 
			
		||||
			fcntl(self.lvm_shell.stderr, F_SETFL, flags | os.O_NONBLOCK)
 | 
			
		||||
 | 
			
		||||
			# wait for the first prompt
 | 
			
		||||
			errors = self._read_until_prompt(no_output=True)[2]
 | 
			
		||||
			errors = self._read_until_prompt()[2]
 | 
			
		||||
			if errors and len(errors):
 | 
			
		||||
				raise RuntimeError(errors)
 | 
			
		||||
		except:
 | 
			
		||||
			raise
 | 
			
		||||
		finally:
 | 
			
		||||
			# These will get deleted when the FD count goes to zero so we
 | 
			
		||||
			# can be sure to clean up correctly no matter how we finish
 | 
			
		||||
			# These will get deleted when the FD count goes to zero so we can be
 | 
			
		||||
			# sure to clean up correctly no matter how we finish
 | 
			
		||||
			os.unlink(tmp_file)
 | 
			
		||||
			os.rmdir(tmp_dir)
 | 
			
		||||
 | 
			
		||||
@@ -170,32 +157,39 @@ class LVMShellProxy(object):
 | 
			
		||||
		self._write_cmd('lastlog\n')
 | 
			
		||||
 | 
			
		||||
		# read everything from the STDOUT to the next prompt
 | 
			
		||||
		stdout, report_json, stderr = self._read_until_prompt()
 | 
			
		||||
		if 'log' in report_json:
 | 
			
		||||
			error_msg = ""
 | 
			
		||||
			# Walk the entire log array and build an error string
 | 
			
		||||
			for log_entry in report_json['log']:
 | 
			
		||||
				if log_entry['log_type'] == "error":
 | 
			
		||||
					if error_msg:
 | 
			
		||||
						error_msg += ', ' + log_entry['log_message']
 | 
			
		||||
					else:
 | 
			
		||||
						error_msg = log_entry['log_message']
 | 
			
		||||
		stdout, report, stderr = self._read_until_prompt()
 | 
			
		||||
 | 
			
		||||
			return error_msg
 | 
			
		||||
		try:
 | 
			
		||||
			log = json.loads(report)
 | 
			
		||||
 | 
			
		||||
		return 'No error reason provided! (missing "log" section)'
 | 
			
		||||
			if 'log' in log:
 | 
			
		||||
				error_msg = ""
 | 
			
		||||
				# Walk the entire log array and build an error string
 | 
			
		||||
				for log_entry in log['log']:
 | 
			
		||||
					if log_entry['log_type'] == "error":
 | 
			
		||||
						if error_msg:
 | 
			
		||||
							error_msg += ', ' + log_entry['log_message']
 | 
			
		||||
						else:
 | 
			
		||||
							error_msg = log_entry['log_message']
 | 
			
		||||
 | 
			
		||||
				return error_msg
 | 
			
		||||
 | 
			
		||||
			return 'No error reason provided! (missing "log" section)'
 | 
			
		||||
		except ValueError:
 | 
			
		||||
			log_error("Invalid JSON returned from LVM")
 | 
			
		||||
			log_error("BEGIN>>\n%s\n<<END" % report)
 | 
			
		||||
			return "Invalid JSON returned from LVM when retrieving exit code"
 | 
			
		||||
 | 
			
		||||
	def call_lvm(self, argv, debug=False):
 | 
			
		||||
		rc = 1
 | 
			
		||||
		error_msg = ""
 | 
			
		||||
		json_result = ""
 | 
			
		||||
 | 
			
		||||
		if self.lvm_shell.poll():
 | 
			
		||||
			raise Exception(
 | 
			
		||||
				self.lvm_shell.returncode,
 | 
			
		||||
				"Underlying lvm shell process is not present!")
 | 
			
		||||
 | 
			
		||||
		argv = add_no_notify(argv)
 | 
			
		||||
 | 
			
		||||
		# create the command string
 | 
			
		||||
		cmd = " ".join(_quote_arg(arg) for arg in argv)
 | 
			
		||||
		cmd += "\n"
 | 
			
		||||
@@ -204,24 +198,23 @@ class LVMShellProxy(object):
 | 
			
		||||
		self._write_cmd(cmd)
 | 
			
		||||
 | 
			
		||||
		# read everything from the STDOUT to the next prompt
 | 
			
		||||
		stdout, report_json, stderr = self._read_until_prompt()
 | 
			
		||||
		stdout, report, stderr = self._read_until_prompt()
 | 
			
		||||
 | 
			
		||||
		# 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'):
 | 
			
		||||
				rc = 0
 | 
			
		||||
			else:
 | 
			
		||||
				error_msg = self.get_error_msg()
 | 
			
		||||
		if report and len(report):
 | 
			
		||||
			json_result = json.loads(report)
 | 
			
		||||
			if 'log' in json_result:
 | 
			
		||||
				if json_result['log'][-1:][0]['log_ret_code'] == '1':
 | 
			
		||||
					rc = 0
 | 
			
		||||
				else:
 | 
			
		||||
					error_msg = self.get_error_msg()
 | 
			
		||||
 | 
			
		||||
		if debug or rc != 0:
 | 
			
		||||
			log_error(('CMD: %s' % cmd))
 | 
			
		||||
			log_error(("EC = %d" % rc))
 | 
			
		||||
			log_error(("ERROR_MSG=\n %s\n" % error_msg))
 | 
			
		||||
 | 
			
		||||
		return rc, report_json, error_msg
 | 
			
		||||
		return rc, json_result, error_msg
 | 
			
		||||
 | 
			
		||||
	def exit_shell(self):
 | 
			
		||||
		try:
 | 
			
		||||
@@ -258,3 +251,5 @@ if __name__ == "__main__":
 | 
			
		||||
		pass
 | 
			
		||||
	except Exception:
 | 
			
		||||
		traceback.print_exc(file=sys.stdout)
 | 
			
		||||
	finally:
 | 
			
		||||
		print()
 | 
			
		||||
							
								
								
									
										85
									
								
								daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										85
									
								
								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:
 | 
			
		||||
@@ -70,20 +68,6 @@ class DataStore(object):
 | 
			
		||||
		else:
 | 
			
		||||
			table[key] = record
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup):
 | 
			
		||||
		for p in c_pvs.values():
 | 
			
		||||
			# Capture which PVs are associated with which VG
 | 
			
		||||
			if p['vg_uuid'] not in c_pvs_in_vgs:
 | 
			
		||||
				c_pvs_in_vgs[p['vg_uuid']] = []
 | 
			
		||||
 | 
			
		||||
			if p['vg_name']:
 | 
			
		||||
				c_pvs_in_vgs[p['vg_uuid']].append(
 | 
			
		||||
					(p['pv_name'], p['pv_uuid']))
 | 
			
		||||
 | 
			
		||||
			# Lookup for translating between /dev/<name> and pv uuid
 | 
			
		||||
			c_lookup[p['pv_name']] = p['pv_uuid']
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_pvs(_pvs):
 | 
			
		||||
		pvs = sorted(_pvs, key=lambda pk: pk['pv_name'])
 | 
			
		||||
@@ -97,7 +81,18 @@ class DataStore(object):
 | 
			
		||||
				c_pvs, p['pv_uuid'], p,
 | 
			
		||||
				['pvseg_start', 'pvseg_size', 'segtype'])
 | 
			
		||||
 | 
			
		||||
		DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup)
 | 
			
		||||
		for p in c_pvs.values():
 | 
			
		||||
			# Capture which PVs are associated with which VG
 | 
			
		||||
			if p['vg_uuid'] not in c_pvs_in_vgs:
 | 
			
		||||
				c_pvs_in_vgs[p['vg_uuid']] = []
 | 
			
		||||
 | 
			
		||||
			if p['vg_name']:
 | 
			
		||||
				c_pvs_in_vgs[p['vg_uuid']].append(
 | 
			
		||||
					(p['pv_name'], p['pv_uuid']))
 | 
			
		||||
 | 
			
		||||
			# Lookup for translating between /dev/<name> and pv uuid
 | 
			
		||||
			c_lookup[p['pv_name']] = p['pv_uuid']
 | 
			
		||||
 | 
			
		||||
		return c_pvs, c_lookup, c_pvs_in_vgs
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
@@ -137,28 +132,29 @@ class DataStore(object):
 | 
			
		||||
						i['pvseg_size'] = i['pv_pe_count']
 | 
			
		||||
						i['segtype'] = 'free'
 | 
			
		||||
 | 
			
		||||
		DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup)
 | 
			
		||||
		for p in c_pvs.values():
 | 
			
		||||
			# Capture which PVs are associated with which VG
 | 
			
		||||
			if p['vg_uuid'] not in c_pvs_in_vgs:
 | 
			
		||||
				c_pvs_in_vgs[p['vg_uuid']] = []
 | 
			
		||||
 | 
			
		||||
			if p['vg_name']:
 | 
			
		||||
				c_pvs_in_vgs[p['vg_uuid']].append(
 | 
			
		||||
					(p['pv_name'], p['pv_uuid']))
 | 
			
		||||
 | 
			
		||||
			# Lookup for translating between /dev/<name> and pv uuid
 | 
			
		||||
			c_lookup[p['pv_name']] = p['pv_uuid']
 | 
			
		||||
 | 
			
		||||
		return c_pvs, c_lookup, c_pvs_in_vgs
 | 
			
		||||
 | 
			
		||||
	@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 +169,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 +230,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 +251,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 +528,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.
 | 
			
		||||
#
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
from . import cfg
 | 
			
		||||
from . import objectmanager
 | 
			
		||||
from . import utils
 | 
			
		||||
from .cfg import BUS_NAME, BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH
 | 
			
		||||
from .cfg import BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH
 | 
			
		||||
import threading
 | 
			
		||||
from . import cmdhandler
 | 
			
		||||
import time
 | 
			
		||||
@@ -20,8 +20,9 @@ import dbus.mainloop.glib
 | 
			
		||||
from . import lvmdb
 | 
			
		||||
# noinspection PyUnresolvedReferences
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
from .fetch import StateUpdate
 | 
			
		||||
from .fetch import load
 | 
			
		||||
from .manager import Manager
 | 
			
		||||
from .background import background_reaper
 | 
			
		||||
import traceback
 | 
			
		||||
import queue
 | 
			
		||||
from . import udevwatch
 | 
			
		||||
@@ -29,8 +30,7 @@ from .utils import log_debug, log_error
 | 
			
		||||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from .cmdhandler import LvmFlightRecorder, supports_vdo
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from .refresh import handle_external_event, event_complete
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Lvm(objectmanager.ObjectManager):
 | 
			
		||||
@@ -38,16 +38,54 @@ class Lvm(objectmanager.ObjectManager):
 | 
			
		||||
		super(Lvm, self).__init__(object_path, BASE_INTERFACE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _discard_pending_refreshes():
 | 
			
		||||
	# We just handled a refresh, if we have any in the queue they can be
 | 
			
		||||
	# removed because by definition they are older than the refresh we just did.
 | 
			
		||||
	# As we limit the number of refreshes getting into the queue
 | 
			
		||||
	# we should only ever have one to remove.
 | 
			
		||||
	requests = []
 | 
			
		||||
	while not cfg.worker_q.empty():
 | 
			
		||||
		try:
 | 
			
		||||
			r = cfg.worker_q.get(block=False)
 | 
			
		||||
			if r.method != handle_external_event:
 | 
			
		||||
				requests.append(r)
 | 
			
		||||
			else:
 | 
			
		||||
				# Make sure we make this event complete even though it didn't
 | 
			
		||||
				# run, otherwise no other events will get processed
 | 
			
		||||
				event_complete()
 | 
			
		||||
				break
 | 
			
		||||
		except queue.Empty:
 | 
			
		||||
			break
 | 
			
		||||
 | 
			
		||||
	# Any requests we removed, but did not discard need to be re-queued
 | 
			
		||||
	for r in requests:
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def process_request():
 | 
			
		||||
	while cfg.run.value != 0:
 | 
			
		||||
		# noinspection PyBroadException
 | 
			
		||||
		try:
 | 
			
		||||
			req = cfg.worker_q.get(True, 5)
 | 
			
		||||
 | 
			
		||||
			start = cfg.db.num_refreshes
 | 
			
		||||
 | 
			
		||||
			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))
 | 
			
		||||
 | 
			
		||||
			end = cfg.db.num_refreshes
 | 
			
		||||
 | 
			
		||||
			num_refreshes = end - start
 | 
			
		||||
 | 
			
		||||
			if num_refreshes > 0:
 | 
			
		||||
				_discard_pending_refreshes()
 | 
			
		||||
 | 
			
		||||
				if num_refreshes > 1:
 | 
			
		||||
					log_debug(
 | 
			
		||||
						"Inspect method %s for too many refreshes" %
 | 
			
		||||
						(str(req.method)))
 | 
			
		||||
			log_debug("Complete ")
 | 
			
		||||
		except queue.Empty:
 | 
			
		||||
			pass
 | 
			
		||||
		except Exception:
 | 
			
		||||
@@ -55,32 +93,6 @@ def process_request():
 | 
			
		||||
			utils.log_error("process_request exception: \n%s" % st)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_bb_size(value):
 | 
			
		||||
	v = int(value)
 | 
			
		||||
	if v < 0:
 | 
			
		||||
		raise argparse.ArgumentTypeError(
 | 
			
		||||
			"positive integers only ('%s' invalid)" % 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
 | 
			
		||||
@@ -103,12 +115,6 @@ def main():
 | 
			
		||||
		help="Use the lvm shell, not fork & exec lvm",
 | 
			
		||||
		default=False,
 | 
			
		||||
		dest='use_lvm_shell')
 | 
			
		||||
	parser.add_argument(
 | 
			
		||||
		"--blackboxsize",
 | 
			
		||||
		help="Size of the black box flight recorder, 0 to disable",
 | 
			
		||||
		default=10,
 | 
			
		||||
		type=check_bb_size,
 | 
			
		||||
		dest='bb_size')
 | 
			
		||||
 | 
			
		||||
	use_session = os.getenv('LVMDBUSD_USE_SESSION', False)
 | 
			
		||||
 | 
			
		||||
@@ -116,64 +122,53 @@ def main():
 | 
			
		||||
	os.environ["LC_ALL"] = "C"
 | 
			
		||||
 | 
			
		||||
	cfg.args = parser.parse_args()
 | 
			
		||||
	cfg.create_request_entry = RequestEntry
 | 
			
		||||
 | 
			
		||||
	# We create a flight recorder in cmdhandler too, but we replace it here
 | 
			
		||||
	# as the user may be specifying a different size.  The default one in
 | 
			
		||||
	# cmdhandler is for when we are running other code with a different main.
 | 
			
		||||
	cfg.blackbox = LvmFlightRecorder(cfg.args.bb_size)
 | 
			
		||||
 | 
			
		||||
	if cfg.args.use_lvm_shell and not cfg.args.use_json:
 | 
			
		||||
		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)
 | 
			
		||||
	cmdhandler.set_execution(cfg.args.use_lvm_shell)
 | 
			
		||||
 | 
			
		||||
	# 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()
 | 
			
		||||
 | 
			
		||||
	cmdhandler.set_execution(cfg.args.use_lvm_shell)
 | 
			
		||||
 | 
			
		||||
	if use_session:
 | 
			
		||||
		cfg.bus = dbus.SessionBus()
 | 
			
		||||
	else:
 | 
			
		||||
		cfg.bus = dbus.SystemBus()
 | 
			
		||||
	# The base name variable needs to exist for things to work.
 | 
			
		||||
	# noinspection PyUnusedLocal
 | 
			
		||||
	base_name = dbus.service.BusName(BUS_NAME, cfg.bus)
 | 
			
		||||
	base_name = dbus.service.BusName(BASE_INTERFACE, cfg.bus)
 | 
			
		||||
	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.load = load
 | 
			
		||||
 | 
			
		||||
	# Using a thread to process requests, we cannot hang the dbus library
 | 
			
		||||
	# thread that is handling the dbus interface
 | 
			
		||||
	cfg.db = lvmdb.DataStore(cfg.args.use_json)
 | 
			
		||||
 | 
			
		||||
	# Start up thread to monitor pv moves
 | 
			
		||||
	thread_list.append(
 | 
			
		||||
		threading.Thread(target=process_request, name='process_request'))
 | 
			
		||||
		threading.Thread(target=background_reaper, name="pv_move_reaper"))
 | 
			
		||||
 | 
			
		||||
	# Have a single thread handling updating lvm and the dbus model so we
 | 
			
		||||
	# don't have multiple threads doing this as the same time
 | 
			
		||||
	updater = StateUpdate()
 | 
			
		||||
	thread_list.append(updater.thread)
 | 
			
		||||
 | 
			
		||||
	cfg.load = updater.load
 | 
			
		||||
	# Using a thread to process requests.
 | 
			
		||||
	thread_list.append(threading.Thread(target=process_request))
 | 
			
		||||
 | 
			
		||||
	cfg.load(refresh=False, emit_signal=False)
 | 
			
		||||
	cfg.loop = GLib.MainLoop()
 | 
			
		||||
 | 
			
		||||
	for thread in thread_list:
 | 
			
		||||
		thread.damon = True
 | 
			
		||||
		thread.start()
 | 
			
		||||
	for process in thread_list:
 | 
			
		||||
		process.damon = True
 | 
			
		||||
		process.start()
 | 
			
		||||
 | 
			
		||||
	# Add udev watching
 | 
			
		||||
	if cfg.args.use_udev:
 | 
			
		||||
@@ -195,10 +190,8 @@ def main():
 | 
			
		||||
			cfg.loop.run()
 | 
			
		||||
			udevwatch.remove()
 | 
			
		||||
 | 
			
		||||
			for thread in thread_list:
 | 
			
		||||
				thread.join()
 | 
			
		||||
			for process in thread_list:
 | 
			
		||||
				process.join()
 | 
			
		||||
	except KeyboardInterrupt:
 | 
			
		||||
		# If we are unable to register signal handler, we will end up here when
 | 
			
		||||
		# the service gets a ^C or a kill -2 <parent pid>
 | 
			
		||||
		utils.handler(signal.SIGINT)
 | 
			
		||||
		utils.handler(signal.SIGINT, None)
 | 
			
		||||
	return 0
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
from .automatedproperties import AutomatedProperties
 | 
			
		||||
 | 
			
		||||
from . import utils
 | 
			
		||||
@@ -13,7 +14,9 @@ from .cfg import MANAGER_INTERFACE
 | 
			
		||||
import dbus
 | 
			
		||||
from . import cfg
 | 
			
		||||
from . import cmdhandler
 | 
			
		||||
from .fetch import load_pvs, load_vgs
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from .refresh import event_add
 | 
			
		||||
from . import udevwatch
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -27,17 +30,7 @@ class Manager(AutomatedProperties):
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Version(self):
 | 
			
		||||
		return dbus.String('1.1.0')
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def handle_execute(rc, out, err):
 | 
			
		||||
		if rc == 0:
 | 
			
		||||
			cfg.load()
 | 
			
		||||
		else:
 | 
			
		||||
			# Need to work on error handling, need consistent
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				MANAGER_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		return dbus.String('1.0.0')
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pv_create(device, create_options):
 | 
			
		||||
@@ -47,11 +40,20 @@ class Manager(AutomatedProperties):
 | 
			
		||||
		pv = cfg.om.get_object_path_by_uuid_lvm_id(device, device)
 | 
			
		||||
		if pv:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				MANAGER_INTERFACE, "PV %s Already exists!" % device)
 | 
			
		||||
				MANAGER_INTERFACE, "PV Already exists!")
 | 
			
		||||
 | 
			
		||||
		created_pv = []
 | 
			
		||||
		rc, out, err = cmdhandler.pv_create(create_options, [device])
 | 
			
		||||
		Manager.handle_execute(rc, out, err)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(device)
 | 
			
		||||
		if rc == 0:
 | 
			
		||||
			pvs = load_pvs([device], emit_signal=True)[0]
 | 
			
		||||
			for p in pvs:
 | 
			
		||||
				created_pv = p.dbus_object_path()
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				MANAGER_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		return created_pv
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
@@ -78,8 +80,20 @@ class Manager(AutomatedProperties):
 | 
			
		||||
					MANAGER_INTERFACE, 'object path = %s not found' % p)
 | 
			
		||||
 | 
			
		||||
		rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name)
 | 
			
		||||
		Manager.handle_execute(rc, out, err)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(name)
 | 
			
		||||
		created_vg = "/"
 | 
			
		||||
 | 
			
		||||
		if rc == 0:
 | 
			
		||||
			vgs = load_vgs([name], emit_signal=True)[0]
 | 
			
		||||
			for v in vgs:
 | 
			
		||||
				created_vg = v.dbus_object_path()
 | 
			
		||||
 | 
			
		||||
			# Update the PVS
 | 
			
		||||
			load_pvs(refresh=True, emit_signal=True, cache_refresh=False)
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				MANAGER_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		return created_vg
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
@@ -107,10 +121,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(
 | 
			
		||||
@@ -131,28 +145,11 @@ class Manager(AutomatedProperties):
 | 
			
		||||
		r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE)
 | 
			
		||||
	def FlightRecorderDump(self):
 | 
			
		||||
		"""
 | 
			
		||||
		Dump the flight recorder to syslog
 | 
			
		||||
		"""
 | 
			
		||||
		cfg.blackbox.dump()
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _lookup_by_lvm_id(key):
 | 
			
		||||
		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key)
 | 
			
		||||
		if not p:
 | 
			
		||||
			p = '/'
 | 
			
		||||
		utils.log_debug('LookUpByLvmId: key = %s, result = %s' % (key, p))
 | 
			
		||||
		return p
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='s',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def LookUpByLvmId(self, key, cb, cbe):
 | 
			
		||||
		out_signature='o')
 | 
			
		||||
	def LookUpByLvmId(self, key):
 | 
			
		||||
		"""
 | 
			
		||||
		Given a lvm id in one of the forms:
 | 
			
		||||
 | 
			
		||||
@@ -164,54 +161,37 @@ class Manager(AutomatedProperties):
 | 
			
		||||
		return the object path in O(1) time.
 | 
			
		||||
 | 
			
		||||
		:param key: The lookup value
 | 
			
		||||
		:param cb:	dbus python call back parameter, not client visible
 | 
			
		||||
		:param cbe:	dbus python error call back parameter, not client visible
 | 
			
		||||
		:return: Return the object path.  If object not found you will get '/'
 | 
			
		||||
		"""
 | 
			
		||||
		r = RequestEntry(-1, Manager._lookup_by_lvm_id, (key,), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _use_lvm_shell(yes_no):
 | 
			
		||||
		return dbus.Boolean(cmdhandler.set_execution(yes_no))
 | 
			
		||||
		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key)
 | 
			
		||||
		if p:
 | 
			
		||||
			return p
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='b', out_signature='b',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def UseLvmShell(self, yes_no, cb, cbe):
 | 
			
		||||
		in_signature='b', out_signature='b')
 | 
			
		||||
	def UseLvmShell(self, yes_no):
 | 
			
		||||
		"""
 | 
			
		||||
		Allow the client to enable/disable lvm shell, used for testing
 | 
			
		||||
		:param yes_no:
 | 
			
		||||
		:param cb:	dbus python call back parameter, not client visible
 | 
			
		||||
		:param cbe:	dbus python error call back parameter, not client visible
 | 
			
		||||
		:return: Boolean
 | 
			
		||||
		:return: Nothing
 | 
			
		||||
		"""
 | 
			
		||||
		r = RequestEntry(-1, Manager._use_lvm_shell, (yes_no,), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _external_event(command):
 | 
			
		||||
		utils.log_debug("Processing _external_event= %s" % command,
 | 
			
		||||
							'bg_black', 'fg_orange')
 | 
			
		||||
		cfg.load()
 | 
			
		||||
		return dbus.Boolean(cmdhandler.set_execution(yes_no))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='s', out_signature='i')
 | 
			
		||||
	def ExternalEvent(self, command):
 | 
			
		||||
		utils.log_debug("ExternalEvent %s" % command)
 | 
			
		||||
 | 
			
		||||
		# If a user didn't explicitly specify udev, we will turn it off now.
 | 
			
		||||
		if not cfg.args.use_udev:
 | 
			
		||||
			if udevwatch.remove():
 | 
			
		||||
				utils.log_debug("ExternalEvent received, disabling "
 | 
			
		||||
								"udev monitoring")
 | 
			
		||||
				# We are dependent on external events now to stay current!
 | 
			
		||||
				cfg.got_external_event = True
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			-1, Manager._external_event, (command,), None, None, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
				cfg.ee = True
 | 
			
		||||
		event_add((command,))
 | 
			
		||||
		return dbus.Int32(0)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
@@ -221,8 +201,15 @@ class Manager(AutomatedProperties):
 | 
			
		||||
			activate, cache, device_path,
 | 
			
		||||
			major_minor, scan_options)
 | 
			
		||||
 | 
			
		||||
		Manager.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
		if rc == 0:
 | 
			
		||||
			# This could potentially change the state quite a bit, so lets
 | 
			
		||||
			# update everything to be safe
 | 
			
		||||
			cfg.load()
 | 
			
		||||
			return '/'
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				MANAGER_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
 
 | 
			
		||||
@@ -32,12 +32,14 @@ class ObjectManager(AutomatedProperties):
 | 
			
		||||
		self._id_to_object_path = {}
 | 
			
		||||
		self.rlock = threading.RLock()
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _get_managed_objects(obj):
 | 
			
		||||
		with obj.rlock:
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface="org.freedesktop.DBus.ObjectManager",
 | 
			
		||||
		out_signature='a{oa{sa{sv}}}')
 | 
			
		||||
	def GetManagedObjects(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			rc = {}
 | 
			
		||||
			try:
 | 
			
		||||
				for k, v in list(obj._objects.items()):
 | 
			
		||||
				for k, v in list(self._objects.items()):
 | 
			
		||||
					path, props = v[0].emit_data()
 | 
			
		||||
					rc[path] = props
 | 
			
		||||
			except Exception:
 | 
			
		||||
@@ -45,14 +47,6 @@ class ObjectManager(AutomatedProperties):
 | 
			
		||||
				sys.exit(1)
 | 
			
		||||
			return rc
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface="org.freedesktop.DBus.ObjectManager",
 | 
			
		||||
		out_signature='a{oa{sa{sv}}}', async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def GetManagedObjects(self, cb, cbe):
 | 
			
		||||
		r = cfg.create_request_entry(-1, ObjectManager._get_managed_objects,
 | 
			
		||||
									(self, ), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	def locked(self):
 | 
			
		||||
		"""
 | 
			
		||||
		If some external code need to run across a number of different
 | 
			
		||||
@@ -189,8 +183,8 @@ class ObjectManager(AutomatedProperties):
 | 
			
		||||
			path = dbus_object.dbus_object_path()
 | 
			
		||||
			interfaces = dbus_object.interface()
 | 
			
		||||
 | 
			
		||||
			# print('UN-Registering object path %s for %s' %
 | 
			
		||||
			#		(path, dbus_object.lvm_id))
 | 
			
		||||
			# print 'UN-Registering object path %s for %s' % \
 | 
			
		||||
			#      (path, dbus_object.lvm_id)
 | 
			
		||||
 | 
			
		||||
			self._lookup_remove(path)
 | 
			
		||||
 | 
			
		||||
@@ -223,9 +217,8 @@ class ObjectManager(AutomatedProperties):
 | 
			
		||||
		:param lvm_id: The lvm identifier
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			lookup_rc = self._id_lookup(lvm_id)
 | 
			
		||||
			if lookup_rc:
 | 
			
		||||
				return self.get_object_by_path(lookup_rc)
 | 
			
		||||
			if lvm_id in self._id_to_object_path:
 | 
			
		||||
				return self.get_object_by_path(self._id_to_object_path[lvm_id])
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
	def get_object_path_by_lvm_id(self, lvm_id):
 | 
			
		||||
@@ -235,24 +228,43 @@ class ObjectManager(AutomatedProperties):
 | 
			
		||||
		:return: Object path or '/' if not found
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			lookup_rc = self._id_lookup(lvm_id)
 | 
			
		||||
			if lookup_rc:
 | 
			
		||||
				return lookup_rc
 | 
			
		||||
			if lvm_id in self._id_to_object_path:
 | 
			
		||||
				return self._id_to_object_path[lvm_id]
 | 
			
		||||
			return '/'
 | 
			
		||||
 | 
			
		||||
	def _id_verify(self, path, uuid, lvm_id):
 | 
			
		||||
	def _uuid_verify(self, path, uuid, lvm_id):
 | 
			
		||||
		"""
 | 
			
		||||
		Ensure our lookups are correct
 | 
			
		||||
		Ensure uuid is present for a successful lvm_id lookup
 | 
			
		||||
		NOTE: Internal call, assumes under object manager lock
 | 
			
		||||
		:param path: 		Path to object we looked up
 | 
			
		||||
		:param uuid: 		uuid lookup
 | 
			
		||||
		:param lvm_id:		lvm_id lookup
 | 
			
		||||
		:param uuid: 		lvm uuid to verify
 | 
			
		||||
		:param lvm_id:		lvm_id used to find object
 | 
			
		||||
		:return: None
 | 
			
		||||
		"""
 | 
			
		||||
		# There is no durable non-changeable name in lvm
 | 
			
		||||
		# This gets called when we found an object based on lvm_id, ensure
 | 
			
		||||
		# uuid is correct too, as they can change. There is no durable
 | 
			
		||||
		# non-changeable name in lvm
 | 
			
		||||
		if lvm_id != uuid:
 | 
			
		||||
			obj = self.get_object_by_path(path)
 | 
			
		||||
			self._lookup_add(obj, path, lvm_id, uuid)
 | 
			
		||||
			if uuid and uuid not in self._id_to_object_path:
 | 
			
		||||
				obj = self.get_object_by_path(path)
 | 
			
		||||
				self._lookup_add(obj, path, lvm_id, uuid)
 | 
			
		||||
 | 
			
		||||
	def _lvm_id_verify(self, path, uuid, lvm_id):
 | 
			
		||||
		"""
 | 
			
		||||
		Ensure lvm_id is present for a successful uuid lookup
 | 
			
		||||
		NOTE: Internal call, assumes under object manager lock
 | 
			
		||||
		:param path: 		Path to object we looked up
 | 
			
		||||
		:param uuid: 		uuid used to find object
 | 
			
		||||
		:param lvm_id:		lvm_id to verify
 | 
			
		||||
		:return: None
 | 
			
		||||
		"""
 | 
			
		||||
		# This gets called when we found an object based on uuid, ensure
 | 
			
		||||
		# lvm_id is correct too, as they can change.  There is no durable
 | 
			
		||||
		# non-changeable name in lvm
 | 
			
		||||
		if lvm_id != uuid:
 | 
			
		||||
			if lvm_id and lvm_id not in self._id_to_object_path:
 | 
			
		||||
				obj = self.get_object_by_path(path)
 | 
			
		||||
				self._lookup_add(obj, path, lvm_id, uuid)
 | 
			
		||||
 | 
			
		||||
	def _id_lookup(self, the_id):
 | 
			
		||||
		path = None
 | 
			
		||||
@@ -319,22 +331,22 @@ class ObjectManager(AutomatedProperties):
 | 
			
		||||
				# Lets check for the uuid first
 | 
			
		||||
				path = self._id_lookup(uuid)
 | 
			
		||||
				if path:
 | 
			
		||||
					# Ensure table lookups are correct
 | 
			
		||||
					self._id_verify(path, uuid, lvm_id)
 | 
			
		||||
					# Verify the lvm_id is sane
 | 
			
		||||
					self._lvm_id_verify(path, uuid, lvm_id)
 | 
			
		||||
				else:
 | 
			
		||||
					# Unable to find by UUID, lets lookup by lvm_id
 | 
			
		||||
					path = self._id_lookup(lvm_id)
 | 
			
		||||
					if path:
 | 
			
		||||
						# Ensure table lookups are correct
 | 
			
		||||
						self._id_verify(path, uuid, lvm_id)
 | 
			
		||||
						# Verify the uuid is sane
 | 
			
		||||
						self._uuid_verify(path, uuid, lvm_id)
 | 
			
		||||
					else:
 | 
			
		||||
						# We have exhausted all lookups, let's create if we can
 | 
			
		||||
						if path_create:
 | 
			
		||||
							path = path_create()
 | 
			
		||||
							self._lookup_add(None, path, lvm_id, uuid)
 | 
			
		||||
 | 
			
		||||
			# print('get_object_path_by_lvm_id(%s, %s, %s): return %s' %
 | 
			
		||||
			#	(uuid, lvm_id, str(path_create), path))
 | 
			
		||||
			# print('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' %
 | 
			
		||||
			# 	   (uuid, lvm_id, str(path_create), str(gen_new), path))
 | 
			
		||||
 | 
			
		||||
			return path
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
@@ -137,23 +135,23 @@ class Pv(AutomatedProperties):
 | 
			
		||||
	def _remove(pv_uuid, pv_name, remove_options):
 | 
			
		||||
		# 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))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def handle_execute(rc, out, err):
 | 
			
		||||
		return _handle_execute(rc, out, err, PV_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def validate_dbus_object(pv_uuid, pv_name):
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
 | 
			
		||||
		if not dbo:
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.om.remove_object(dbo, True)
 | 
			
		||||
			else:
 | 
			
		||||
				# Need to work on error handling, need consistent
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					PV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				PV_INTERFACE,
 | 
			
		||||
				'PV with uuid %s and name %s not present!' %
 | 
			
		||||
				(pv_uuid, pv_name))
 | 
			
		||||
		return dbo
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=PV_INTERFACE,
 | 
			
		||||
@@ -170,9 +168,22 @@ class Pv(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
 | 
			
		||||
												resize_options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				dbo.refresh()
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					PV_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				PV_INTERFACE,
 | 
			
		||||
				'PV with uuid %s and name %s not present!' %
 | 
			
		||||
				(pv_uuid, pv_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -190,9 +201,21 @@ class Pv(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.pv_allocatable(
 | 
			
		||||
				pv_name, yes_no, allocation_options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.load()
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					PV_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				PV_INTERFACE,
 | 
			
		||||
				'PV with uuid %s and name %s not present!' %
 | 
			
		||||
				(pv_uuid, pv_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								daemons/lvmdbusd/refresh.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								daemons/lvmdbusd/refresh.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
# This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
# modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
# of the GNU General Public License v.2.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
# Try and minimize the refreshes we do.
 | 
			
		||||
 | 
			
		||||
import threading
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from . import cfg
 | 
			
		||||
from . import utils
 | 
			
		||||
 | 
			
		||||
_rlock = threading.RLock()
 | 
			
		||||
_count = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def handle_external_event(command):
 | 
			
		||||
	utils.log_debug("External event: '%s'" % command)
 | 
			
		||||
	event_complete()
 | 
			
		||||
	cfg.load()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def event_add(params):
 | 
			
		||||
	global _rlock
 | 
			
		||||
	global _count
 | 
			
		||||
	with _rlock:
 | 
			
		||||
		if _count == 0:
 | 
			
		||||
			_count += 1
 | 
			
		||||
			r = RequestEntry(
 | 
			
		||||
				-1, handle_external_event,
 | 
			
		||||
				params, None, None, False)
 | 
			
		||||
			cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def event_complete():
 | 
			
		||||
	global _rlock
 | 
			
		||||
	global _count
 | 
			
		||||
	with _rlock:
 | 
			
		||||
		if _count > 0:
 | 
			
		||||
			_count -= 1
 | 
			
		||||
		return _count
 | 
			
		||||
@@ -13,12 +13,13 @@ 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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RequestEntry(object):
 | 
			
		||||
	def __init__(self, tmo, method, arguments, cb, cb_error,
 | 
			
		||||
			return_tuple=True, job_state=None):
 | 
			
		||||
		self.tmo = tmo
 | 
			
		||||
		self.method = method
 | 
			
		||||
		self.arguments = arguments
 | 
			
		||||
		self.cb = cb
 | 
			
		||||
@@ -34,32 +35,25 @@ class RequestEntry(object):
 | 
			
		||||
		self._return_tuple = return_tuple
 | 
			
		||||
		self._job_state = job_state
 | 
			
		||||
 | 
			
		||||
		if tmo < 0:
 | 
			
		||||
		if self.tmo < 0:
 | 
			
		||||
			# Client is willing to block forever
 | 
			
		||||
			pass
 | 
			
		||||
		elif tmo == 0:
 | 
			
		||||
			self._return_job()
 | 
			
		||||
		else:
 | 
			
		||||
			# Note: using 990 instead of 1000 for second to ms conversion to
 | 
			
		||||
			# account for overhead.  Goal is to return just before the
 | 
			
		||||
			# timeout amount has expired.  Better to be a little early than
 | 
			
		||||
			# late.
 | 
			
		||||
			self.timer_id = GLib.timeout_add(
 | 
			
		||||
				tmo * 990, RequestEntry._request_timeout, self)
 | 
			
		||||
			self.timer_id = GLib.timeout_add_seconds(
 | 
			
		||||
				tmo, RequestEntry._request_timeout, self)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _request_timeout(r):
 | 
			
		||||
		"""
 | 
			
		||||
		Method which gets called when the timer runs out!
 | 
			
		||||
		:param r:  RequestEntry which timed out
 | 
			
		||||
		:return: Result of timer_expired
 | 
			
		||||
		:return: Nothing
 | 
			
		||||
		"""
 | 
			
		||||
		return r.timer_expired()
 | 
			
		||||
		r.timer_expired()
 | 
			
		||||
 | 
			
		||||
	def _return_job(self):
 | 
			
		||||
		# Return job is only called when we create a request object or when
 | 
			
		||||
		# we pop a timer.  In both cases we are running in the correct context
 | 
			
		||||
		# and do not need to schedule the call back in main context.
 | 
			
		||||
		self._job = Job(self, self._job_state)
 | 
			
		||||
		cfg.om.register_object(self._job, True)
 | 
			
		||||
		if self._return_tuple:
 | 
			
		||||
@@ -116,9 +110,9 @@ class RequestEntry(object):
 | 
			
		||||
				if error_rc == 0:
 | 
			
		||||
					if self.cb:
 | 
			
		||||
						if self._return_tuple:
 | 
			
		||||
							mt_async_call(self.cb, (result, '/'))
 | 
			
		||||
							self.cb((result, '/'))
 | 
			
		||||
						else:
 | 
			
		||||
							mt_async_call(self.cb, result)
 | 
			
		||||
							self.cb(result)
 | 
			
		||||
				else:
 | 
			
		||||
					if self.cb_error:
 | 
			
		||||
						if not error_exception:
 | 
			
		||||
@@ -129,9 +123,10 @@ class RequestEntry(object):
 | 
			
		||||
							else:
 | 
			
		||||
								error_exception = Exception(error_msg)
 | 
			
		||||
 | 
			
		||||
						mt_async_call(self.cb_error, error_exception)
 | 
			
		||||
						self.cb_error(error_exception)
 | 
			
		||||
			else:
 | 
			
		||||
				# We have a job and it's complete, indicate that it's done.
 | 
			
		||||
				# TODO: We need to signal the job is done too.
 | 
			
		||||
				self._job.Complete = True
 | 
			
		||||
				self._job = None
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,42 +9,12 @@
 | 
			
		||||
 | 
			
		||||
import pyudev
 | 
			
		||||
import threading
 | 
			
		||||
from .refresh import event_add
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from . import utils
 | 
			
		||||
 | 
			
		||||
observer = None
 | 
			
		||||
observer_lock = threading.RLock()
 | 
			
		||||
 | 
			
		||||
_udev_lock = threading.RLock()
 | 
			
		||||
_udev_count = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def udev_add():
 | 
			
		||||
	global _udev_count
 | 
			
		||||
	with _udev_lock:
 | 
			
		||||
		if _udev_count == 0:
 | 
			
		||||
			_udev_count += 1
 | 
			
		||||
 | 
			
		||||
			# Place this on the queue so any other operations will sequence
 | 
			
		||||
			# behind it
 | 
			
		||||
			r = RequestEntry(
 | 
			
		||||
				-1, _udev_event, (), None, None, False)
 | 
			
		||||
			cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def udev_complete():
 | 
			
		||||
	global _udev_count
 | 
			
		||||
	with _udev_lock:
 | 
			
		||||
		if _udev_count > 0:
 | 
			
		||||
			_udev_count -= 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _udev_event():
 | 
			
		||||
	utils.log_debug("Processing udev event")
 | 
			
		||||
	udev_complete()
 | 
			
		||||
	cfg.load()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyUnusedLocal
 | 
			
		||||
def filter_event(action, device):
 | 
			
		||||
@@ -52,8 +22,8 @@ def filter_event(action, device):
 | 
			
		||||
	# when appropriate.
 | 
			
		||||
	refresh = False
 | 
			
		||||
 | 
			
		||||
	if 'ID_FS_TYPE' in device:
 | 
			
		||||
		fs_type_new = device['ID_FS_TYPE']
 | 
			
		||||
	if '.ID_FS_TYPE_NEW' in device:
 | 
			
		||||
		fs_type_new = device['.ID_FS_TYPE_NEW']
 | 
			
		||||
 | 
			
		||||
		if 'LVM' in fs_type_new:
 | 
			
		||||
			refresh = True
 | 
			
		||||
@@ -68,7 +38,7 @@ def filter_event(action, device):
 | 
			
		||||
		refresh = True
 | 
			
		||||
 | 
			
		||||
	if refresh:
 | 
			
		||||
		udev_add()
 | 
			
		||||
		event_add(('udev',))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add():
 | 
			
		||||
 
 | 
			
		||||
@@ -14,28 +14,14 @@ import ctypes
 | 
			
		||||
import os
 | 
			
		||||
import string
 | 
			
		||||
import datetime
 | 
			
		||||
from fcntl import fcntl, F_GETFL, F_SETFL
 | 
			
		||||
 | 
			
		||||
import dbus
 | 
			
		||||
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
 | 
			
		||||
@@ -67,20 +53,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
 | 
			
		||||
@@ -91,17 +65,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):
 | 
			
		||||
@@ -313,47 +278,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():
 | 
			
		||||
@@ -369,8 +299,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
 | 
			
		||||
 | 
			
		||||
@@ -388,10 +316,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)
 | 
			
		||||
 | 
			
		||||
@@ -483,7 +407,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._-+/=!:&#
 | 
			
		||||
@@ -570,128 +494,3 @@ def validate_tag(interface, tag):
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			interface, 'tag (%s) contains invalid character, allowable set(%s)'
 | 
			
		||||
			% (tag, _ALLOWABLE_TAG_CH))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_no_notify(cmdline):
 | 
			
		||||
	"""
 | 
			
		||||
	Given a command line to execute we will see if `--config` is present, if it
 | 
			
		||||
	is we will add the global/notify_dbus=0 to it, otherwise we will append it
 | 
			
		||||
	to the end of the list.
 | 
			
		||||
	:param: cmdline: The command line to inspect
 | 
			
		||||
	:type: cmdline: list
 | 
			
		||||
	:return: cmdline with notify_dbus config option present
 | 
			
		||||
	:rtype: list
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	# Only after we have seen an external event will be disable lvm from sending
 | 
			
		||||
	# us one when we call lvm
 | 
			
		||||
	if cfg.got_external_event:
 | 
			
		||||
		if 'help' in cmdline:
 | 
			
		||||
			return cmdline
 | 
			
		||||
 | 
			
		||||
		if '--config' in cmdline:
 | 
			
		||||
			for i, arg in enumerate(cmdline):
 | 
			
		||||
				if arg == '--config':
 | 
			
		||||
					if len(cmdline) <= i+1:
 | 
			
		||||
						raise dbus.exceptions.DBusException("Missing value for --config option.")
 | 
			
		||||
					cmdline[i+1] += " global/notify_dbus=0"
 | 
			
		||||
					break
 | 
			
		||||
		else:
 | 
			
		||||
			cmdline.extend(['--config', 'global/notify_dbus=0'])
 | 
			
		||||
	return cmdline
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# The methods below which start with mt_* are used to execute the desired code
 | 
			
		||||
# on the the main thread of execution to alleviate any issues the dbus-python
 | 
			
		||||
# library with regards to multi-threaded access.  Essentially, we are trying to
 | 
			
		||||
# ensure all dbus library interaction is done from the same thread!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _async_handler(call_back, parameters):
 | 
			
		||||
	params_str = ", ".join(str(x) for x in parameters)
 | 
			
		||||
	log_debug('Main thread execution, callback = %s, parameters = (%s)' %
 | 
			
		||||
				(str(call_back), params_str))
 | 
			
		||||
 | 
			
		||||
	try:
 | 
			
		||||
		if parameters:
 | 
			
		||||
			call_back(*parameters)
 | 
			
		||||
		else:
 | 
			
		||||
			call_back()
 | 
			
		||||
	except:
 | 
			
		||||
		st = traceback.format_exc()
 | 
			
		||||
		log_error("mt_async_call: exception (logged, not reported!) \n %s" % st)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
# Example:
 | 
			
		||||
# result = MThreadRunner(foo, arg1, arg2).done()
 | 
			
		||||
#
 | 
			
		||||
class MThreadRunner(object):
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def runner(obj):
 | 
			
		||||
		# noinspection PyProtectedMember
 | 
			
		||||
		obj._run()
 | 
			
		||||
		with obj.cond:
 | 
			
		||||
			obj.function_complete = True
 | 
			
		||||
			obj.cond.notify_all()
 | 
			
		||||
 | 
			
		||||
	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())
 | 
			
		||||
 | 
			
		||||
	def done(self):
 | 
			
		||||
		GLib.idle_add(MThreadRunner.runner, self)
 | 
			
		||||
		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!")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _remove_objects(dbus_objects_rm):
 | 
			
		||||
	for o in dbus_objects_rm:
 | 
			
		||||
		cfg.om.remove_object(o, emit_signal=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Remove dbus objects from main thread
 | 
			
		||||
def mt_remove_dbus_objects(objs):
 | 
			
		||||
	MThreadRunner(_remove_objects, objs).done()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Make stream non-blocking
 | 
			
		||||
def make_non_block(stream):
 | 
			
		||||
	flags = fcntl(stream, F_GETFL)
 | 
			
		||||
	fcntl(stream, F_SETFL, flags | os.O_NONBLOCK)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_decoded(stream):
 | 
			
		||||
	tmp = stream.read()
 | 
			
		||||
	if tmp:
 | 
			
		||||
		return tmp.decode("utf-8")
 | 
			
		||||
	return ''
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,16 @@
 | 
			
		||||
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
 | 
			
		||||
from .state import State
 | 
			
		||||
from . import background
 | 
			
		||||
from .utils import round_size, mt_remove_dbus_objects
 | 
			
		||||
from .utils import round_size
 | 
			
		||||
from .job import JobState
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -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):
 | 
			
		||||
@@ -155,28 +145,29 @@ class Vg(AutomatedProperties):
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def fetch_new_lv(vg_name, lv_name):
 | 
			
		||||
		cfg.load()
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id("%s/%s" % (vg_name, lv_name))
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def handle_execute(rc, out, err):
 | 
			
		||||
		return _handle_execute(rc, out, err, VG_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def validate_dbus_object(vg_uuid, vg_name):
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(vg_uuid, vg_name)
 | 
			
		||||
		if not dbo:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(vg_uuid, vg_name))
 | 
			
		||||
		return dbo
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.vg_rename(vg_name, new_name,
 | 
			
		||||
												rename_options)
 | 
			
		||||
			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))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -193,9 +184,31 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _remove(uuid, vg_name, remove_options):
 | 
			
		||||
		# 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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			# Remove the VG, if successful then remove from the model
 | 
			
		||||
			rc, out, err = cmdhandler.vg_remove(vg_name, remove_options)
 | 
			
		||||
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				# Remove the VG
 | 
			
		||||
				cfg.om.remove_object(dbo, True)
 | 
			
		||||
 | 
			
		||||
				# If an LV has hidden LVs, things can get quite involved,
 | 
			
		||||
				# especially if it's the last thin pool to get removed, so
 | 
			
		||||
				# lets refresh all
 | 
			
		||||
				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))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -210,14 +223,32 @@ 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))
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.vg_change(change_options, vg_name)
 | 
			
		||||
 | 
			
		||||
			# To use an example with d-feet (Method input)
 | 
			
		||||
			# {"activate": __import__('gi.repository.GLib', globals(),
 | 
			
		||||
			# locals(), ['Variant']).Variant("s", "n")}
 | 
			
		||||
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.load()
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
		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}',
 | 
			
		||||
@@ -232,23 +263,34 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		Vg.validate_dbus_object(uuid, vg_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		pv_devices = []
 | 
			
		||||
		if dbo:
 | 
			
		||||
			pv_devices = []
 | 
			
		||||
 | 
			
		||||
		# If pv_object_paths is not empty, then get the device paths
 | 
			
		||||
		if pv_object_paths and len(pv_object_paths) > 0:
 | 
			
		||||
			for pv_op in pv_object_paths:
 | 
			
		||||
				pv = cfg.om.get_object_by_path(pv_op)
 | 
			
		||||
				if pv:
 | 
			
		||||
					pv_devices.append(pv.lvm_id)
 | 
			
		||||
				else:
 | 
			
		||||
					raise dbus.exceptions.DBusException(
 | 
			
		||||
						VG_INTERFACE,
 | 
			
		||||
						'PV Object path not found = %s!' % pv_op)
 | 
			
		||||
			# If pv_object_paths is not empty, then get the device paths
 | 
			
		||||
			if pv_object_paths and len(pv_object_paths) > 0:
 | 
			
		||||
				for pv_op in pv_object_paths:
 | 
			
		||||
					pv = cfg.om.get_object_by_path(pv_op)
 | 
			
		||||
					if pv:
 | 
			
		||||
						pv_devices.append(pv.lvm_id)
 | 
			
		||||
					else:
 | 
			
		||||
						raise dbus.exceptions.DBusException(
 | 
			
		||||
							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)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.load()
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -265,25 +307,36 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _extend(uuid, vg_name, pv_object_paths, extend_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		Vg.validate_dbus_object(uuid, vg_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		extend_devices = []
 | 
			
		||||
		if dbo:
 | 
			
		||||
			extend_devices = []
 | 
			
		||||
 | 
			
		||||
		for i in pv_object_paths:
 | 
			
		||||
			pv = cfg.om.get_object_by_path(i)
 | 
			
		||||
			if pv:
 | 
			
		||||
				extend_devices.append(pv.lvm_id)
 | 
			
		||||
			for i in pv_object_paths:
 | 
			
		||||
				pv = cfg.om.get_object_by_path(i)
 | 
			
		||||
				if pv:
 | 
			
		||||
					extend_devices.append(pv.lvm_id)
 | 
			
		||||
				else:
 | 
			
		||||
					raise dbus.exceptions.DBusException(
 | 
			
		||||
						VG_INTERFACE, 'PV Object path not found = %s!' % i)
 | 
			
		||||
 | 
			
		||||
			if len(extend_devices):
 | 
			
		||||
				rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices,
 | 
			
		||||
													extend_options)
 | 
			
		||||
				if rc == 0:
 | 
			
		||||
					cfg.load()
 | 
			
		||||
				else:
 | 
			
		||||
					raise dbus.exceptions.DBusException(
 | 
			
		||||
						VG_INTERFACE,
 | 
			
		||||
						'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					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))
 | 
			
		||||
					VG_INTERFACE, 'No pv_object_paths provided!')
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE, 'No pv_object_paths provided!')
 | 
			
		||||
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
@@ -320,22 +373,33 @@ class Vg(AutomatedProperties):
 | 
			
		||||
			create_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		pv_dests = []
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		Vg.validate_dbus_object(uuid, vg_name)
 | 
			
		||||
		if dbo:
 | 
			
		||||
			if len(pv_dests_and_ranges):
 | 
			
		||||
				for pr in pv_dests_and_ranges:
 | 
			
		||||
					pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
 | 
			
		||||
					if not pv_dbus_obj:
 | 
			
		||||
						raise dbus.exceptions.DBusException(
 | 
			
		||||
							VG_INTERFACE,
 | 
			
		||||
							'PV Destination (%s) not found' % pr[0])
 | 
			
		||||
 | 
			
		||||
		if len(pv_dests_and_ranges):
 | 
			
		||||
			for pr in pv_dests_and_ranges:
 | 
			
		||||
				pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
 | 
			
		||||
				if not pv_dbus_obj:
 | 
			
		||||
					raise dbus.exceptions.DBusException(
 | 
			
		||||
						VG_INTERFACE,
 | 
			
		||||
						'PV Destination (%s) not found' % pr[0])
 | 
			
		||||
					pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
 | 
			
		||||
 | 
			
		||||
				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
 | 
			
		||||
			rc, out, err = cmdhandler.vg_lv_create(
 | 
			
		||||
				vg_name, create_options, name, size_bytes, pv_dests)
 | 
			
		||||
 | 
			
		||||
		Vg.handle_execute(*cmdhandler.vg_lv_create(
 | 
			
		||||
			vg_name, create_options, name, size_bytes, pv_dests))
 | 
			
		||||
		return Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				return Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -371,10 +435,25 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	def _lv_create_linear(uuid, vg_name, name, size_bytes,
 | 
			
		||||
			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))
 | 
			
		||||
		return Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.vg_lv_create_linear(
 | 
			
		||||
				vg_name, create_options, name, size_bytes, thin_pool)
 | 
			
		||||
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				created_lv = Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
		return created_lv
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -394,11 +473,24 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes,
 | 
			
		||||
			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(
 | 
			
		||||
			vg_name, create_options, name, size_bytes,
 | 
			
		||||
			num_stripes, stripe_size_kb, thin_pool))
 | 
			
		||||
		return Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.vg_lv_create_striped(
 | 
			
		||||
				vg_name, create_options, name, size_bytes,
 | 
			
		||||
				num_stripes, stripe_size_kb, thin_pool)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				created_lv = Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE, 'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
		return created_lv
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -421,10 +513,25 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	def _lv_create_mirror(uuid, vg_name, name, size_bytes,
 | 
			
		||||
			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))
 | 
			
		||||
		return Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.vg_lv_create_mirror(
 | 
			
		||||
				vg_name, create_options, name, size_bytes, num_copies)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				created_lv = Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
		return created_lv
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -445,11 +552,26 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes,
 | 
			
		||||
						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(
 | 
			
		||||
			vg_name, create_options, name, raid_type, size_bytes,
 | 
			
		||||
			num_stripes, stripe_size_kb))
 | 
			
		||||
		return Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.vg_lv_create_raid(
 | 
			
		||||
				vg_name, create_options, name, raid_type, size_bytes,
 | 
			
		||||
				num_stripes, stripe_size_kb)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				created_lv = Vg.fetch_new_lv(vg_name, name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
		return created_lv
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -470,27 +592,35 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	def _create_pool(uuid, vg_name, meta_data_lv, data_lv,
 | 
			
		||||
						create_options, create_method):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		Vg.validate_dbus_object(uuid, vg_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		# Retrieve the full names for the metadata and data lv
 | 
			
		||||
		md = cfg.om.get_object_by_path(meta_data_lv)
 | 
			
		||||
		data = cfg.om.get_object_by_path(data_lv)
 | 
			
		||||
 | 
			
		||||
		if md and data:
 | 
			
		||||
		if dbo and md and data:
 | 
			
		||||
 | 
			
		||||
			new_name = data.Name
 | 
			
		||||
 | 
			
		||||
			rc, out, err = create_method(
 | 
			
		||||
				md.lv_full_name(), data.lv_full_name(), create_options)
 | 
			
		||||
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				mt_remove_dbus_objects((md, data))
 | 
			
		||||
				cfg.om.remove_object(md, emit_signal=True)
 | 
			
		||||
				cfg.om.remove_object(data, emit_signal=True)
 | 
			
		||||
 | 
			
		||||
			Vg.handle_execute(rc, out, err)
 | 
			
		||||
				cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name)
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			msg = ""
 | 
			
		||||
 | 
			
		||||
			if not dbo:
 | 
			
		||||
				msg += 'VG with uuid %s and name %s not present!' % \
 | 
			
		||||
					(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
			if not md:
 | 
			
		||||
				msg += 'Meta data LV with object path %s not present!' % \
 | 
			
		||||
					(meta_data_lv)
 | 
			
		||||
@@ -501,7 +631,7 @@ class Vg(AutomatedProperties):
 | 
			
		||||
 | 
			
		||||
			raise dbus.exceptions.DBusException(VG_INTERFACE, msg)
 | 
			
		||||
 | 
			
		||||
		return Vg.fetch_new_lv(vg_name, new_name)
 | 
			
		||||
		return cache_pool_lv
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -535,20 +665,33 @@ class Vg(AutomatedProperties):
 | 
			
		||||
		pv_devices = []
 | 
			
		||||
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		Vg.validate_dbus_object(uuid, vg_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		# Check for existence of pv object paths
 | 
			
		||||
		for p in pv_object_paths:
 | 
			
		||||
			pv = cfg.om.get_object_by_path(p)
 | 
			
		||||
			if pv:
 | 
			
		||||
				pv_devices.append(pv.Name)
 | 
			
		||||
		if dbo:
 | 
			
		||||
			# Check for existence of pv object paths
 | 
			
		||||
			for p in pv_object_paths:
 | 
			
		||||
				pv = cfg.om.get_object_by_path(p)
 | 
			
		||||
				if pv:
 | 
			
		||||
					pv_devices.append(pv.Name)
 | 
			
		||||
				else:
 | 
			
		||||
					raise dbus.exceptions.DBusException(
 | 
			
		||||
						VG_INTERFACE, 'PV object path = %s not found' % p)
 | 
			
		||||
 | 
			
		||||
			rc, out, err = cmdhandler.pv_tag(
 | 
			
		||||
				pv_devices, tags_add, tags_del, tag_options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.load()
 | 
			
		||||
				return '/'
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE, 'PV object path = %s not found' % p)
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		Vg.handle_execute(*cmdhandler.pv_tag(
 | 
			
		||||
			pv_devices, tags_add, tags_del, tag_options))
 | 
			
		||||
		return '/'
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -586,11 +729,25 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		Vg.validate_dbus_object(uuid, vg_name)
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		Vg.handle_execute(*cmdhandler.vg_tag(
 | 
			
		||||
			vg_name, tags_add, tags_del, tag_options))
 | 
			
		||||
		return '/'
 | 
			
		||||
		if dbo:
 | 
			
		||||
 | 
			
		||||
			rc, out, err = cmdhandler.vg_tag(
 | 
			
		||||
				vg_name, tags_add, tags_del, tag_options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				dbo.refresh()
 | 
			
		||||
				return '/'
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -627,9 +784,23 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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))
 | 
			
		||||
		return '/'
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = method(vg_name, value, options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				dbo.refresh()
 | 
			
		||||
				return '/'
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -687,10 +858,23 @@ class Vg(AutomatedProperties):
 | 
			
		||||
	def _vg_activate_deactivate(uuid, vg_name, activate, control_flags,
 | 
			
		||||
								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))
 | 
			
		||||
		return '/'
 | 
			
		||||
		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
 | 
			
		||||
 | 
			
		||||
		if dbo:
 | 
			
		||||
			rc, out, err = cmdhandler.activate_deactivate(
 | 
			
		||||
				'vgchange', vg_name, activate, control_flags, options)
 | 
			
		||||
			if rc == 0:
 | 
			
		||||
				cfg.load()
 | 
			
		||||
				return '/'
 | 
			
		||||
			else:
 | 
			
		||||
				raise dbus.exceptions.DBusException(
 | 
			
		||||
					VG_INTERFACE,
 | 
			
		||||
					'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				VG_INTERFACE,
 | 
			
		||||
				'VG with uuid %s and name %s not present!' %
 | 
			
		||||
				(uuid, vg_name))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=VG_INTERFACE,
 | 
			
		||||
@@ -716,12 +900,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 +955,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
 | 
			
		||||
							
								
								
									
										65
									
								
								daemons/lvmetad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								daemons/lvmetad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2011-2012 Red Hat, Inc.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of LVM2.
 | 
			
		||||
#
 | 
			
		||||
# This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
# modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
# of the GNU Lesser General Public License v.2.1.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
# along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
SOURCES = lvmetad-core.c
 | 
			
		||||
SOURCES2 = testclient.c
 | 
			
		||||
 | 
			
		||||
TARGETS = lvmetad lvmetactl
 | 
			
		||||
 | 
			
		||||
.PHONY: install_lvmetad
 | 
			
		||||
 | 
			
		||||
CFLOW_LIST = $(SOURCES)
 | 
			
		||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
 | 
			
		||||
CFLOW_TARGET = lvmetad
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
 | 
			
		||||
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
 | 
			
		||||
 | 
			
		||||
LIBS += $(PTHREAD_LIBS)
 | 
			
		||||
 | 
			
		||||
LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS)
 | 
			
		||||
CLDFLAGS += -L$(top_builddir)/libdaemon/server
 | 
			
		||||
CFLAGS += $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
 | 
			
		||||
lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
 | 
			
		||||
		    $(top_builddir)/libdaemon/server/libdaemonserver.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
 | 
			
		||||
 | 
			
		||||
lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \
 | 
			
		||||
	$(top_builddir)/libdaemon/server/libdaemonserver.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LVMLIBS)
 | 
			
		||||
 | 
			
		||||
CLEAN_TARGETS += lvmetactl.o
 | 
			
		||||
 | 
			
		||||
# TODO: No idea. No idea how to test either.
 | 
			
		||||
#ifneq ("$(CFLOW_CMD)", "")
 | 
			
		||||
#CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
 | 
			
		||||
#-include $(top_builddir)/libdm/libdevmapper.cflow
 | 
			
		||||
#-include $(top_builddir)/lib/liblvm-internal.cflow
 | 
			
		||||
#-include $(top_builddir)/lib/liblvm2cmd.cflow
 | 
			
		||||
#-include $(top_builddir)/daemons/dmeventd/$(LIB_NAME).cflow
 | 
			
		||||
#-include $(top_builddir)/daemons/dmeventd/plugins/mirror/$(LIB_NAME)-lvm2mirror.cflow
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
install_lvmetad: lvmetad
 | 
			
		||||
	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
 | 
			
		||||
 | 
			
		||||
install_lvm2: install_lvmetad
 | 
			
		||||
 | 
			
		||||
install: install_lvm2
 | 
			
		||||
							
								
								
									
										249
									
								
								daemons/lvmetad/lvmetactl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								daemons/lvmetad/lvmetactl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 Red Hat, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU Lesser General Public License v.2.1.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "tool.h"
 | 
			
		||||
 | 
			
		||||
#include "lvmetad-client.h"
 | 
			
		||||
 | 
			
		||||
daemon_handle h;
 | 
			
		||||
 | 
			
		||||
static void print_reply(daemon_reply reply)
 | 
			
		||||
{
 | 
			
		||||
	const char *a = daemon_reply_str(reply, "response", NULL);
 | 
			
		||||
	const char *b = daemon_reply_str(reply, "status", NULL);
 | 
			
		||||
	const char *c = daemon_reply_str(reply, "reason", NULL);
 | 
			
		||||
 | 
			
		||||
	printf("response \"%s\" status \"%s\" reason \"%s\"\n",
 | 
			
		||||
	       a ? a : "", b ? b : "", c ? c : "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	daemon_reply reply;
 | 
			
		||||
	char *cmd;
 | 
			
		||||
	char *uuid;
 | 
			
		||||
	char *name;
 | 
			
		||||
	int val;
 | 
			
		||||
	int ver;
 | 
			
		||||
 | 
			
		||||
	if (argc < 2) {
 | 
			
		||||
		printf("lvmetactl dump\n");
 | 
			
		||||
		printf("lvmetactl pv_list\n");
 | 
			
		||||
		printf("lvmetactl vg_list\n");
 | 
			
		||||
		printf("lvmetactl get_global_info\n");
 | 
			
		||||
		printf("lvmetactl vg_lookup_name <name>\n");
 | 
			
		||||
		printf("lvmetactl vg_lookup_uuid <uuid>\n");
 | 
			
		||||
		printf("lvmetactl pv_lookup_uuid <uuid>\n");
 | 
			
		||||
		printf("lvmetactl set_global_invalid 0|1\n");
 | 
			
		||||
		printf("lvmetactl set_global_disable 0|1\n");
 | 
			
		||||
		printf("lvmetactl set_vg_version <uuid> <name> <version>\n");
 | 
			
		||||
		printf("lvmetactl vg_lock_type <uuid>\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = argv[1];
 | 
			
		||||
 | 
			
		||||
	h = lvmetad_open(NULL);
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(cmd, "dump")) {
 | 
			
		||||
		reply = daemon_send_simple(h, "dump",
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "pv_list")) {
 | 
			
		||||
		reply = daemon_send_simple(h, "pv_list",
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "vg_list")) {
 | 
			
		||||
		reply = daemon_send_simple(h, "vg_list",
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "get_global_info")) {
 | 
			
		||||
		reply = daemon_send_simple(h, "get_global_info",
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "set_global_invalid")) {
 | 
			
		||||
		if (argc < 3) {
 | 
			
		||||
			printf("set_global_invalid 0|1\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		val = atoi(argv[2]);
 | 
			
		||||
 | 
			
		||||
		reply = daemon_send_simple(h, "set_global_info",
 | 
			
		||||
					   "global_invalid = " FMTd64, (int64_t) val,
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		print_reply(reply);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "set_global_disable")) {
 | 
			
		||||
		if (argc < 3) {
 | 
			
		||||
			printf("set_global_disable 0|1\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		val = atoi(argv[2]);
 | 
			
		||||
 | 
			
		||||
		reply = daemon_send_simple(h, "set_global_info",
 | 
			
		||||
					   "global_disable = " FMTd64, (int64_t) val,
 | 
			
		||||
					   "disable_reason = %s", LVMETAD_DISABLE_REASON_DIRECT,
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		print_reply(reply);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "set_vg_version")) {
 | 
			
		||||
		if (argc < 5) {
 | 
			
		||||
			printf("set_vg_version <uuid> <name> <ver>\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		uuid = argv[2];
 | 
			
		||||
		name = argv[3];
 | 
			
		||||
		ver = atoi(argv[4]);
 | 
			
		||||
 | 
			
		||||
		if ((strlen(uuid) == 1) && (uuid[0] == '-'))
 | 
			
		||||
			uuid = NULL;
 | 
			
		||||
		if ((strlen(name) == 1) && (name[0] == '-'))
 | 
			
		||||
			name = NULL;
 | 
			
		||||
 | 
			
		||||
		if (uuid && name) {
 | 
			
		||||
			reply = daemon_send_simple(h, "set_vg_info",
 | 
			
		||||
						   "uuid = %s", uuid,
 | 
			
		||||
						   "name = %s", name,
 | 
			
		||||
						   "version = " FMTd64, (int64_t) ver,
 | 
			
		||||
						   "token = %s", "skip",
 | 
			
		||||
						   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
						   "cmd = %s", "lvmetactl",
 | 
			
		||||
						   NULL);
 | 
			
		||||
		} else if (uuid) {
 | 
			
		||||
			reply = daemon_send_simple(h, "set_vg_info",
 | 
			
		||||
						   "uuid = %s", uuid,
 | 
			
		||||
						   "version = " FMTd64, (int64_t) ver,
 | 
			
		||||
						   "token = %s", "skip",
 | 
			
		||||
						   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
						   "cmd = %s", "lvmetactl",
 | 
			
		||||
						   NULL);
 | 
			
		||||
		} else if (name) {
 | 
			
		||||
			reply = daemon_send_simple(h, "set_vg_info",
 | 
			
		||||
						   "name = %s", name,
 | 
			
		||||
						   "version = " FMTd64, (int64_t) ver,
 | 
			
		||||
						   "token = %s", "skip",
 | 
			
		||||
						   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
						   "cmd = %s", "lvmetactl",
 | 
			
		||||
						   NULL);
 | 
			
		||||
		} else {
 | 
			
		||||
			printf("name or uuid required\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		print_reply(reply);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "vg_lookup_name")) {
 | 
			
		||||
		if (argc < 3) {
 | 
			
		||||
			printf("vg_lookup_name <name>\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		name = argv[2];
 | 
			
		||||
 | 
			
		||||
		reply = daemon_send_simple(h, "vg_lookup",
 | 
			
		||||
					   "name = %s", name,
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "vg_lookup_uuid")) {
 | 
			
		||||
		if (argc < 3) {
 | 
			
		||||
			printf("vg_lookup_uuid <uuid>\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		uuid = argv[2];
 | 
			
		||||
 | 
			
		||||
		reply = daemon_send_simple(h, "vg_lookup",
 | 
			
		||||
					   "uuid = %s", uuid,
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "vg_lock_type")) {
 | 
			
		||||
		struct dm_config_node *metadata;
 | 
			
		||||
		const char *lock_type;
 | 
			
		||||
 | 
			
		||||
		if (argc < 3) {
 | 
			
		||||
			printf("vg_lock_type <uuid>\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		uuid = argv[2];
 | 
			
		||||
 | 
			
		||||
		reply = daemon_send_simple(h, "vg_lookup",
 | 
			
		||||
					   "uuid = %s", uuid,
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		/* printf("%s\n", reply.buffer.mem); */
 | 
			
		||||
 | 
			
		||||
		metadata = dm_config_find_node(reply.cft->root, "metadata");
 | 
			
		||||
		if (!metadata) {
 | 
			
		||||
			printf("no metadata\n");
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lock_type = dm_config_find_str(metadata, "metadata/lock_type", NULL);
 | 
			
		||||
		if (!lock_type) {
 | 
			
		||||
			printf("no lock_type\n");
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		printf("lock_type %s\n", lock_type);
 | 
			
		||||
 | 
			
		||||
	} else if (!strcmp(cmd, "pv_lookup_uuid")) {
 | 
			
		||||
		if (argc < 3) {
 | 
			
		||||
			printf("pv_lookup_uuid <uuid>\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		uuid = argv[2];
 | 
			
		||||
 | 
			
		||||
		reply = daemon_send_simple(h, "pv_lookup",
 | 
			
		||||
					   "uuid = %s", uuid,
 | 
			
		||||
					   "token = %s", "skip",
 | 
			
		||||
					   "pid = " FMTd64, (int64_t)getpid(),
 | 
			
		||||
					   "cmd = %s", "lvmetactl",
 | 
			
		||||
					   NULL);
 | 
			
		||||
		printf("%s\n", reply.buffer.mem);
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		printf("unknown command\n");
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	daemon_reply_destroy(reply);
 | 
			
		||||
out_close:
 | 
			
		||||
	daemon_close(h);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user