mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-11-03 08:23:48 +03:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v2_02_186
			...
			dev-bmr-dm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					59a6fc0e4c | ||
| 
						 | 
					f64da7c628 | 
							
								
								
									
										103
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										103
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +1,12 @@
 | 
			
		||||
*.5
 | 
			
		||||
*.7
 | 
			
		||||
*.8
 | 
			
		||||
*.8_gen
 | 
			
		||||
*.a
 | 
			
		||||
*.d
 | 
			
		||||
*.o
 | 
			
		||||
*.orig
 | 
			
		||||
*.pc
 | 
			
		||||
*.pot
 | 
			
		||||
*.pyc
 | 
			
		||||
*.pyo
 | 
			
		||||
*.rej
 | 
			
		||||
*.so
 | 
			
		||||
*.so.*
 | 
			
		||||
@@ -31,103 +28,3 @@ make.tmpl
 | 
			
		||||
/cscope.out
 | 
			
		||||
/tags
 | 
			
		||||
/tmp/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tools/man-generator
 | 
			
		||||
tools/man-generator.c
 | 
			
		||||
 | 
			
		||||
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/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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								COPYING
									
									
									
									
									
								
							@@ -2,7 +2,7 @@
 | 
			
		||||
		       Version 2, June 1991
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 | 
			
		||||
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
			
		||||
     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
@@ -305,7 +305,7 @@ the "copyright" line and a pointer to where the full notice is found.
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.
 | 
			
		||||
							
								
								
									
										76
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								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.
 | 
			
		||||
#
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
@@ -18,7 +18,7 @@ top_builddir = @top_builddir@
 | 
			
		||||
abs_top_builddir = @abs_top_builddir@
 | 
			
		||||
abs_top_srcdir = @abs_top_srcdir@
 | 
			
		||||
 | 
			
		||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts device_mapper tools
 | 
			
		||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools
 | 
			
		||||
 | 
			
		||||
ifeq ("@UDEV_RULES@", "yes")
 | 
			
		||||
  SUBDIRS += udev
 | 
			
		||||
@@ -43,7 +43,8 @@ endif
 | 
			
		||||
ifeq ($(MAKECMDGOALS),distclean)
 | 
			
		||||
  SUBDIRS = conf include man test scripts \
 | 
			
		||||
    libdaemon lib tools daemons libdm \
 | 
			
		||||
    udev po liblvm python device_mapper
 | 
			
		||||
    udev po liblvm python \
 | 
			
		||||
    unit-tests/datastruct unit-tests/mm unit-tests/regex
 | 
			
		||||
tools.distclean: test.distclean
 | 
			
		||||
endif
 | 
			
		||||
DISTCLEAN_DIRS += lcov_reports*
 | 
			
		||||
@@ -58,12 +59,7 @@ liblvm: lib
 | 
			
		||||
daemons: lib libdaemon tools
 | 
			
		||||
tools: lib libdaemon device-mapper
 | 
			
		||||
po: tools daemons
 | 
			
		||||
man: tools
 | 
			
		||||
all_man: tools
 | 
			
		||||
scripts: liblvm libdm
 | 
			
		||||
test: tools daemons
 | 
			
		||||
unit-test: lib
 | 
			
		||||
run-unit-test: unit-test
 | 
			
		||||
 | 
			
		||||
lib.device-mapper: include.device-mapper
 | 
			
		||||
libdm.device-mapper: include.device-mapper
 | 
			
		||||
@@ -99,10 +95,10 @@ endif
 | 
			
		||||
DISTCLEAN_TARGETS += cscope.out
 | 
			
		||||
CLEAN_DIRS += autom4te.cache
 | 
			
		||||
 | 
			
		||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit-test run-unit-test: test
 | 
			
		||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld unit: all
 | 
			
		||||
	$(MAKE) -C test $(@)
 | 
			
		||||
 | 
			
		||||
conf.generate man.generate: tools
 | 
			
		||||
conf.generate: tools
 | 
			
		||||
 | 
			
		||||
# how to use parenthesis in makefiles
 | 
			
		||||
leftparen:=(
 | 
			
		||||
@@ -124,20 +120,16 @@ rpm: dist
 | 
			
		||||
	$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES
 | 
			
		||||
	$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES
 | 
			
		||||
	$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES
 | 
			
		||||
	DM_VER=$$(cut -d' ' -f1 $(top_srcdir)/VERSION_DM | cut -d- -f1);\
 | 
			
		||||
	GIT_VER=$$(cd $(top_srcdir); git describe | cut -s -d- --output-delimiter=. -f2,3);\
 | 
			
		||||
	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," \
 | 
			
		||||
	    -e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \
 | 
			
		||||
	    -e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $${GIT_VER:-"0"}," \
 | 
			
		||||
	    -e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \
 | 
			
		||||
	    $(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc
 | 
			
		||||
	rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
 | 
			
		||||
 | 
			
		||||
generate: conf.generate man.generate
 | 
			
		||||
generate: conf.generate
 | 
			
		||||
	$(MAKE) -C conf generate
 | 
			
		||||
	$(MAKE) -C man generate
 | 
			
		||||
 | 
			
		||||
all_man:
 | 
			
		||||
	$(MAKE) -C man all_man
 | 
			
		||||
 | 
			
		||||
install_system_dirs:
 | 
			
		||||
	$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR)
 | 
			
		||||
@@ -148,7 +140,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:
 | 
			
		||||
@@ -158,8 +150,8 @@ install_systemd_generators:
 | 
			
		||||
install_systemd_units:
 | 
			
		||||
	$(MAKE) -C scripts install_systemd_units
 | 
			
		||||
 | 
			
		||||
install_all_man:
 | 
			
		||||
	$(MAKE) -C man install_all_man
 | 
			
		||||
install_full_man:
 | 
			
		||||
	$(MAKE) -C man install_full_man
 | 
			
		||||
 | 
			
		||||
ifeq ("@PYTHON_BINDINGS@", "yes")
 | 
			
		||||
install_python_bindings:
 | 
			
		||||
@@ -171,12 +163,8 @@ install_tmpfiles_configuration:
 | 
			
		||||
 | 
			
		||||
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
 | 
			
		||||
	libdaemon/client.info libdaemon/server.info \
 | 
			
		||||
	test/unit.info \
 | 
			
		||||
	daemons/clvmd.info \
 | 
			
		||||
	daemons/dmeventd.info \
 | 
			
		||||
	daemons/lvmetad.info \
 | 
			
		||||
	daemons/lvmlockd.info \
 | 
			
		||||
	daemons/lvmpolld.info
 | 
			
		||||
	daemons/clvmd.info daemons/dmeventd.info \
 | 
			
		||||
	daemons/lvmetad.info
 | 
			
		||||
 | 
			
		||||
CLEAN_TARGETS += $(LCOV_TRACES)
 | 
			
		||||
 | 
			
		||||
@@ -214,11 +202,37 @@ endif
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
all: tags
 | 
			
		||||
tags:
 | 
			
		||||
	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 '{}' +
 | 
			
		||||
	test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags | head -1)" || $(RM) tags
 | 
			
		||||
	test -f tags || find $(top_srcdir) -maxdepth 4 -type f -name '*.[ch]' -exec ctags -a '{}' +
 | 
			
		||||
 | 
			
		||||
CLEAN_TARGETS += tags
 | 
			
		||||
DISTCLEAN_TARGETS += tags
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README
									
									
									
									
									
								
							@@ -6,17 +6,11 @@ Installation instructions are in INSTALL.
 | 
			
		||||
There is no warranty - see COPYING and COPYING.LIB.
 | 
			
		||||
 | 
			
		||||
Tarballs are available from:
 | 
			
		||||
  ftp://sourceware.org/pub/lvm2/
 | 
			
		||||
  ftp://sources.redhat.com/pub/lvm2/
 | 
			
		||||
  https://github.com/lvmteam/lvm2/releases
 | 
			
		||||
 | 
			
		||||
The source code is stored in git:
 | 
			
		||||
  https://sourceware.org/git/?p=lvm2.git
 | 
			
		||||
  git clone git://sourceware.org/git/lvm2.git
 | 
			
		||||
mirrored to:
 | 
			
		||||
  https://github.com/lvmteam/lvm2
 | 
			
		||||
  git clone https://github.com/lvmteam/lvm2.git
 | 
			
		||||
  git clone git@github.com:lvmteam/lvm2.git
 | 
			
		||||
  http://git.fedorahosted.org/git/lvm2.git
 | 
			
		||||
  git clone git://git.fedorahosted.org/git/lvm2.git
 | 
			
		||||
 | 
			
		||||
Mailing list for general discussion related to LVM2:
 | 
			
		||||
  linux-lvm@redhat.com
 | 
			
		||||
@@ -34,14 +28,6 @@ and multipath-tools:
 | 
			
		||||
  dm-devel@redhat.com
 | 
			
		||||
  Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
 | 
			
		||||
 | 
			
		||||
Website:
 | 
			
		||||
  https://sourceware.org/lvm2/
 | 
			
		||||
 | 
			
		||||
Report upstream bugs at:
 | 
			
		||||
  https://bugzilla.redhat.com/enter_bug.cgi?product=LVM%20and%20device-mapper
 | 
			
		||||
or open issues at:
 | 
			
		||||
  https://github.com/lvmteam/lvm2/issues
 | 
			
		||||
 | 
			
		||||
The source code repository used until 7th June 2012 is accessible here:
 | 
			
		||||
  http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								TESTING
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								TESTING
									
									
									
									
									
								
							@@ -1,62 +0,0 @@
 | 
			
		||||
LVM2 Test Suite
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
The codebase contains many tests in the test subdirectory.
 | 
			
		||||
 | 
			
		||||
Before running tests
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Keep in mind the testsuite MUST run under root user.
 | 
			
		||||
 | 
			
		||||
It is recommended not to use LVM on the test machine, especially when running
 | 
			
		||||
tests with udev (`make check_system`.)
 | 
			
		||||
 | 
			
		||||
You MUST disable (or mask) any LVM daemons:
 | 
			
		||||
 | 
			
		||||
- lvmetad
 | 
			
		||||
- dmeventd
 | 
			
		||||
- lvmpolld
 | 
			
		||||
- lvmdbusd
 | 
			
		||||
- lvmlockd
 | 
			
		||||
- clvmd
 | 
			
		||||
- cmirrord
 | 
			
		||||
 | 
			
		||||
For running cluster tests, we are using singlenode locking. Pass
 | 
			
		||||
`--with-clvmd=singlenode` to configure.
 | 
			
		||||
 | 
			
		||||
NOTE: This is useful only for testing, and should not be used in produciton
 | 
			
		||||
code.
 | 
			
		||||
 | 
			
		||||
To run D-Bus daemon tests, existing D-Bus session is required.
 | 
			
		||||
 | 
			
		||||
Running tests
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
As root run:
 | 
			
		||||
 | 
			
		||||
    make check
 | 
			
		||||
 | 
			
		||||
To run only tests matching a string:
 | 
			
		||||
 | 
			
		||||
    make check T=test
 | 
			
		||||
 | 
			
		||||
To skip tests matching a string:
 | 
			
		||||
 | 
			
		||||
    make check S=test
 | 
			
		||||
 | 
			
		||||
There are other targets and many environment variables can be used to tweak the
 | 
			
		||||
testsuite - for full list and description run `make -C test help`.
 | 
			
		||||
 | 
			
		||||
Installing testsuite
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
It is possible to install and run a testsuite against installed LVM. Run the
 | 
			
		||||
following:
 | 
			
		||||
 | 
			
		||||
    make -C test install
 | 
			
		||||
 | 
			
		||||
Then lvm2-testsuite binary can be executed to test installed binaries.
 | 
			
		||||
 | 
			
		||||
See `lvm2-testsuite --help` for options. The same environment variables can be
 | 
			
		||||
used as with `make check`.
 | 
			
		||||
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
1.02.164 (2019-08-27)
 | 
			
		||||
1.02.107-git (2015-08-26)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										937
									
								
								WHATS_NEW
									
									
									
									
									
								
							
							
						
						
									
										937
									
								
								WHATS_NEW
									
									
									
									
									
								
							@@ -1,940 +1,5 @@
 | 
			
		||||
Version 2.02.186 - 27th August 2019
 | 
			
		||||
Version 2.02.130 - 
 | 
			
		||||
===================================
 | 
			
		||||
  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.
 | 
			
		||||
  Fix metadata writes from corrupting with large physical block size.
 | 
			
		||||
  Report no_discard_passdown for cache LVs with lvs -o+kernel_discards.
 | 
			
		||||
  Prevent shared active mirror LVs with lvmlockd.
 | 
			
		||||
 | 
			
		||||
Version 2.02.185 - 13th May 2019
 | 
			
		||||
================================
 | 
			
		||||
  Fix change of monitoring in clustered volumes.
 | 
			
		||||
  Improve -lXXX%VG modifier which improves cache segment estimation.
 | 
			
		||||
  Add synchronization with udev before removing cached devices.
 | 
			
		||||
  Fix missing growth of _pmspare volume when extending _tmeta volume.
 | 
			
		||||
  Automatically grow thin metadata, when thin data gets too big.
 | 
			
		||||
  Add support for vgsplit with cached devices.
 | 
			
		||||
  Fix signal delivery checking race in libdaemon (lvmetad).
 | 
			
		||||
  Add missing Before=shutdown.target to LVM2 services to fix shutdown ordering.
 | 
			
		||||
 | 
			
		||||
Version 2.02.184 - 22nd March 2019
 | 
			
		||||
==================================
 | 
			
		||||
  Fix (de)activation of RaidLVs with visible SubLVs
 | 
			
		||||
  Change scan_lvs default to 0 so LVs are not scanned for PVs.
 | 
			
		||||
  Add scan_lvs config setting to control if lvm scans LVs for PVs.
 | 
			
		||||
  Fix missing proper initialization of pv_list struct when adding pv.
 | 
			
		||||
 | 
			
		||||
Version 2.02.183 - 07th December 2018
 | 
			
		||||
=====================================
 | 
			
		||||
  Avoid disabling lvmetad when repair does nothing.
 | 
			
		||||
  Fix component detection for md version 0.90.
 | 
			
		||||
  Use sync io if async io_setup fails, or use_aio=0 is set in config.
 | 
			
		||||
  Avoid opening devices to get block size by using existing open fd.
 | 
			
		||||
 | 
			
		||||
Version 2.02.182 - 30th October 2018
 | 
			
		||||
====================================
 | 
			
		||||
  Fix possible write race between last metadata block and the first extent.
 | 
			
		||||
  Fix filtering of md 1.0 devices so they are not seen as duplicate PVs.
 | 
			
		||||
  Fix lvconvert striped/raid0/raid0_meta -> raid6 regression.
 | 
			
		||||
  Add After=rbdmap.service to {lvm2-activation-net,blk-availability}.service.
 | 
			
		||||
  Fix pvs with lvmetad to avoid too many open files from filter reads.
 | 
			
		||||
  Fix pvscan --cache to avoid too many open files from filter reads.
 | 
			
		||||
  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.
 | 
			
		||||
  Fix mirrors honoring read_only_volume_list.
 | 
			
		||||
 | 
			
		||||
Version 2.02.181 - 01 August 2018
 | 
			
		||||
=================================
 | 
			
		||||
  Reject conversions on raid1 LVs with split tracked SubLVs.
 | 
			
		||||
  Reject conversions on raid1 split tracked SubLVs.
 | 
			
		||||
  Fix dmstats list failing when no regions exist.
 | 
			
		||||
  Reject conversions of LVs under snapshot.
 | 
			
		||||
  Limit suggested options on incorrect option for lvconvert subcommand.
 | 
			
		||||
 | 
			
		||||
Version 2.02.180 - 19th July 2018
 | 
			
		||||
=================================
 | 
			
		||||
  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.
 | 
			
		||||
  lvconvert: provide possible layouts between linear and striped/raid
 | 
			
		||||
  Fix unmonitoring of merging snapshots.
 | 
			
		||||
  Add missing -l description in fsadm man page.
 | 
			
		||||
  Cache can uses metadata format 2 with cleaner policy.
 | 
			
		||||
  Avoid showing internal error in lvs output or pvmoved LVs.
 | 
			
		||||
  Fix check if resized PV can also fit metadata area.
 | 
			
		||||
  Reopen devices RDWR only before writing to avoid udev issues.
 | 
			
		||||
  Change pvresize output confusing when no resize took place.
 | 
			
		||||
  Fix lvmetad hanging on shutdown.
 | 
			
		||||
  Fix mem leak in clvmd and more coverity issues.
 | 
			
		||||
 | 
			
		||||
Version 2.02.179 - 18th June 2018
 | 
			
		||||
=================================
 | 
			
		||||
  Allow forced vgchange to lock type none on clustered VG.
 | 
			
		||||
  Add the report field "shared".
 | 
			
		||||
  Enable automatic metadata consistency repair on a shared VG.
 | 
			
		||||
  Fix pvremove force on a PV with a shared VG.
 | 
			
		||||
  Fixed vgimportclone of a PV with a shared VG.
 | 
			
		||||
  Enable previously disallowed thin/cache commands in shared VGs.
 | 
			
		||||
  Enable metadata-related changes on LVs active with shared lock.
 | 
			
		||||
  Do not continue trying to use a device that cannot be opened.
 | 
			
		||||
  Fix problems opening a device that fails and returns.
 | 
			
		||||
  Use versionsort to fix archive file expiry beyond 100000 files.
 | 
			
		||||
 | 
			
		||||
Version 2.02.178 - 13th June 2018
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
Version 2.02.178-rc1 - 24th May 2018
 | 
			
		||||
====================================
 | 
			
		||||
  Add libaio dependency for build.
 | 
			
		||||
  Remove lvm1 and pool format handling and add filter to ignore them.
 | 
			
		||||
  Move some filter checks to after disks are read.
 | 
			
		||||
  Rework disk scanning and when it is used.
 | 
			
		||||
  Add new io layer and shift code to using it.
 | 
			
		||||
  Fix lvconvert's return code on degraded -m raid1 conversion.
 | 
			
		||||
  --enable-testing switch for ./configure has been removed.
 | 
			
		||||
  --with-snapshots switch for ./configure has been removed.
 | 
			
		||||
  --with-mirrors switch for ./configure has been removed.
 | 
			
		||||
  --with-raid switch for ./configure has been removed.
 | 
			
		||||
  --with-thin switch for ./configure has been removed.
 | 
			
		||||
  --with-cache switch for ./configure has been removed.
 | 
			
		||||
  Include new unit-test framework and unit tests.
 | 
			
		||||
  Extend validation of region_size for mirror segment.
 | 
			
		||||
  Reload whole device stack when reinitilizing mirror log.
 | 
			
		||||
  Mirrors without monitoring are WARNING and not blocking on error.
 | 
			
		||||
  Detect too big region_size with clustered mirrors.
 | 
			
		||||
  Fix evaluation of maximal region size for mirror log.
 | 
			
		||||
  Enhance mirror log size estimation and use smaller size when possible.
 | 
			
		||||
  Fix incorrect mirror log size calculation on 32bit arch.
 | 
			
		||||
  Enhance preloading tree creating.
 | 
			
		||||
  Fix regression on acceptance of any LV on lvconvert.
 | 
			
		||||
  Restore usability of thin LV to be again external origin for another thin.
 | 
			
		||||
  Keep systemd vars on change event in 69-dm-lvm-metad.rules for systemd reload.
 | 
			
		||||
  Write systemd and non-systemd rule in 69-dm-lvm-metad.rules, GOTO active one.
 | 
			
		||||
  Add test for activation/volume_list (Sub)LV remnants.
 | 
			
		||||
  Disallow usage of cache format 2 with mq cache policy.
 | 
			
		||||
  Again accept striped LV as COW LV with lvconvert -s (2.02.169).
 | 
			
		||||
  Fix raid target version testing for supported features.
 | 
			
		||||
  Allow activation of pools when thin/cache_check tool is missing.
 | 
			
		||||
  Remove RaidLV on creation failure when rmeta devices can't be activated.
 | 
			
		||||
  Add prioritized_section() to restore cookie boundaries (2.02.177).
 | 
			
		||||
  Enhance error messages when read error happens.
 | 
			
		||||
  Enhance mirror log initialization for old mirror target.
 | 
			
		||||
  Skip private crypto and stratis devices.
 | 
			
		||||
  Skip frozen raid devices from scanning.
 | 
			
		||||
  Activate RAID SubLVs on read_only_volume_list readwrite.
 | 
			
		||||
  Offer convenience type raid5_n converting to raid10.
 | 
			
		||||
  Automatically avoid reading invalid snapshots during device scan.
 | 
			
		||||
  Ensure COW device is writable even for read-only thick snapshots.
 | 
			
		||||
  Support activation of component LVs in read-only mode.
 | 
			
		||||
  Extend internal library to recognize and work with component LV.
 | 
			
		||||
  Skip duplicate check for active LV when prompting for its removal.
 | 
			
		||||
  Activate correct lock holding LV when it is cached.
 | 
			
		||||
  Do not modify archived metadata when removing striped raid.
 | 
			
		||||
  Fix memleak on error path when obtaining lv_raid_data_offset.
 | 
			
		||||
  Fix compatibility size test of extended external origin.
 | 
			
		||||
  Add external_origin visiting in for_each_sub_lv().
 | 
			
		||||
  Ensure cluster commands drop their device cache before locking VG.
 | 
			
		||||
  Do not report LV as remotely active when it's locally exclusive in cluster.
 | 
			
		||||
  Add deprecate messages for usage of mirrors with mirrorlog.
 | 
			
		||||
  Separate reporting of monitoring status and error status.
 | 
			
		||||
  Improve validation of created strings in vgimportclone.
 | 
			
		||||
  Add missing initialisation of mem pool in systemd generator.
 | 
			
		||||
  Do not reopen output streams for multithreaded users of liblvm.
 | 
			
		||||
  Configure ensures /usr/bin dir is checked for dmpd tools.
 | 
			
		||||
  Restore pvmove support for wide-clustered active volumes (2.02.177).
 | 
			
		||||
  Avoid non-exclusive activation of exclusive segment types.
 | 
			
		||||
  Fix trimming sibling PVs when doing a pvmove of raid subLVs.
 | 
			
		||||
  Preserve exclusive activation during thin snaphost merge.
 | 
			
		||||
  Avoid exceeding array bounds in allocation tag processing.
 | 
			
		||||
  Add --lockopt to common options and add option to skip selected locks.
 | 
			
		||||
 | 
			
		||||
Version 2.02.177 - 18th December 2017
 | 
			
		||||
=====================================
 | 
			
		||||
  When writing text metadata content, use complete 4096 byte blocks.
 | 
			
		||||
  Change text format metadata alignment from 512 to 4096 bytes.
 | 
			
		||||
  When writing metadata, consistently skip mdas marked as failed.
 | 
			
		||||
  Refactor and adjust text format metadata alignment calculation.
 | 
			
		||||
  Fix python3 path in lvmdbusd to use value detected by configure.
 | 
			
		||||
  Reduce checks for active LVs in vgchange before background polling.
 | 
			
		||||
  Ensure _node_send_message always uses clean status of thin pool.
 | 
			
		||||
  Fix lvmlockd to use pool lock when accessing _tmeta volume.
 | 
			
		||||
  Report expected sanlock_convert errors only when retries fail.
 | 
			
		||||
  Avoid blocking in sanlock_convert on SH to EX lock conversion.
 | 
			
		||||
  Deactivate missing raid LV legs (_rimage_X-missing_Y_Z) on decativation.
 | 
			
		||||
  Skip read-modify-write when entire block is replaced.
 | 
			
		||||
  Categorise I/O with reason annotations in debug messages.
 | 
			
		||||
  Allow extending of raid LVs created with --nosync after a failed repair.
 | 
			
		||||
  Command will lock memory only when suspending volumes.
 | 
			
		||||
  Merge segments when pvmove is finished.
 | 
			
		||||
  Remove label_verify that has never been used.
 | 
			
		||||
  Ensure very large numbers used as arguments are not casted to lower values. 
 | 
			
		||||
  Enhance reading and validation of options stripes and stripes_size.
 | 
			
		||||
  Fix printing of default stripe size when user is not using stripes.
 | 
			
		||||
  Activation code for pvmove automatically discovers holding LVs for resume.
 | 
			
		||||
  Make a pvmove LV locking holder.
 | 
			
		||||
  Do not change critical section counter on resume path without real resume.
 | 
			
		||||
  Enhance activation code to automatically suspend pvmove participants.
 | 
			
		||||
  Prevent conversion of thin volumes to snapshot origin when lvmlockd is used.
 | 
			
		||||
  Correct the steps to change lock type in lvmlockd man page.
 | 
			
		||||
  Retry lock acquisition on recognized sanlock errors.
 | 
			
		||||
  Fix lock manager error codes in lvmlockd.
 | 
			
		||||
  Remove unnecessary single read from lvmdiskscan.
 | 
			
		||||
  Check raid reshape flags in vg_validate().
 | 
			
		||||
  Add support for pvmove of cache and snapshot origins.
 | 
			
		||||
  Avoid using precommitted metadata for suspending pvmove tree.
 | 
			
		||||
  Ehnance pvmove locking.
 | 
			
		||||
  Deactivate activated LVs on error path when pvmove activation fails.
 | 
			
		||||
  Add "io" to log/debug_classes for logging low-level I/O.
 | 
			
		||||
  Eliminate redundant nested VG metadata in VG struct.
 | 
			
		||||
  Avoid importing persistent filter in vgscan/pvscan/vgrename.
 | 
			
		||||
  Fix memleak of string buffer when vgcfgbackup runs in secure mode.
 | 
			
		||||
  Do not print error when clvmd cannot find running clvmd.
 | 
			
		||||
  Prevent start of new merge of snapshot if origin is already being merged.
 | 
			
		||||
  Fix offered type for raid6_n_6 to raid5 conversion (raid5_n).
 | 
			
		||||
  Deactivate sub LVs when removing unused cache-pool.
 | 
			
		||||
  Do not take backup with suspended devices.
 | 
			
		||||
  Avoid RAID4 activation on incompatible kernels under all circumstances.
 | 
			
		||||
  Reject conversion request to striped/raid0 on 2-legged raid4/5.
 | 
			
		||||
 | 
			
		||||
Version 2.02.176 - 3rd November 2017
 | 
			
		||||
====================================
 | 
			
		||||
  Keep Install section only in lvm2-{lvmetad,lvmpolld}.socket systemd unit.
 | 
			
		||||
  Fix segfault in lvm_pv_remove in liblvm. (2.02.173)
 | 
			
		||||
  Do not allow storing VG metadata with LV without any segment.
 | 
			
		||||
  Fix printed message when thin snapshot was already merged.
 | 
			
		||||
  Remove created spare LV when creation of thin-pool failed.
 | 
			
		||||
  Avoid reading ignored metadata when mda gets used again.
 | 
			
		||||
  Fix detection of moved PVs in vgsplit. (2.02.175)
 | 
			
		||||
  Ignore --stripes/--stripesize on RAID takeover
 | 
			
		||||
  Improve used paths for generated systemd units and init shells.
 | 
			
		||||
  Disallow creation of snapshot of mirror/raid subLV (was never supported).
 | 
			
		||||
  Fix regression in more advanced vgname extraction in lvconvert (2.02.169).
 | 
			
		||||
  Allow lvcreate to be used for caching of _tdata LV.
 | 
			
		||||
  Avoid internal error when resizing cache type _tdata LV (not yet supported).
 | 
			
		||||
  Show original converted names when lvconverting LV to pool volume.
 | 
			
		||||
  Move lib code used only by liblvm into metadata-liblvm.c.
 | 
			
		||||
  Distinguish between device not found and excluded by filter.
 | 
			
		||||
  Monitor external origin LVs.
 | 
			
		||||
  Remove the replicator code, including configure --with-replicators.
 | 
			
		||||
  Allow lvcreate --type mirror to work with 100%FREE.
 | 
			
		||||
  Improve selection of resource name for complex volume activation lock.
 | 
			
		||||
  Avoid cutting first character of resource name for activation lock.
 | 
			
		||||
  Support for encrypted devices in fsadm.
 | 
			
		||||
  Improve thin pool overprovisioning and repair warning messages.
 | 
			
		||||
  Fix incorrect adjustment of region size on striped RaidLVs.
 | 
			
		||||
 | 
			
		||||
Version 2.02.175 - 6th October 2017
 | 
			
		||||
===================================
 | 
			
		||||
  Use --help with blockdev when checking for --getsize64 support in fsadm.
 | 
			
		||||
  Dump lvmdbusd debug information with SIGUSR1.
 | 
			
		||||
  Fix metadata corruption in vgsplit and vgmerge intermediate states.
 | 
			
		||||
  Add PV_MOVED_VG PV status flag to mark PVs moving between VGs.
 | 
			
		||||
  Fix lvmdbus hang and recognise unknown VG correctly.
 | 
			
		||||
  Improve error messages when command rules fail.
 | 
			
		||||
  Require LV name with pvmove in a shared VG.
 | 
			
		||||
  Allow shared active mirror LVs with lvmlockd, dlm, and cmirrord.
 | 
			
		||||
  Support lvconvert --repair with cache and cachepool volumes.
 | 
			
		||||
  lvconvert --repair respects --poolmetadataspare option.
 | 
			
		||||
  Mark that we don't plan to develop liblvm2app and python bindings any further.
 | 
			
		||||
  Fix thin pool creation in shared VG. (2.02.173)
 | 
			
		||||
 | 
			
		||||
Version 2.02.174 - 13th September 2017
 | 
			
		||||
======================================
 | 
			
		||||
  Prevent raid1 split with trackchanges in a shared VG.
 | 
			
		||||
  Avoid double unlocking of client & lockspace mutexes in lvmlockd.
 | 
			
		||||
  Fix leaking of file descriptor for non-blocking filebased locking.
 | 
			
		||||
  Fix check for 2nd mda at end of disk fits if using pvcreate --restorefile.
 | 
			
		||||
  Use maximum metadataarea size that fits with pvcreate --restorefile.
 | 
			
		||||
  Always clear cached bootloaderarea when wiping label e.g. in pvcreate.
 | 
			
		||||
  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.
 | 
			
		||||
 | 
			
		||||
Version 2.02.166 - 26th September 2016
 | 
			
		||||
======================================
 | 
			
		||||
  Fix lvm2-activation-generator to read all LVM2 config sources. (2.02.155)
 | 
			
		||||
  Fix lvchange-rebuild-raid.sh to cope with older target versions.
 | 
			
		||||
  Use dm_config_parse_without_dup_node_check() to speedup metadata reading.
 | 
			
		||||
  Fix lvconvert --repair regression
 | 
			
		||||
  Fix reported origin lv field for cache volumes. (2.02.133)
 | 
			
		||||
  Always specify snapshot cow LV for monitoring not internal LV. (2.02.165)
 | 
			
		||||
  Fix lvchange --discard|--zero for active thin-pool.
 | 
			
		||||
  Enforce 4MiB or 25% metadata free space for thin pool operations.
 | 
			
		||||
  Fix lock-holder device for thin pool with inactive thin volumes.
 | 
			
		||||
  Use --alloc normal for mirror logs even if the mimages were stricter.
 | 
			
		||||
  Use O_DIRECT to gather metadata in lvmdump.
 | 
			
		||||
  Ignore creation_time when checking for matching metadata for lvmetad.
 | 
			
		||||
  Fix possible NULL pointer derefence when checking for monitoring.
 | 
			
		||||
  Add lvmreport(7) man page.
 | 
			
		||||
  Don't install lvmraid(7) man page when raid excluded. (2.02.165)
 | 
			
		||||
  Report 0% as dirty (copy%) for cache without any used block.
 | 
			
		||||
  Fix lvm2api reporting of cache data and metadata percent.
 | 
			
		||||
  Restore reporting of metadata usage for cache volumes (2.02.155).
 | 
			
		||||
  Support raid scrubbing on cache origin LV.
 | 
			
		||||
 | 
			
		||||
Version 2.02.165 - 7th September 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Add lvmraid(7) man page.
 | 
			
		||||
  Use udev db to check for mpath components before running pvscan for lvmetad.
 | 
			
		||||
  Use lsblk -s and lsblk -O in lvmdump only if these options are supported.
 | 
			
		||||
  Fix number of stripes shown in lvcreate raid10 message when too many.
 | 
			
		||||
  Change lvmdbusd to use new lvm shell facilities.
 | 
			
		||||
  Do not monitor cache-pool metadata when LV is just being cleared.
 | 
			
		||||
  Add allocation/cache_pool_max_chunks to prevent misuse of cache target.
 | 
			
		||||
  Give error not segfault in lvconvert --splitmirrors when PV lies outside LV.
 | 
			
		||||
  Fix typo in report/columns_as_rows config option name recognition (2.02.99).
 | 
			
		||||
  Avoid PV tags when checking allocation against parallel PVs.
 | 
			
		||||
  Disallow mirror conversions of raid10 volumes.
 | 
			
		||||
  Fix dmeventd unmonitoring when segment type (and dso) changes.
 | 
			
		||||
  Don't allow lvconvert --repair on raid0 devices or attempt to monitor them.
 | 
			
		||||
  No longer adjust incorrect number of raid stripes supplied to lvcreate.
 | 
			
		||||
  Move lcm and gcd to lib/misc.
 | 
			
		||||
  Fix vgsplit of external origins. (2.02.162)
 | 
			
		||||
  Prohibit creation of RAID LVs unless VG extent size is at least the page size.
 | 
			
		||||
  Suppress some unnecessary --stripesize parameter warnings.
 | 
			
		||||
  Fix 'pvmove -n name ...' to prohibit collocation of RAID SubLVs
 | 
			
		||||
 | 
			
		||||
Version 2.02.164 - 15th August 2016
 | 
			
		||||
===================================
 | 
			
		||||
  Fix selection of PVs when allocating raid0_meta.
 | 
			
		||||
  Fix sdbus socket leak leading to hang in lvmnotify.
 | 
			
		||||
  Specify max stripes for raid LV types: raid0:64; 1:10; 4,5:63; 6:62; 10:32.
 | 
			
		||||
  Avoid double suffix when naming _rmeta LV paired with _rimage LV.
 | 
			
		||||
 | 
			
		||||
Version 2.02.163 - 10th August 2016
 | 
			
		||||
===================================
 | 
			
		||||
  Add profile for lvmdbusd which uses lvm shell json report output.
 | 
			
		||||
  Restrict in-command modification of some parms in lvm shell.
 | 
			
		||||
  Apply LVM_COMMAND_PROFILE early for lvm shell.
 | 
			
		||||
  Refactor reporting so lvm shell log report collects whole of cmd execution.
 | 
			
		||||
  Support LVM_*_FD envvars to redirect output to file descriptors.
 | 
			
		||||
  Limit use of --corelog and --mirrorlog to mirrors in lvconvert.
 | 
			
		||||
  Reject --nosync option for RAID6 LVs in lvcreate.
 | 
			
		||||
  Do not refresh whole cmd context if profile dropped after processing LVM cmd.
 | 
			
		||||
  Support straightforward lvconvert between striped and raid4 LVs.
 | 
			
		||||
  Support straightforward lvconvert between raid1 and mirror LVs.
 | 
			
		||||
  Report supported conversions when asked for unsupported raid lvconvert.
 | 
			
		||||
  Add "--rebuild PV" option to lvchange to allow for PV selective rebuilds.
 | 
			
		||||
  Preserve existing mirror region size when using --repair.
 | 
			
		||||
  Forbid stripe parameters with lvconvert --repair.
 | 
			
		||||
  Unify stripe size validation into get_stripe_params to catch missing cases.
 | 
			
		||||
  Further lvconvert validation logic refactoring.
 | 
			
		||||
 | 
			
		||||
Version 2.02.162 - 28th July 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Extend vg_validate also to check raid configurations thoroughly.
 | 
			
		||||
  Support lvconvert -Zn also when doing full cache pool conversion.
 | 
			
		||||
  Suppress not zeroing warn when converting to thin LV for non-zeroing tpool.
 | 
			
		||||
  Fix automatic updates of PV extension headers to newest version.
 | 
			
		||||
  Improve lvconvert --trackchanges validation to require --splitmirrors 1.
 | 
			
		||||
  Add note about lastlog built-in command to lvm man page.
 | 
			
		||||
  Fix unrecognised segtype flag message.
 | 
			
		||||
  lvconvert not clears cache pool metadata ONLY with -Zn.
 | 
			
		||||
  Add allocation/raid_stripe_all_devices to reinstate previous behaviour.
 | 
			
		||||
  Create raid stripes across fixed small numbers of PVs instead of all PVs.
 | 
			
		||||
  Enabled lvconvert --uncache to work with partial VG.
 | 
			
		||||
  Disallow lvconvert --replace with raid0* LVs.
 | 
			
		||||
  Fix some lvmetad changed VG metadata notifications that sent uncommitted data.
 | 
			
		||||
 | 
			
		||||
Version 2.02.161 - 15th July 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Prohibit some lvchange/lvresize that were failing on raid0 volumes.
 | 
			
		||||
  Fix segfaults in complex vgsplits. (2.02.159)
 | 
			
		||||
  Reformat unwieldy lvconvert man page.
 | 
			
		||||
  Allow --force to be passed through to pvcreate from vgcreate. (2.02.144)
 | 
			
		||||
  Fix lvresize of filesystem when LV has already right size (2.02.141)
 | 
			
		||||
  New LVM_LOG_FILE_MAX_LINES env var to limit max size of created logs.
 | 
			
		||||
 | 
			
		||||
Version 2.02.160 - 6th July 2016
 | 
			
		||||
================================
 | 
			
		||||
  Minor fixes from coverity.
 | 
			
		||||
 | 
			
		||||
Version 2.02.159 - 6th July 2016
 | 
			
		||||
================================
 | 
			
		||||
  Add raid0_meta segment type that provides metadata space for raid conversions.
 | 
			
		||||
  Fix created link for a used pool for vgmknode.
 | 
			
		||||
  Introduce and use is_power_of_2 macro.
 | 
			
		||||
  Support conversions between striped and raid0 segment types.
 | 
			
		||||
  Add infrastructure for raid takeover lvconvert options.
 | 
			
		||||
 | 
			
		||||
Version 2.02.158 - 25th June 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Add a more efficient native vgimportclone command to replace the script.
 | 
			
		||||
  Make lvmlockd always attempt to connect to lvmetad if no connection exists.
 | 
			
		||||
  Let lvmetad handle new connections after shutdown signal.
 | 
			
		||||
  Disable lvmetad when vgcfgrestore begins and enable it again after.
 | 
			
		||||
  Make pvscan do activation if lvmetad is configured but not running.
 | 
			
		||||
  Fix rescanning the PVs for a single VG when using lvmetad.
 | 
			
		||||
  Pool metadata lvresize uses now same code as resize of normal volume.
 | 
			
		||||
  Preserve monitoring status when updating thin-pool metadata.
 | 
			
		||||
  Return 0 (inactive) when status cannot be queried in _lv_active().
 | 
			
		||||
  Switch to log_warn() for failing activation status query.
 | 
			
		||||
  Replace vgimportclone script with binary.
 | 
			
		||||
  While lvmetad is shutting down, continue handling all connections cleanly.
 | 
			
		||||
  Refactor lvconvert argument handling code.
 | 
			
		||||
  Notify lvmetad when vgcfgrestore changes VG metadata.
 | 
			
		||||
  Add --logonly option to report only cmd log for a command, not other reports.
 | 
			
		||||
  Add log/command_log_selection to configure default selection used on cmd log.
 | 
			
		||||
  Use 'orphan' object type in cmd log for groups to collect PVs not yet in VGs.
 | 
			
		||||
  Add lvm lastlog command for query and display of last cmd's log in lvm shell.
 | 
			
		||||
  Report per-object return codes via cmd log while processing multiple objects.
 | 
			
		||||
  Annotate processing code with log report hooks for per-object command log.
 | 
			
		||||
  Also pass common printed messages (besides warnings and errors) to log report.
 | 
			
		||||
  Log warnings and errors via report during cmd processing if this is enabled.
 | 
			
		||||
  Make it possible to iterate over internal 'orphan' VGs in process_each_vg fn.
 | 
			
		||||
  Make -S|--select option groupable that allows this option to be repeated.
 | 
			
		||||
  Make -O|--sort option groupable that allows this option to be repeated.
 | 
			
		||||
  Add --configreport option to select report for which next options are applied.
 | 
			
		||||
  Add support for priorities on grouping command arguments.
 | 
			
		||||
  Add report/{pvs,vgs,lvs,pvsegs,segs}_{cols,sort}_full to lvm.conf.
 | 
			
		||||
  Add lvm fullreport command for joined PV, VG, LV and segment report per VG.
 | 
			
		||||
  Integrate report group handling and cmd log report into cmd processing code.
 | 
			
		||||
  Add log/report_command_log to lvm.conf to enable or disable cmd log report.
 | 
			
		||||
  Add log/report_output_format to lvm.conf for default report output format.
 | 
			
		||||
  Recognize --reportformat {basic|json} option to select report output format.
 | 
			
		||||
  Add log/command_log_{sort,cols} to lvm.conf to configure command log report.
 | 
			
		||||
  Add log_object_{type,name,id,group,group_id} fields to cmd log.
 | 
			
		||||
  Add log_{seq_num,type,context,message,errno,ret_code} fields to cmd log.
 | 
			
		||||
  Add CMDLOG report type - a separate report type for command logging.
 | 
			
		||||
 | 
			
		||||
Version 2.02.157 - 17th June 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Change pvscan --cache -aay to scan locally if lvmetad fails.
 | 
			
		||||
 | 
			
		||||
Version 2.02.156 - 11th June 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Don't allow duplicate orphan PVs to be used with vgcreate/vgextend/pvcreate.
 | 
			
		||||
  Improve handling of lvmetad update failures.
 | 
			
		||||
  Yes/No prompt accepts '^[ ^t]*([Yy]([Ee]([Ss]|)|)|[Nn]([Oo]|))[ ^t]*$'.
 | 
			
		||||
  If available, also collect output from lsblk command when running lvmdump -s.
 | 
			
		||||
 | 
			
		||||
Version 2.02.155 - 3rd June 2016
 | 
			
		||||
================================
 | 
			
		||||
  Reject PV tags on pvmove cmdline because only 1 PV is supported. (2.02.141)
 | 
			
		||||
  Fix compilation error when building with configure --disable-devmapper.
 | 
			
		||||
  Fix lvmconfig --type diff to display complete diff if config cascade used.
 | 
			
		||||
  Automatically filter out partitioned loop devices with partscan (losetup -P).
 | 
			
		||||
  Fix lvm devtypes internal error if -S used with field name from pvs/vgs/lvs.
 | 
			
		||||
  When reporting Data%,Snap%,Meta%,Cpy%Sync use single ioctl per LV.
 | 
			
		||||
  Add lvseg_percent_with_info_and_seg_status() for percent retrieval.
 | 
			
		||||
  Enhance internal seg_status handling to understand snapshots better.
 | 
			
		||||
  When refresh failed in suspend, call resume upon error path.
 | 
			
		||||
  Support passthrough cache mode when waiting for clean cache.
 | 
			
		||||
  Check cache status only for 'in-use' cache pools.
 | 
			
		||||
  Extend setup_task() to preset flushing for dm_task object.
 | 
			
		||||
  When checking LV is a merging COW, validate its a COW LV first.
 | 
			
		||||
  Correcting value in copy_percent() for 100%.
 | 
			
		||||
  Update vgreduce to use process_each_vg.
 | 
			
		||||
  Update lvconvert to use process_each_lv.
 | 
			
		||||
  Update pvscan to use process_each_vg for autoactivation.
 | 
			
		||||
  Add basic support for --type raid0 using md.
 | 
			
		||||
  Add support for lvchange --cachemode for cached LV.
 | 
			
		||||
  Fix liblvm2app error handling when setting up context.
 | 
			
		||||
  Delay liblvm2app init in python code until it is needed.
 | 
			
		||||
  Simplify thread locking in lvmetad to fix locking problems.
 | 
			
		||||
  Allow pvremove -ff to remove a duplicate PV.
 | 
			
		||||
  Fix lvm2-activation-generator to read lvm.conf without full command setup.
 | 
			
		||||
  Allow a minimal context to be used in lvm2app for reading lvm.conf.
 | 
			
		||||
 | 
			
		||||
Version 2.02.154 - 14th May 2016
 | 
			
		||||
================================
 | 
			
		||||
  Fix liblvm segfault after failure initialising lvmetad connection.
 | 
			
		||||
  Retry open without O_NOATIME if it fails (not file owner/CAP_FOWNER).
 | 
			
		||||
  Split _report into one fn for options and arguments and one for processing.
 | 
			
		||||
 | 
			
		||||
Version 2.02.153 - 7th May 2016
 | 
			
		||||
===============================
 | 
			
		||||
  Change warning messages related to duplicate PVs.
 | 
			
		||||
  A named device is always processed itself, not switched for a duplicate.
 | 
			
		||||
  Add PV attr "d" and report field "duplicate" for duplicate PVs.
 | 
			
		||||
  Add config setting to disallow VG changes when duplicate PVs exist.
 | 
			
		||||
  Use device size and active LVs to choose the preferred duplicate PV.
 | 
			
		||||
  Disable lvmetad when duplicate PVs are seen.
 | 
			
		||||
  Support --chunksize option also when caching LV when possible.
 | 
			
		||||
  Add function to check for target presence and version via 1 ioctl.
 | 
			
		||||
 | 
			
		||||
Version 2.02.152 - 30th April 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Use any inherited tags when wiping metadata sub LVs to ensure activation.
 | 
			
		||||
  Add str_list_wipe.
 | 
			
		||||
  Improve support for interrupting procesing of volumes during lvchange.
 | 
			
		||||
  Use failed command return code when lvchanging read-only volume.
 | 
			
		||||
  Show creation transaction_id and zeroing state of pool with thin volume.
 | 
			
		||||
  Stop checking for dm_cache_mq policy with cache target 1.9 (alias to smq).
 | 
			
		||||
  Check first /sys/module/dm_* dir existance before using modprobe.
 | 
			
		||||
  Remove mpath from 10-dm.rules, superseded by 11-dm-mpath.rules (mpath>=0.6.0).
 | 
			
		||||
 | 
			
		||||
Version 2.02.151 - 23rd April 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Fix error path after reusing of _setup_task (2.02.150).
 | 
			
		||||
  Fix memory access for empty sysfs values (2.02.149).
 | 
			
		||||
  Disable lvmetad when lvm1 metadata is seen, so commands revert to scanning.
 | 
			
		||||
  Suppress errors when snapshot merge gets delayed because volume is in use.
 | 
			
		||||
  Avoid internal snapshot LV names in messages.
 | 
			
		||||
  Autodetect and use /run/lock dir when available instead of /var/lock.
 | 
			
		||||
  lvchange --refresh for merging thin origin will retry to deactivate snapshot.
 | 
			
		||||
  Recognize in-progress snapshot merge for thin volumes from dm table.
 | 
			
		||||
  Avoid deciding to initiate a pending snapshot merge during resume.
 | 
			
		||||
  Improve retrying lvmetad requests while lvmetad is being updated.
 | 
			
		||||
  Read devices instead of using the lvmetad cache if rescan fails.
 | 
			
		||||
  Move lvmetad token/filter check and device rescan to the start of commands.
 | 
			
		||||
  Don't try deactivating fictional internal LV before snapshot merge. (2.02.105)
 | 
			
		||||
  When not obtaining devs from udev, check they exist before caching them.
 | 
			
		||||
  Detect device mismatch also when compiling without udev support.
 | 
			
		||||
 | 
			
		||||
Version 2.02.150 - 9th April 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Avoid using flushing dm status ioctl when checking for usable DM device.
 | 
			
		||||
  Check for devices without LVM- uuid prefix only with kernels < 3.X.
 | 
			
		||||
  Reuse %FREE size aproximation with lvcreate -l%PVS thin-pool.
 | 
			
		||||
  Allow the lvmdump directory to exist already provided it is empty.
 | 
			
		||||
  Show lvconverted percentage with 2 decimal digits.
 | 
			
		||||
  Fix regression in suspend when repairing --type mirror (2.02.133).
 | 
			
		||||
 | 
			
		||||
Version 2.02.149 - 1st April 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Do not flush thin-pool when checking metadata fullness.
 | 
			
		||||
  Remove spurious error about no value in /sys/dev/block/major:minor/dm/uuid.
 | 
			
		||||
  Fix device mismatch detection for LV if persistent .cache file is used.
 | 
			
		||||
  Fix holder device not being found in /dev while sysfs has it during dev scan.
 | 
			
		||||
 | 
			
		||||
Version 2.02.148 - 26th March 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Introduce TARGET_NAME and MODULE NAME macros.
 | 
			
		||||
  Replace hard-coded module and target names with macros.
 | 
			
		||||
  Add pv_major and pv_minor report fields.
 | 
			
		||||
  Detect and warn about mismatch between devices used and assumed for an LV.
 | 
			
		||||
 | 
			
		||||
Version 2.02.147 - 19th March 2016
 | 
			
		||||
==================================
 | 
			
		||||
  If available, use /proc/self/mountinfo to detect mounted volume in fsadm.
 | 
			
		||||
  Fix resize of stacked raid thin data volume (2.02.141).
 | 
			
		||||
  Fix test for lvremove failure in lvconvert --uncache (2.02.146).
 | 
			
		||||
 | 
			
		||||
Version 2.02.146 - 11th March 2016
 | 
			
		||||
==================================
 | 
			
		||||
  More man page cleanups in lvconvert.
 | 
			
		||||
  Fix makefile vpath in /udev when generating udev rules files.
 | 
			
		||||
  Another attempt to improve VG name parsing for lvconvert (2.02.144).
 | 
			
		||||
  Use new cache status info and skip flushing for failed cache.
 | 
			
		||||
  Support --uncache with missing PVs.
 | 
			
		||||
  Tidy report field names, headings and widths.
 | 
			
		||||
  Add vgscan --notifydbus to send a dbus notification.
 | 
			
		||||
  Add dbus notification from commands after a PV/VG/LV changes state.
 | 
			
		||||
 | 
			
		||||
Version 2.02.145 - 4th March 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Make it possible to use lvremove and lvrename on historical LVs.
 | 
			
		||||
  For historical LVs, report 'none' for lv_layout and 'history' for lv_role.
 | 
			
		||||
  Add full_{ancestors,descendants} fields to report LV ancestry with history.
 | 
			
		||||
  Report (h)istorical state within 5th bit (State) of the lv_attr field.
 | 
			
		||||
  Add lv_historical reporting field to report if LV is historical or not.
 | 
			
		||||
  Add lv_time_removed reporting field to display removal time for hist. LVs.
 | 
			
		||||
  Report lv_name, lv_uuid, vg_name, lv_time for historical LVs.
 | 
			
		||||
  Add --nohistory switch to lvremove to disable history recording on demand.
 | 
			
		||||
  Add -H|--history switch to lvs and lvdisplay to include historical LVs.
 | 
			
		||||
  Create historical LVs out of removed thin snapshot LVs and record in history.
 | 
			
		||||
  Add metadata/lvs_history_retention_time for automatic removal of hist. LVs.
 | 
			
		||||
  Add metadata/record_lvs_history config for switching LV history recording.
 | 
			
		||||
  Add support and infrastructure for tracking historical LVs.
 | 
			
		||||
  Improve lvconvert man page.
 | 
			
		||||
  Add kernel_cache_policy lvs field.
 | 
			
		||||
  Display [unknown] instead of 'unknown device' in pvs output.
 | 
			
		||||
  Fix error path when pvcreate allocation fails (2.02.144).
 | 
			
		||||
  Display [unknown] instead of blank for unknown VG names in pvs output.
 | 
			
		||||
 | 
			
		||||
Version 2.02.144 - 26th February 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Use new PV processing code in pvcreate/vgcreate/vgextend/pvremove.
 | 
			
		||||
  Add new PV processing code that prompts user without locks held.
 | 
			
		||||
  Prevent lvmlockd blocking with new flag requiring sanlock 3.3.0.
 | 
			
		||||
  Only show (u)sed pv_attr char when PV is not (a)llocatable. (2.02.143)
 | 
			
		||||
  Update makefile to generate lcov output also for lvmpolld and lvmlockd.
 | 
			
		||||
  Fix SystemdService lvm2-lvmdbusd.service name.
 | 
			
		||||
  Improve support for env LVM_VG_NAME for reference VG name in lvconvert.
 | 
			
		||||
  Fix regression when lvresize accepted zero sizes. (2.02.141)
 | 
			
		||||
  Always warn user about PV in use even when pvremove uses --force --force.
 | 
			
		||||
  Use uninitialized pool header detection in all cases.
 | 
			
		||||
  Fix read error detection when checking for uninitialized thin-pool header.
 | 
			
		||||
  Fix error path for internal error in lvmetad VG lookup code.
 | 
			
		||||
 | 
			
		||||
Version 2.02.143 - 21st February 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Fix error path when sending thin-pool message fails in update_pool_lv().
 | 
			
		||||
  Support reporting CheckNeeded and Fail state for thin-pool and thin LV.
 | 
			
		||||
  For failing thin-pool and thin volume correctly report percentage as INVALID.
 | 
			
		||||
  Report -1, not 'unkown' for lv_{snapshot_invalid,merge_failed} with --binary.
 | 
			
		||||
  Add configure --enable-dbus-service for an LVM D-Bus service.
 | 
			
		||||
  Replace configure --enable-python_bindings with python2 and python3 versions.
 | 
			
		||||
  If PV belongs to some VG and metadata missing, skip it if system ID is used.
 | 
			
		||||
  Automatically change PV header extension to latest version if writing PV/VG.
 | 
			
		||||
  Identify used PVs in pv_attr field by new 'u' character.
 | 
			
		||||
  Add pv_in_use reporting field to report if PV is used or not.
 | 
			
		||||
  Add pv_ext_vsn reporting field to report PV header extension version.
 | 
			
		||||
  Add protective flag marking PVs as used even if no metadata available.
 | 
			
		||||
 | 
			
		||||
Version 2.02.142 - 15th February 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Fix memory pool corruption in pvmove (2.02.141).
 | 
			
		||||
  Support control of spare metadata creation when repairing thin-pool.
 | 
			
		||||
  Fix config type of 'log/verbose' from bool to int (2.02.99).
 | 
			
		||||
  Fix inverted data LV thinp watermark calc for dmeventd response (2.02.133).
 | 
			
		||||
  Use use_blkid_wiping=0 if not defined in lvm.conf and support not compiled in.
 | 
			
		||||
  Do not check for suspended devices if scanning for lvmetad update.
 | 
			
		||||
  Clear cached bootloader areas when PV format changed.
 | 
			
		||||
  Fix partn table filter with external_device_info_source="udev" and blkid<2.20.
 | 
			
		||||
 | 
			
		||||
Version 2.02.141 - 25th January 2016
 | 
			
		||||
====================================
 | 
			
		||||
  Add metadata/check_pv_device_sizes switch to lvm.conf for device size checks.
 | 
			
		||||
  Warn if device size is less than corresponding PV size in metadata.
 | 
			
		||||
  Cache device sizes internally.
 | 
			
		||||
  Restore support for command breaking in process_each_lv_in_vg() (2.02.118).
 | 
			
		||||
  Use correct mempool when process_each_lv_in_vg() (2.02.118).
 | 
			
		||||
  Fix lvm.8 man to show again prohibited suffixes.
 | 
			
		||||
  Fix configure to set proper use_blkid_wiping if autodetected as disabled.
 | 
			
		||||
  Initialise udev in clvmd for use in device scanning. (2.02.116)
 | 
			
		||||
  Add seg_le_ranges report field for common format when displaying seg devices.
 | 
			
		||||
  Honour report/list_item_separator for seg_metadata_le_ranges report field.
 | 
			
		||||
  Don't mark hidden devs in -o devices,metadata_devices,seg_pe_ranges.(2.02.140)
 | 
			
		||||
  Change LV sizes in seg_pe_ranges report field to match underlying devices.
 | 
			
		||||
  Add kernel_cache_settings report field for cache LV settings used in kernel.
 | 
			
		||||
 | 
			
		||||
Version 2.02.140 - 16th January 2016
 | 
			
		||||
====================================
 | 
			
		||||
  Fix lvm2app to return either 0 or 1 for lvm_vg_is_{clustered,exported}.
 | 
			
		||||
  Add kernel_discards report field to display thin pool discard used in kernel.
 | 
			
		||||
  Correct checking of target presence when driver access is disabled.
 | 
			
		||||
  Eval poolmetadatasize arg earlier in lvresize.
 | 
			
		||||
  Fix vgcfgrestore to respect allocatable attribute of PVs.
 | 
			
		||||
  Add report/mark_hidden_devices to lvm.conf.
 | 
			
		||||
  Use brackets consistently in report fields to mark hidden devices.
 | 
			
		||||
  Restore background polling processing during auto-activation (2.02.119).
 | 
			
		||||
  Fix invalid memory read when reporting cache LV policy_name (2.02.126).
 | 
			
		||||
 | 
			
		||||
Version 2.02.139 - 8th January 2016
 | 
			
		||||
===================================
 | 
			
		||||
  Update lvmlockd with the new VG seqno before devices are suspended.
 | 
			
		||||
  Rework vgrename to use the common processing code in toollib.
 | 
			
		||||
  Make pvs show new devices on the system since the last .cache update.
 | 
			
		||||
  Document F,D and M thin pool health status chars for lv_attr in lvs man page.
 | 
			
		||||
  Also add lvm2-activation{-early,-net}.service systemd status for lvmdump -s.
 | 
			
		||||
 | 
			
		||||
Version 2.02.138 - 14th December 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Support lvrename for hidden (used) cache pools.
 | 
			
		||||
  Fix lvrename for stacked cache pools.
 | 
			
		||||
 | 
			
		||||
Version 2.02.137 - 5th December 2015
 | 
			
		||||
====================================
 | 
			
		||||
  Restore archiving before changing metadata in vgextend (2.02.117).
 | 
			
		||||
  Dropped internal usage of log_suppress(2).
 | 
			
		||||
  Cleaned logging code for buffer size usage.
 | 
			
		||||
  Added internal id_read_format_try() function to check and read valid UUID.
 | 
			
		||||
  Change lvcreate, lvrename, lvresize to use process_each_vg.
 | 
			
		||||
  Change process_each_vg to handle single VG as separate arg.
 | 
			
		||||
  Issue error if ambiguous VG name is supplied in most commands.
 | 
			
		||||
  Make process_each fns always work through full list of known VG names.
 | 
			
		||||
  Use dm_get_status_mirror() instead of individual parsers.
 | 
			
		||||
  Add mem pool arg for check_transient_status() target function.
 | 
			
		||||
  Avoid misleading error with -m is omitted with lvconvert to raid types.
 | 
			
		||||
  Add system_id to vginfo cache.
 | 
			
		||||
 | 
			
		||||
Version 2.02.136 - 28th November 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Add new --sinceversion option for lvmconfig --type new.
 | 
			
		||||
  Fix inactive table loaded for wrapping thin-pool when resizing it.
 | 
			
		||||
  Extend the list of ignored libraries when locking memory.
 | 
			
		||||
 | 
			
		||||
Version 2.02.135 - 23rd November 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Add a model file for Coverity.
 | 
			
		||||
  Show correct error message for unsupported yet cache pool repair.
 | 
			
		||||
  Allow lvconvert cache pools' data and metadata LV to raid.
 | 
			
		||||
  Fix reading of old metadata with missing cache policy or mode settings.
 | 
			
		||||
  Issue error if external_device_info_source=udev and udev db record incomplete.
 | 
			
		||||
  Update lvmetad duplicate VG name handling to use hash function extensions.
 | 
			
		||||
  Detect invalid vgrenames by vgid where the name is unchanged.
 | 
			
		||||
  Fix passing of 32bit values through daemons (mostly lvmlockd).
 | 
			
		||||
  Use local memory pool for whole alloc_handle manipulation.
 | 
			
		||||
  Add missing pointer validation after dm_get_next_target().
 | 
			
		||||
  Do not deref NULL pointer in debug message for _match_pv_tags().
 | 
			
		||||
  Drop unneeded stat() call when checking for sysfs file.
 | 
			
		||||
  Fix memory leak on error path of failing thin-pool percentage check.
 | 
			
		||||
  Add missing test for failing node allocation in lvmetad.
 | 
			
		||||
  Correct configure messages when enabling/disabling lvmlockd.
 | 
			
		||||
 | 
			
		||||
Version 2.02.134 - 9th November 2015
 | 
			
		||||
====================================
 | 
			
		||||
  Refactor some lvmetad code and adjust some duplicate PV messages.
 | 
			
		||||
  No longer repair/wipe VG/PVs if inaccessible because foreign or shared.
 | 
			
		||||
  Pass correct data size to mirror log calc so log can be bigger than 1 extent.
 | 
			
		||||
 | 
			
		||||
Version 2.02.133 - 30th October 2015
 | 
			
		||||
====================================
 | 
			
		||||
  Support repeated -o|--options for reporting commands.
 | 
			
		||||
  Support -o- and -o# for reporting commands to remove and compact fields.
 | 
			
		||||
  Fix missing PVs from pvs output if vgremove is run concurrently.
 | 
			
		||||
  Remove unwanted error message when running pvs/vgs/lvs and vgremove at once.
 | 
			
		||||
  Check newly created VG's metadata do not overlap in metadata ring buffer.
 | 
			
		||||
  Check metadata area size is at least the minimum size defined for the format.
 | 
			
		||||
  Thin pool targets uses low_water_mark from profile.
 | 
			
		||||
  Dropping 'yet' from error of unsupported thick snapshot of snapshots.
 | 
			
		||||
  Do not support unpartitioned DASD devices with CDL formatted with pvcreate.
 | 
			
		||||
  For thins use flush for suspend only when volume size is reduced.
 | 
			
		||||
  Enable code which detects the need of flush during suspend.
 | 
			
		||||
  Ensure --use-policy will resize volume to fit below threshold.
 | 
			
		||||
  Correct percentage evaluation when checking thin-pool over threshold.
 | 
			
		||||
  Fix lvmcache to move PV from VG to orphans if VG is removed and lvmetad used.
 | 
			
		||||
  Fix lvmcache to not cache even invalid info about PV which got removed.
 | 
			
		||||
  Support checking of memlock daemon counter.
 | 
			
		||||
  Allow all log levels to be used with the lvmetad -l option.
 | 
			
		||||
  Add optional shutdown when idle support for lvmetad.
 | 
			
		||||
  Fix missing in-sync progress info while lvconvert used with lvmpolld.
 | 
			
		||||
  Add report/compact_output_cols to lvm.conf to define report cols to compact.
 | 
			
		||||
  Do not change logging in lvm2 library when it's already set.
 | 
			
		||||
  Check for enough space in thin-pool in command before creating new thin.
 | 
			
		||||
  Make libblkid detect all copies of the same signature if use_blkid_wiping=1.
 | 
			
		||||
  Fix vgimportclone with -n to not add number unnecessarily to base VG name.
 | 
			
		||||
  Cleanup vgimportclone script and remove dependency on awk, grep, cut and tr.
 | 
			
		||||
  Add vg_missing_pv_count report field to report number of missing PVs in a VG.
 | 
			
		||||
  Properly identify internal LV holding sanlock locks within lv_role field.
 | 
			
		||||
  Add metadata_devices and seg_metadata_le_ranges report fields for raid vols.
 | 
			
		||||
  Fix lvm2-{activation,clvmd,cmirrord,monitor} service to exec before mounting.
 | 
			
		||||
 | 
			
		||||
Version 2.02.132 - 22nd September 2015
 | 
			
		||||
======================================
 | 
			
		||||
  Fix lvmconf to set locking_type=2 if external locking library is requested.
 | 
			
		||||
  Remove verbose message when rescanning an unchanged device. (2.02.119)
 | 
			
		||||
  Add origin_uuid, mirror_log_uuid, move_pv_uuid, convert_lv_uuid report fields.
 | 
			
		||||
  Add pool_lv_uuid, metadata_lv_uuid, data_lv_uuid reporting fields.
 | 
			
		||||
  Fix PV label processing failure after pvcreate in lvm shell with lvmetad.
 | 
			
		||||
 | 
			
		||||
Version 2.02.131 - 15th September 2015
 | 
			
		||||
======================================
 | 
			
		||||
  Rename 'make install_full_man' to install_all_man and add all_man target.
 | 
			
		||||
  Fix vgimportclone cache_dir path name (2.02.115).
 | 
			
		||||
  Swapping of LV identifiers handles more complex LVs.
 | 
			
		||||
  Use passed list of PVS when allocating space in lvconvert --thinpool.
 | 
			
		||||
  Disallow usage of --stripe and --stripesize when creating cache pool.
 | 
			
		||||
  Warn user when caching raid or thin pool data LV.
 | 
			
		||||
  When layering LV, move LV flags with segments.
 | 
			
		||||
  Ignore persistent cache if configuration changed. (2.02.127)
 | 
			
		||||
  Fix devices/filter to be applied before disk-accessing filters. (2.02.112)
 | 
			
		||||
  Make tags only when requested via 'make tags'.
 | 
			
		||||
  Configure supports --disable-dependency-tracking for one-time builds.
 | 
			
		||||
  Fix usage of configure.h when building in srcdir != builddir.
 | 
			
		||||
 | 
			
		||||
Version 2.02.130 - 5th September 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Fix use of uninitialized device status if reading outdated .cache record.
 | 
			
		||||
  Restore support for --monitor option in lvcreate (2.02.112).
 | 
			
		||||
  Read thin-pool data and metadata percent without flush.
 | 
			
		||||
  Detect blocked thin-pool and avoid scanning their thin volumes.
 | 
			
		||||
  Check if dm device is usable before checking its size (2.02.116).
 | 
			
		||||
  Extend parsing of cache_check version in configure.
 | 
			
		||||
  Make lvpoll error messages visible in lvmpolld's stderr and in syslog.
 | 
			
		||||
  Add 'make install_full_man' to install all man pages regardless of config.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										355
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										355
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							@@ -1,358 +1,13 @@
 | 
			
		||||
Version 1.02.164 - 27th August 2019
 | 
			
		||||
Version 1.02.107 - 
 | 
			
		||||
===================================
 | 
			
		||||
  Add debug of dmsetup udevcomplete with hexa print DM_COOKIE_COMPLETED.
 | 
			
		||||
  Fix versioning of dm_stats_create_region and dm_stats_create_region.
 | 
			
		||||
  Parsing of cache status understand no_discard_passdown.
 | 
			
		||||
 | 
			
		||||
Version 1.02.158 - 13th May 2019
 | 
			
		||||
================================
 | 
			
		||||
 | 
			
		||||
Version 1.02.156 - 22nd March 2019
 | 
			
		||||
==================================
 | 
			
		||||
  Ensure migration_threshold for cache is at least 8 chunks.
 | 
			
		||||
  Enhance ioctl flattening and add parameters only when needed.
 | 
			
		||||
  Add DM_DEVICE_ARM_POLL for API completness matching kernel.
 | 
			
		||||
 | 
			
		||||
Version 1.02.154 - 07th December 2018
 | 
			
		||||
=====================================
 | 
			
		||||
  Do not add parameters for RESUME with DM_DEVICE_CREATE dm task.
 | 
			
		||||
  Fix dmstats report printing no output.
 | 
			
		||||
 | 
			
		||||
Version 1.02.152 - 30th October 2018
 | 
			
		||||
====================================
 | 
			
		||||
  Add hot fix to avoiding locking collision when monitoring thin-pools.
 | 
			
		||||
 | 
			
		||||
Version 1.02.150 - 01 August 2018
 | 
			
		||||
=================================
 | 
			
		||||
  Add vdo plugin for monitoring VDO devices.
 | 
			
		||||
 | 
			
		||||
Version 1.02.149 - 19th July 2018
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
Version 1.02.148 - 18th June 2018
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
Version 1.02.147 - 13th June 2018
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
Version 1.02.147-rc1 - 24th May 2018
 | 
			
		||||
====================================
 | 
			
		||||
  Reuse uname() result for mirror target.
 | 
			
		||||
  Recognize also mounted btrfs through dm_device_has_mounted_fs().
 | 
			
		||||
  Add missing log_error() into dm_stats_populate() returning 0.
 | 
			
		||||
  Avoid calling dm_stats_populat() for DM devices without any stats regions.
 | 
			
		||||
  Support DM_DEBUG_WITH_LINE_NUMBERS envvar for debug msg with source:line.
 | 
			
		||||
  Configured command for thin pool threshold handling gets whole environment.
 | 
			
		||||
  Fix tests for failing dm_snprintf() in stats code.
 | 
			
		||||
  Parsing mirror status accepts 'userspace' keyword in status.
 | 
			
		||||
  Introduce dm_malloc_aligned for page alignment of buffers.
 | 
			
		||||
 | 
			
		||||
Version 1.02.146 - 18th December 2017
 | 
			
		||||
=====================================
 | 
			
		||||
  Activation tree of thin pool skips duplicated check of pool status.
 | 
			
		||||
  Remove code supporting replicator target.
 | 
			
		||||
  Do not ignore failure of _info_by_dev().
 | 
			
		||||
  Propagate delayed resume for pvmove subvolumes.
 | 
			
		||||
  Suppress integrity encryption keys in 'table' output unless --showkeys supplied.
 | 
			
		||||
 | 
			
		||||
Version 1.02.145 - 3rd November 2017
 | 
			
		||||
====================================
 | 
			
		||||
  Keep Install section only in dm-event.socket systemd unit.
 | 
			
		||||
  Issue a specific error with dmsetup status if device is unknown.
 | 
			
		||||
  Fix RT_LIBS reference in generated libdevmapper.pc for pkg-config
 | 
			
		||||
 | 
			
		||||
Version 1.02.144 - 6th October 2017
 | 
			
		||||
===================================
 | 
			
		||||
  Schedule exit when received SIGTERM in dmeventd.
 | 
			
		||||
  Also try to unmount /boot on blkdeactivate -u if on top of supported device.
 | 
			
		||||
  Use blkdeactivate -r wait in blk-availability systemd service/initscript.
 | 
			
		||||
  Add blkdeactivate -r wait option to wait for MD resync/recovery/reshape.
 | 
			
		||||
  Fix blkdeactivate regression with failing DM/MD devs deactivation (1.02.142).
 | 
			
		||||
  Fix typo in blkdeactivate's '--{dm,lvm,mpath}options' option name.
 | 
			
		||||
  Correct return value testing when get reserved values for reporting.
 | 
			
		||||
  Take -S with dmsetup suspend/resume/clear/wipe_table/remove/deps/status/table.
 | 
			
		||||
 | 
			
		||||
Version 1.02.143 - 13th September 2017
 | 
			
		||||
======================================
 | 
			
		||||
  Restore umask when creation of node fails.
 | 
			
		||||
  Add --concise to dmsetup create for many devices with tables in one command.
 | 
			
		||||
  Accept minor number without major in library when it knows dm major number.
 | 
			
		||||
  Introduce single-line concise table output format: dmsetup table --concise
 | 
			
		||||
 | 
			
		||||
Version 1.02.142 - 20th July 2017
 | 
			
		||||
=================================
 | 
			
		||||
  Create /dev/disk/by-part{uuid,label} and gpt-auto-root symlinks with udev.
 | 
			
		||||
 | 
			
		||||
Version 1.02.141 - 28th June 2017
 | 
			
		||||
=================================
 | 
			
		||||
  Fix reusing of dm_task structure for status reading (used by dmeventd).
 | 
			
		||||
  Add dm_percent_to_round_float for adjusted percentage rounding.
 | 
			
		||||
  Reset array with dead rimage devices once raid gets in sync.
 | 
			
		||||
  Drop unneeded --config option from raid dmeventd plugin.
 | 
			
		||||
  dm_get_status_raid() handle better some incosistent md statuses.
 | 
			
		||||
  Accept truncated files in calls to dm_stats_update_regions_from_fd().
 | 
			
		||||
  Restore Warning by 5% increment when thin-pool is over 80% (1.02.138).
 | 
			
		||||
 | 
			
		||||
Version 1.02.140 - 3rd May 2017
 | 
			
		||||
===============================
 | 
			
		||||
  Add missing configure --enable-dmfilemapd status message and fix --disable.
 | 
			
		||||
 | 
			
		||||
Version 1.02.139 - 13th April 2017
 | 
			
		||||
==================================
 | 
			
		||||
  Fix assignment in _target_version() when dm task can't run.
 | 
			
		||||
  Flush stdout on each iteration when using --count or --interval.
 | 
			
		||||
  Show detailed error message when execvp fails while starting dmfilemapd.
 | 
			
		||||
  Fix segmentation fault when dmfilemapd is run with no arguments.
 | 
			
		||||
  Numerous minor dmfilemapd fixes from coverity.
 | 
			
		||||
 | 
			
		||||
Version 1.02.138 - 28th March 2017
 | 
			
		||||
==================================
 | 
			
		||||
  Support additional raid5/6 configurations.
 | 
			
		||||
  Provide dm_tree_node_add_cache_target@base compatible symbol.
 | 
			
		||||
  Support DM_CACHE_FEATURE_METADATA2, new cache metadata format 2.
 | 
			
		||||
  Improve code to handle mode mask for cache nodes.
 | 
			
		||||
  Cache status check for passthrough also require trailing space.
 | 
			
		||||
  Add extra memory page when limiting pthread stack size in dmeventd.
 | 
			
		||||
  Avoids immediate resume when preloaded device is smaller.
 | 
			
		||||
  Do not suppress kernel key description in dmsetup table output for dm-crypt.
 | 
			
		||||
  Support configurable command executed from dmeventd thin plugin.
 | 
			
		||||
  Support new R|r human readable units output format.
 | 
			
		||||
  Thin dmeventd plugin reacts faster on lvextend failure path with umount.
 | 
			
		||||
  Add dm_stats_bind_from_fd() to bind a stats handle from a file descriptor.
 | 
			
		||||
  Do not try call callback when reverting activation on error path.
 | 
			
		||||
  Fix file mapping for extents with physically adjacent extents in dmstats.
 | 
			
		||||
  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.
 | 
			
		||||
  Fix 'dmstats delete' with dmsetup older than v1.02.129
 | 
			
		||||
  Fix stats walk segfault with dmsetup older than v1.02.129
 | 
			
		||||
 | 
			
		||||
Version 1.02.135 - 26th September 2016
 | 
			
		||||
======================================
 | 
			
		||||
  Fix man entry for dmsetup status.
 | 
			
		||||
  Introduce new dm_config_parse_without_dup_node_check().
 | 
			
		||||
  Don't omit last entry in dmstats list --group.
 | 
			
		||||
 | 
			
		||||
Version 1.02.134 - 7th September 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Improve explanation of udev fallback in libdevmapper.h.
 | 
			
		||||
 | 
			
		||||
Version 1.02.133 - 10th August 2016
 | 
			
		||||
===================================
 | 
			
		||||
  Add dm_report_destroy_rows/dm_report_group_output_and_pop_all for lvm shell.
 | 
			
		||||
  Adjust group handling and json production for lvm shell.
 | 
			
		||||
 | 
			
		||||
Version 1.02.132 - 28th July 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Fix json reporting to escape '"' character that may appear in reported string.
 | 
			
		||||
 | 
			
		||||
Version 1.02.131 - 15th July 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Disable queueing on mpath devs in blk-availability systemd service/initscript.
 | 
			
		||||
  Add new -m|--mpathoption disablequeueing to blkdeactivate.
 | 
			
		||||
  Automatically group regions with 'create --segments' unless --nogroup.
 | 
			
		||||
  Fix resource leak when deleting the first member of a group.
 | 
			
		||||
  Allow --bounds with 'create --filemap' for dmstats.
 | 
			
		||||
  Enable creation of filemap regions with histograms.
 | 
			
		||||
  Enable histogram aggregation for regions with more than one area.
 | 
			
		||||
  Enable histogram aggregation for groups of regions.
 | 
			
		||||
  Add a --filemap option to 'dmstats create' to allow mapping of files.
 | 
			
		||||
  Add dm_stats_create_regions_from_fd() to map file extents to regions.
 | 
			
		||||
 | 
			
		||||
Version 1.02.130 - 6th July 2016
 | 
			
		||||
================================
 | 
			
		||||
  Minor fixes from coverity.
 | 
			
		||||
 | 
			
		||||
Version 1.02.129 - 6th July 2016
 | 
			
		||||
================================
 | 
			
		||||
  Update default dmstats field selections for groups.
 | 
			
		||||
  Add 'obj_type', 'group_id', and 'statsname' fields to dmstats reports.
 | 
			
		||||
  Add --area, --region, and --group to dmstats to control object selection.
 | 
			
		||||
  Add --alias, --groupid, --regions to dmstats for group creation and deletion.
 | 
			
		||||
  Add 'group' and 'ungroup' commands to dmstats.
 | 
			
		||||
  Allow dm_stats_delete_group() to optionally delete all group members.
 | 
			
		||||
  Add dm_stats_get_object_type() to return the type of object present.
 | 
			
		||||
  Add dm_stats_walk_init() allowing control of objects visited by walks.
 | 
			
		||||
  Add dm_stats_get_group_descriptor() to return the member list as a string.
 | 
			
		||||
  Introduce dm_stats_get_nr_groups() and dm_stats_group_present().
 | 
			
		||||
  Add dm_stats_{get,set}_alias() to set and retrieve alias names for groups.
 | 
			
		||||
  Add dm_stats_get_group_id() to return the group ID for a given region.
 | 
			
		||||
  Add dm_stats_{create,delete}_group() to allow grouping of stats regions.
 | 
			
		||||
  Add enum-driven dm_stats_get_{metric,counter}() interfaces.
 | 
			
		||||
  Add dm_bitset_parse_list() to parse a string representation of a bitset.
 | 
			
		||||
  Thin dmeventd plugin umounts lvm2 volume only when pool is 95% or more.
 | 
			
		||||
 | 
			
		||||
Version 1.02.128 - 25th June 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Recognize 'all' keyword used in selection as synonym for "" (no selection).
 | 
			
		||||
  Add dm_report_set_selection to set selection for multiple output of report.
 | 
			
		||||
  Add DM_REPORT_OUTPUT_MULTIPLE_TIMES flag for multiple output of same report.
 | 
			
		||||
  Move field width handling/sort init from dm_report_object to dm_report_output.
 | 
			
		||||
  Add _LOG_BYPASS_REPORT flag for bypassing any log report currently set.
 | 
			
		||||
  Introduce DM_REPORT_GROUP_JSON for report group with JSON output format.
 | 
			
		||||
  Introduce DM_REPORT_GROUP_BASIC for report group with basic report output.
 | 
			
		||||
  Introduce DM_REPORT_GROUP_SINGLE for report group having single report only.
 | 
			
		||||
  Add dm_report_group_{create,push,pop,destroy} to support report grouping.
 | 
			
		||||
 | 
			
		||||
Version 1.02.127 - 11th June 2016
 | 
			
		||||
=================================
 | 
			
		||||
 Fix blkdeactivate regression causing skipping of dm + md devices. (1.02.126)
 | 
			
		||||
 | 
			
		||||
Version 1.02.126 - 3rd June 2016
 | 
			
		||||
================================
 | 
			
		||||
  Report passthrough caching mode when parsing cache mode.
 | 
			
		||||
 | 
			
		||||
Version 1.02.125 - 14th May 2016
 | 
			
		||||
================================
 | 
			
		||||
  Show library version in message even if dm driver version is unavailable.
 | 
			
		||||
 | 
			
		||||
Version 1.02.124 - 30th April 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Add dm_udev_wait_immediate to libdevmapper for waiting outside the library.
 | 
			
		||||
 | 
			
		||||
Version 1.02.123 - 23rd April 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Do not strip LVM- when debug reporting not found uuid.
 | 
			
		||||
 | 
			
		||||
Version 1.02.122 - 9th April 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Change log_debug ioctl flags from single characters into words.
 | 
			
		||||
 | 
			
		||||
Version 1.02.121 - 26th March 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Adjust raid status function.
 | 
			
		||||
 | 
			
		||||
Version 1.02.120 - 11th March 2016
 | 
			
		||||
==================================
 | 
			
		||||
  Improve parsing of cache status and report Fail, Error, needs_check, ro.
 | 
			
		||||
 | 
			
		||||
Version 1.02.119 - 4th March 2016
 | 
			
		||||
=================================
 | 
			
		||||
  Fix dm_config_write_node and variants to return error on subsection failures.
 | 
			
		||||
  Remove 4096 char limit due to buffer size if writing dm_config_node.
 | 
			
		||||
 | 
			
		||||
Version 1.02.118 - 26th February 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Fix string boundary check in _get_canonical_field_name().
 | 
			
		||||
  Always initialized hist struct in _stats_parse_histogram().
 | 
			
		||||
 | 
			
		||||
Version 1.02.117 - 21st February 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Improve status parsing for thin-pool and thin devices.
 | 
			
		||||
 | 
			
		||||
Version 1.02.116 - 15th February 2016
 | 
			
		||||
=====================================
 | 
			
		||||
  Use fully aligned allocations for dm_pool_strdup/strndup() (1.02.64).
 | 
			
		||||
  Fix thin-pool table parameter feature order to match kernel output.
 | 
			
		||||
 | 
			
		||||
Version 1.02.115 - 25th January 2016
 | 
			
		||||
====================================
 | 
			
		||||
  Fix man page for dmsetup udevcreatecookie.
 | 
			
		||||
 | 
			
		||||
Version 1.02.114 - 14th December 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Better support for dmsetup static linkage.
 | 
			
		||||
  Extend validity checks on dmeventd client socket.
 | 
			
		||||
 | 
			
		||||
Version 1.02.113 - 5th December 2015
 | 
			
		||||
====================================
 | 
			
		||||
  Mirror plugin in dmeventd uses dm_get_status_mirror().
 | 
			
		||||
  Add dm_get_status_mirror() for parsing mirror status line.
 | 
			
		||||
 | 
			
		||||
Version 1.02.112 - 28th November 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Show error message when trying to create unsupported raid type.
 | 
			
		||||
  Improve preloading sequence of an active thin-pool target.
 | 
			
		||||
  Drop extra space from cache target line to fix unneded table reloads.
 | 
			
		||||
 | 
			
		||||
Version 1.02.111 - 23rd November 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Extend dm_hash to support multiple values with the same key.
 | 
			
		||||
  Add missing check for allocation inside dm_split_lvm_name().
 | 
			
		||||
  Test dm_task_get_message_response for !NULL in dm_stats_print_region().
 | 
			
		||||
  Add checks for failing dm_stats_create() in dmsetup.
 | 
			
		||||
  Add missing fifo close when failed to initialize client connection.
 | 
			
		||||
 | 
			
		||||
Version 1.02.110 - 30th October 2015
 | 
			
		||||
====================================
 | 
			
		||||
  Disable thin monitoring plugin when it fails too often (>10 times).
 | 
			
		||||
  Fix/restore parsing of empty field '-' when processing dmeventd event.
 | 
			
		||||
  Enhance dm_tree_node_size_changed() to recognize size reduction.
 | 
			
		||||
  Support exit on idle for dmenventd (1 hour).
 | 
			
		||||
  Add support to allow unmonitor device from plugin itself.
 | 
			
		||||
  New design for thread co-operation in dmeventd.
 | 
			
		||||
  Dmeventd read device status with 'noflush'.
 | 
			
		||||
  Dmeventd closes control device when no device is monitored.
 | 
			
		||||
  Thin plugin for dmeventd improved percentage usage.
 | 
			
		||||
  Snapshot plugin for dmeventd improved percentage usage.
 | 
			
		||||
  Add dm_hold_control_dev to allow holding of control device open.
 | 
			
		||||
  Add dm_report_compact_given_fields to remove given empty fields from report.
 | 
			
		||||
  Use libdm status parsing and local mem raid dmeventd plugin.
 | 
			
		||||
  Use local mem pool and lock only lvm2 execution for mirror dmeventd plugin.
 | 
			
		||||
  Lock protect only lvm2 execution for snapshot and thin dmeventd plugin.
 | 
			
		||||
  Use local mempool for raid and mirror plugins.
 | 
			
		||||
  Reworked thread initialization for dmeventd plugins.
 | 
			
		||||
  Dmeventd handles snapshot overflow for now equally as invalid.
 | 
			
		||||
  Convert dmeventd to use common logging macro system from libdm.
 | 
			
		||||
  Return -ENOMEM when device registration fails instead of 0 (=success).
 | 
			
		||||
  Enforce writethrough mode for cleaner policy.
 | 
			
		||||
  Add support for recognition and deactivation of MD devices to blkdeactivate.
 | 
			
		||||
  Move target status functions out of libdm-deptree.
 | 
			
		||||
  Correct use of max_write_behind parameter when generating raid target line.
 | 
			
		||||
  Fix dm-event systemd service to make sure it is executed before mounting.
 | 
			
		||||
 | 
			
		||||
Version 1.02.109 - 22nd September 2015
 | 
			
		||||
======================================
 | 
			
		||||
  Update man pages for dmsetup and dmstats.
 | 
			
		||||
  Improve help text for dmsetup.
 | 
			
		||||
  Use --noflush and --nolockfs when removing device with --force.
 | 
			
		||||
  Parse new Overflow status string for snapshot target.
 | 
			
		||||
  Check dir path components are valid if using dm_create_dir, error out if not.
 | 
			
		||||
  Fix /dev/mapper handling to remove dangling entries if symlinks are found.
 | 
			
		||||
  Make it possible to use blank value as selection for string list report field.
 | 
			
		||||
 | 
			
		||||
Version 1.02.108 - 15th September 2015
 | 
			
		||||
======================================
 | 
			
		||||
  Do not check for full thin pool when activating without messages (1.02.107).
 | 
			
		||||
 | 
			
		||||
Version 1.02.107 - 5th September 2015
 | 
			
		||||
=====================================
 | 
			
		||||
  Parse thin-pool status with one single routine internally.
 | 
			
		||||
  Add --histogram to select default histogram fields for list and report.
 | 
			
		||||
  Add report fields for displaying latency histogram configuration and data.
 | 
			
		||||
  Add dmstats --bounds to specify histogram boundaries for a new region.
 | 
			
		||||
  Add dm_histogram_to_string() to format histogram data in string form.
 | 
			
		||||
  Add public methods to libdm to access numerical histogram config and data.
 | 
			
		||||
  Parse and store histogram data in dm_stats_list() and dm_stats_populate().
 | 
			
		||||
  Add an argument to specify histogram bounds to dm_stats_create_region().
 | 
			
		||||
  Add dm_histogram_bounds_from_{string,uint64_t}() to parse histogram bounds.
 | 
			
		||||
  Add dm_histogram handle type to represent a latency histogram and its bounds.
 | 
			
		||||
  Fix devmapper.pc pkgconfig file to not reference non-existent rt.pc file.
 | 
			
		||||
  Reinstate dm_task_get_info@Base to libdevmapper exports. (1.02.106)
 | 
			
		||||
 | 
			
		||||
Version 1.02.106 - 26th August 2015
 | 
			
		||||
===================================
 | 
			
		||||
  Add basic report fields for displaying latency histogram data.
 | 
			
		||||
  Add dmstats --histogram to specify histogram boundaries for a region.
 | 
			
		||||
  Add public methods to libdm-stats to access numerical histogram data.
 | 
			
		||||
  Add the ability to parse histogram data into numeric form to libdm-stats.
 | 
			
		||||
  Add 'precise' column to statistics reports.
 | 
			
		||||
  Add --precise switch to 'dmstats create' to request nanosecond counters.
 | 
			
		||||
  Add precise argument to dm_stats_create_region().
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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])
 | 
			
		||||
])
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										510
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										510
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							@@ -12,120 +12,32 @@
 | 
			
		||||
# PARTICULAR PURPOSE.
 | 
			
		||||
 | 
			
		||||
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#     http://www.gnu.org/software/autoconf-archive/ax_python_module.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
 | 
			
		||||
# serial 1 (pkg-config-0.24)
 | 
			
		||||
# 
 | 
			
		||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
#   AX_PYTHON_MODULE(modname[, fatal, python])
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
#   Checks for Python module.
 | 
			
		||||
#
 | 
			
		||||
#   If fatal is non-empty then absence of a module will trigger an error.
 | 
			
		||||
#   The third parameter can either be "python" for Python 2 or "python3" for
 | 
			
		||||
#   Python 3; defaults to Python 3.
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2008 Andrew Collier
 | 
			
		||||
#
 | 
			
		||||
#   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.
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
#serial 8
 | 
			
		||||
 | 
			
		||||
AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE])
 | 
			
		||||
AC_DEFUN([AX_PYTHON_MODULE],[
 | 
			
		||||
    if test -z $PYTHON;
 | 
			
		||||
    then
 | 
			
		||||
        if test -z "$3";
 | 
			
		||||
        then
 | 
			
		||||
            PYTHON="python3"
 | 
			
		||||
        else
 | 
			
		||||
            PYTHON="$3"
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
    PYTHON_NAME=`basename $PYTHON`
 | 
			
		||||
    AC_MSG_CHECKING($PYTHON_NAME module: $1)
 | 
			
		||||
    $PYTHON -c "import $1" 2>/dev/null
 | 
			
		||||
    if test $? -eq 0;
 | 
			
		||||
    then
 | 
			
		||||
        AC_MSG_RESULT(yes)
 | 
			
		||||
        eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
 | 
			
		||||
    else
 | 
			
		||||
        AC_MSG_RESULT(no)
 | 
			
		||||
        eval AS_TR_CPP(HAVE_PYMOD_$1)=no
 | 
			
		||||
        #
 | 
			
		||||
        if test -n "$2"
 | 
			
		||||
        then
 | 
			
		||||
            AC_MSG_ERROR(failed to find required module $1)
 | 
			
		||||
            exit 1
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
dnl pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
 | 
			
		||||
dnl serial 11 (pkg-config-0.29)
 | 
			
		||||
dnl
 | 
			
		||||
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])
 | 
			
		||||
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 +59,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 +80,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 +93,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 +104,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 +170,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 +190,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 +210,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,258 +225,6 @@ _PKG_CONFIG([$1], [variable="][$3]["], [$2])
 | 
			
		||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
 | 
			
		||||
 | 
			
		||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
 | 
			
		||||
])dnl PKG_CHECK_VAR
 | 
			
		||||
 | 
			
		||||
# 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,
 | 
			
		||||
# with or without modifications, as long as this notice is preserved.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
 | 
			
		||||
# ---------------------------------------------------------------------------
 | 
			
		||||
# Adds support for distributing Python modules and packages.  To
 | 
			
		||||
# install modules, copy them to $(pythondir), using the python_PYTHON
 | 
			
		||||
# automake variable.  To install a package with the same name as the
 | 
			
		||||
# automake package, install to $(pkgpythondir), or use the
 | 
			
		||||
# pkgpython_PYTHON automake variable.
 | 
			
		||||
#
 | 
			
		||||
# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
 | 
			
		||||
# locations to install python extension modules (shared libraries).
 | 
			
		||||
# Another macro is required to find the appropriate flags to compile
 | 
			
		||||
# extension modules.
 | 
			
		||||
#
 | 
			
		||||
# If your package is configured with a different prefix to python,
 | 
			
		||||
# users will have to add the install directory to the PYTHONPATH
 | 
			
		||||
# environment variable, or create a .pth file (see the python
 | 
			
		||||
# documentation for details).
 | 
			
		||||
#
 | 
			
		||||
# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
 | 
			
		||||
# cause an error if the version of python installed on the system
 | 
			
		||||
# doesn't meet the requirement.  MINIMUM-VERSION should consist of
 | 
			
		||||
# numbers and dots only.
 | 
			
		||||
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 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])
 | 
			
		||||
 | 
			
		||||
  m4_if([$1],[],[
 | 
			
		||||
    dnl No version check is needed.
 | 
			
		||||
    # Find any Python interpreter.
 | 
			
		||||
    if test -z "$PYTHON"; then
 | 
			
		||||
      AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
 | 
			
		||||
    fi
 | 
			
		||||
    am_display_PYTHON=python
 | 
			
		||||
  ], [
 | 
			
		||||
    dnl A version check is needed.
 | 
			
		||||
    if test -n "$PYTHON"; then
 | 
			
		||||
      # If the user set $PYTHON, use it and don't search something else.
 | 
			
		||||
      AC_MSG_CHECKING([whether $PYTHON version is >= $1])
 | 
			
		||||
      AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
 | 
			
		||||
			      [AC_MSG_RESULT([yes])],
 | 
			
		||||
			      [AC_MSG_RESULT([no])
 | 
			
		||||
			       AC_MSG_ERROR([Python interpreter is too old])])
 | 
			
		||||
      am_display_PYTHON=$PYTHON
 | 
			
		||||
    else
 | 
			
		||||
      # Otherwise, try each interpreter until we find one that satisfies
 | 
			
		||||
      # VERSION.
 | 
			
		||||
      AC_CACHE_CHECK([for a Python interpreter with version >= $1],
 | 
			
		||||
	[am_cv_pathless_PYTHON],[
 | 
			
		||||
	for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
 | 
			
		||||
	  test "$am_cv_pathless_PYTHON" = none && break
 | 
			
		||||
	  AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
 | 
			
		||||
	done])
 | 
			
		||||
      # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
 | 
			
		||||
      if test "$am_cv_pathless_PYTHON" = none; then
 | 
			
		||||
	PYTHON=:
 | 
			
		||||
      else
 | 
			
		||||
        AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
 | 
			
		||||
      fi
 | 
			
		||||
      am_display_PYTHON=$am_cv_pathless_PYTHON
 | 
			
		||||
    fi
 | 
			
		||||
  ])
 | 
			
		||||
 | 
			
		||||
  if test "$PYTHON" = :; then
 | 
			
		||||
  dnl Run any user-specified action, or abort.
 | 
			
		||||
    m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
 | 
			
		||||
  else
 | 
			
		||||
 | 
			
		||||
  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; 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
 | 
			
		||||
  dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX.  These are made
 | 
			
		||||
  dnl distinct variables so they can be overridden if need be.  However,
 | 
			
		||||
  dnl general consensus is that you shouldn't need this ability.
 | 
			
		||||
 | 
			
		||||
  AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
 | 
			
		||||
  AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
 | 
			
		||||
 | 
			
		||||
  dnl At times (like when building shared libraries) you may want
 | 
			
		||||
  dnl to know which OS platform Python thinks this is.
 | 
			
		||||
 | 
			
		||||
  AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
 | 
			
		||||
    [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
 | 
			
		||||
  AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
 | 
			
		||||
 | 
			
		||||
  # Just factor out some code duplication.
 | 
			
		||||
  am_python_setup_sysconfig="\
 | 
			
		||||
import sys
 | 
			
		||||
# Prefer sysconfig over distutils.sysconfig, for better compatibility
 | 
			
		||||
# with python 3.x.  See automake bug#10227.
 | 
			
		||||
try:
 | 
			
		||||
    import sysconfig
 | 
			
		||||
except ImportError:
 | 
			
		||||
    can_use_sysconfig = 0
 | 
			
		||||
else:
 | 
			
		||||
    can_use_sysconfig = 1
 | 
			
		||||
# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
 | 
			
		||||
# <https://github.com/pypa/virtualenv/issues/118>
 | 
			
		||||
try:
 | 
			
		||||
    from platform import python_implementation
 | 
			
		||||
    if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7':
 | 
			
		||||
        can_use_sysconfig = 0
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass"
 | 
			
		||||
 | 
			
		||||
  dnl Set up 4 directories:
 | 
			
		||||
 | 
			
		||||
  dnl pythondir -- where to install python scripts.  This is the
 | 
			
		||||
  dnl   site-packages directory, not the python standard library
 | 
			
		||||
  dnl   directory like in previous automake betas.  This behavior
 | 
			
		||||
  dnl   is more consistent with lispdir.m4 for example.
 | 
			
		||||
  dnl Query distutils for this directory.
 | 
			
		||||
  AC_CACHE_CHECK([for $am_display_PYTHON script directory],
 | 
			
		||||
    [am_cv_python_pythondir],
 | 
			
		||||
    [if test "x$prefix" = xNONE
 | 
			
		||||
     then
 | 
			
		||||
       am_py_prefix=$ac_default_prefix
 | 
			
		||||
     else
 | 
			
		||||
       am_py_prefix=$prefix
 | 
			
		||||
     fi
 | 
			
		||||
     am_cv_python_pythondir=`$PYTHON -c "
 | 
			
		||||
$am_python_setup_sysconfig
 | 
			
		||||
if can_use_sysconfig:
 | 
			
		||||
    sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
 | 
			
		||||
else:
 | 
			
		||||
    from distutils import sysconfig
 | 
			
		||||
    sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
 | 
			
		||||
sys.stdout.write(sitedir)"`
 | 
			
		||||
     case $am_cv_python_pythondir in
 | 
			
		||||
     $am_py_prefix*)
 | 
			
		||||
       am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
 | 
			
		||||
       am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
 | 
			
		||||
       ;;
 | 
			
		||||
     *)
 | 
			
		||||
       case $am_py_prefix in
 | 
			
		||||
         /usr|/System*) ;;
 | 
			
		||||
         *)
 | 
			
		||||
	  am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
 | 
			
		||||
	  ;;
 | 
			
		||||
       esac
 | 
			
		||||
       ;;
 | 
			
		||||
     esac
 | 
			
		||||
    ])
 | 
			
		||||
  AC_SUBST([pythondir], [$am_cv_python_pythondir])
 | 
			
		||||
 | 
			
		||||
  dnl pkgpythondir -- $PACKAGE directory under pythondir.  Was
 | 
			
		||||
  dnl   PYTHON_SITE_PACKAGE in previous betas, but this naming is
 | 
			
		||||
  dnl   more consistent with the rest of automake.
 | 
			
		||||
 | 
			
		||||
  AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
 | 
			
		||||
 | 
			
		||||
  dnl pyexecdir -- directory for installing python extension modules
 | 
			
		||||
  dnl   (shared libraries)
 | 
			
		||||
  dnl Query distutils for this directory.
 | 
			
		||||
  AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
 | 
			
		||||
    [am_cv_python_pyexecdir],
 | 
			
		||||
    [if test "x$exec_prefix" = xNONE
 | 
			
		||||
     then
 | 
			
		||||
       am_py_exec_prefix=$am_py_prefix
 | 
			
		||||
     else
 | 
			
		||||
       am_py_exec_prefix=$exec_prefix
 | 
			
		||||
     fi
 | 
			
		||||
     am_cv_python_pyexecdir=`$PYTHON -c "
 | 
			
		||||
$am_python_setup_sysconfig
 | 
			
		||||
if can_use_sysconfig:
 | 
			
		||||
    sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
 | 
			
		||||
else:
 | 
			
		||||
    from distutils import sysconfig
 | 
			
		||||
    sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
 | 
			
		||||
sys.stdout.write(sitedir)"`
 | 
			
		||||
     case $am_cv_python_pyexecdir in
 | 
			
		||||
     $am_py_exec_prefix*)
 | 
			
		||||
       am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
 | 
			
		||||
       am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
 | 
			
		||||
       ;;
 | 
			
		||||
     *)
 | 
			
		||||
       case $am_py_exec_prefix in
 | 
			
		||||
         /usr|/System*) ;;
 | 
			
		||||
         *)
 | 
			
		||||
	   am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
 | 
			
		||||
	   ;;
 | 
			
		||||
       esac
 | 
			
		||||
       ;;
 | 
			
		||||
     esac
 | 
			
		||||
    ])
 | 
			
		||||
  AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
 | 
			
		||||
 | 
			
		||||
  dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
 | 
			
		||||
 | 
			
		||||
  AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
 | 
			
		||||
 | 
			
		||||
  dnl Run any user-specified action.
 | 
			
		||||
  $2
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
 | 
			
		||||
# ---------------------------------------------------------------------------
 | 
			
		||||
# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
 | 
			
		||||
# Run ACTION-IF-FALSE otherwise.
 | 
			
		||||
# This test uses sys.hexversion instead of the string equivalent (first
 | 
			
		||||
# word of sys.version), in order to cope with versions such as 2.2c1.
 | 
			
		||||
# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
 | 
			
		||||
AC_DEFUN([AM_PYTHON_CHECK_VERSION],
 | 
			
		||||
 [prog="import sys
 | 
			
		||||
# split strings by '.' and convert to numeric.  Append some zeros
 | 
			
		||||
# because we need at least 4 digits for the hex conversion.
 | 
			
		||||
# map returns an iterator in Python 3.0 and a list in 2.x
 | 
			
		||||
minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
 | 
			
		||||
minverhex = 0
 | 
			
		||||
# xrange is not present in Python 3.0 and range returns an iterator
 | 
			
		||||
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-2014 Free Software Foundation, Inc.
 | 
			
		||||
#
 | 
			
		||||
# This file is free software; the Free Software Foundation
 | 
			
		||||
# gives unlimited permission to copy and/or distribute it,
 | 
			
		||||
# with or without modifications, as long as this notice is preserved.
 | 
			
		||||
 | 
			
		||||
# AM_RUN_LOG(COMMAND)
 | 
			
		||||
# -------------------
 | 
			
		||||
# Run COMMAND, save the exit status in ac_status, and log it.
 | 
			
		||||
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
 | 
			
		||||
AC_DEFUN([AM_RUN_LOG],
 | 
			
		||||
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
 | 
			
		||||
   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
 | 
			
		||||
   ac_status=$?
 | 
			
		||||
   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
 | 
			
		||||
   (exit $ac_status); }])
 | 
			
		||||
])# PKG_CHECK_VAR
 | 
			
		||||
 | 
			
		||||
m4_include([acinclude.m4])
 | 
			
		||||
 
 | 
			
		||||
@@ -1,170 +0,0 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
# py-compile - Compile a Python program
 | 
			
		||||
 | 
			
		||||
scriptversion=2011-06-08.12; # UTC
 | 
			
		||||
 | 
			
		||||
# Copyright (C) 2000-2014 Free Software Foundation, Inc.
 | 
			
		||||
 | 
			
		||||
# 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, 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, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
# This file is maintained in Automake, please report
 | 
			
		||||
# bugs to <bug-automake@gnu.org> or send patches to
 | 
			
		||||
# <automake-patches@gnu.org>.
 | 
			
		||||
 | 
			
		||||
if [ -z "$PYTHON" ]; then
 | 
			
		||||
  PYTHON=python
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
me=py-compile
 | 
			
		||||
 | 
			
		||||
usage_error ()
 | 
			
		||||
{
 | 
			
		||||
  echo "$me: $*" >&2
 | 
			
		||||
  echo "Try '$me --help' for more information." >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
basedir=
 | 
			
		||||
destdir=
 | 
			
		||||
while test $# -ne 0; do
 | 
			
		||||
  case "$1" in
 | 
			
		||||
    --basedir)
 | 
			
		||||
      if test $# -lt 2; then
 | 
			
		||||
        usage_error "option '--basedir' requires an argument"
 | 
			
		||||
      else
 | 
			
		||||
        basedir=$2
 | 
			
		||||
      fi
 | 
			
		||||
      shift
 | 
			
		||||
      ;;
 | 
			
		||||
    --destdir)
 | 
			
		||||
      if test $# -lt 2; then
 | 
			
		||||
        usage_error "option '--destdir' requires an argument"
 | 
			
		||||
      else
 | 
			
		||||
        destdir=$2
 | 
			
		||||
      fi
 | 
			
		||||
      shift
 | 
			
		||||
      ;;
 | 
			
		||||
    -h|--help)
 | 
			
		||||
      cat <<\EOF
 | 
			
		||||
Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..."
 | 
			
		||||
 | 
			
		||||
Byte compile some python scripts FILES.  Use --destdir to specify any
 | 
			
		||||
leading directory path to the FILES that you don't want to include in the
 | 
			
		||||
byte compiled file.  Specify --basedir for any additional path information you
 | 
			
		||||
do want to be shown in the byte compiled file.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
  py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py
 | 
			
		||||
 | 
			
		||||
Report bugs to <bug-automake@gnu.org>.
 | 
			
		||||
EOF
 | 
			
		||||
      exit $?
 | 
			
		||||
      ;;
 | 
			
		||||
    -v|--version)
 | 
			
		||||
      echo "$me $scriptversion"
 | 
			
		||||
      exit $?
 | 
			
		||||
      ;;
 | 
			
		||||
    --)
 | 
			
		||||
      shift
 | 
			
		||||
      break
 | 
			
		||||
      ;;
 | 
			
		||||
    -*)
 | 
			
		||||
      usage_error "unrecognized option '$1'"
 | 
			
		||||
      ;;
 | 
			
		||||
    *)
 | 
			
		||||
      break
 | 
			
		||||
      ;;
 | 
			
		||||
  esac
 | 
			
		||||
  shift
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
files=$*
 | 
			
		||||
if test -z "$files"; then
 | 
			
		||||
    usage_error "no files given"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# if basedir was given, then it should be prepended to filenames before
 | 
			
		||||
# byte compilation.
 | 
			
		||||
if [ -z "$basedir" ]; then
 | 
			
		||||
    pathtrans="path = file"
 | 
			
		||||
else
 | 
			
		||||
    pathtrans="path = os.path.join('$basedir', file)"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# if destdir was given, then it needs to be prepended to the filename to
 | 
			
		||||
# byte compile but not go into the compiled file.
 | 
			
		||||
if [ -z "$destdir" ]; then
 | 
			
		||||
    filetrans="filepath = path"
 | 
			
		||||
else
 | 
			
		||||
    filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$PYTHON -c "
 | 
			
		||||
import sys, os, py_compile, imp
 | 
			
		||||
 | 
			
		||||
files = '''$files'''
 | 
			
		||||
 | 
			
		||||
sys.stdout.write('Byte-compiling python modules...\n')
 | 
			
		||||
for file in files.split():
 | 
			
		||||
    $pathtrans
 | 
			
		||||
    $filetrans
 | 
			
		||||
    if not os.path.exists(filepath) or not (len(filepath) >= 3
 | 
			
		||||
                                            and filepath[-3:] == '.py'):
 | 
			
		||||
	    continue
 | 
			
		||||
    sys.stdout.write(file)
 | 
			
		||||
    sys.stdout.flush()
 | 
			
		||||
    if hasattr(imp, 'get_tag'):
 | 
			
		||||
        py_compile.compile(filepath, imp.cache_from_source(filepath), path)
 | 
			
		||||
    else:
 | 
			
		||||
        py_compile.compile(filepath, filepath + 'c', path)
 | 
			
		||||
sys.stdout.write('\n')" || exit $?
 | 
			
		||||
 | 
			
		||||
# this will fail for python < 1.5, but that doesn't matter ...
 | 
			
		||||
$PYTHON -O -c "
 | 
			
		||||
import sys, os, py_compile, imp
 | 
			
		||||
 | 
			
		||||
# pypy does not use .pyo optimization
 | 
			
		||||
if hasattr(sys, 'pypy_translation_info'):
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
 | 
			
		||||
files = '''$files'''
 | 
			
		||||
sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n')
 | 
			
		||||
for file in files.split():
 | 
			
		||||
    $pathtrans
 | 
			
		||||
    $filetrans
 | 
			
		||||
    if not os.path.exists(filepath) or not (len(filepath) >= 3
 | 
			
		||||
                                            and filepath[-3:] == '.py'):
 | 
			
		||||
	    continue
 | 
			
		||||
    sys.stdout.write(file)
 | 
			
		||||
    sys.stdout.flush()
 | 
			
		||||
    if hasattr(imp, 'get_tag'):
 | 
			
		||||
        py_compile.compile(filepath, imp.cache_from_source(filepath, False), path)
 | 
			
		||||
    else:
 | 
			
		||||
        py_compile.compile(filepath, filepath + 'o', path)
 | 
			
		||||
sys.stdout.write('\n')" 2>/dev/null || :
 | 
			
		||||
 | 
			
		||||
# Local Variables:
 | 
			
		||||
# mode: shell-script
 | 
			
		||||
# sh-indentation: 2
 | 
			
		||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
 | 
			
		||||
# time-stamp-start: "scriptversion="
 | 
			
		||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
 | 
			
		||||
# time-stamp-time-zone: "UTC"
 | 
			
		||||
# time-stamp-end: "; # UTC"
 | 
			
		||||
# End:
 | 
			
		||||
@@ -1,862 +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>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
enum node_type {
 | 
			
		||||
	UNSET = 0,
 | 
			
		||||
	VALUE,
 | 
			
		||||
	VALUE_CHAIN,
 | 
			
		||||
	PREFIX_CHAIN,
 | 
			
		||||
	NODE4,
 | 
			
		||||
	NODE16,
 | 
			
		||||
	NODE48,
 | 
			
		||||
	NODE256
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct value {
 | 
			
		||||
	enum node_type type;
 | 
			
		||||
	union radix_value value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This is used for entries that have a key which is a prefix of another key.
 | 
			
		||||
struct value_chain {
 | 
			
		||||
	union radix_value value;
 | 
			
		||||
	struct value child;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct prefix_chain {
 | 
			
		||||
	struct value child;
 | 
			
		||||
	unsigned len;
 | 
			
		||||
	uint8_t prefix[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct node4 {
 | 
			
		||||
	uint32_t nr_entries;
 | 
			
		||||
	uint8_t keys[4];
 | 
			
		||||
	struct value values[4];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct node16 {
 | 
			
		||||
	uint32_t nr_entries;
 | 
			
		||||
	uint8_t keys[16];
 | 
			
		||||
	struct value values[16];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct node48 {
 | 
			
		||||
	uint32_t nr_entries;
 | 
			
		||||
	uint8_t keys[256];
 | 
			
		||||
	struct value values[48];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct node256 {
 | 
			
		||||
        uint32_t nr_entries;
 | 
			
		||||
	struct value values[256];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct radix_tree {
 | 
			
		||||
	unsigned nr_entries;
 | 
			
		||||
	struct value root;
 | 
			
		||||
	radix_value_dtr dtr;
 | 
			
		||||
	void *dtr_context;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
struct radix_tree *radix_tree_create(radix_value_dtr dtr, void *dtr_context)
 | 
			
		||||
{
 | 
			
		||||
	struct radix_tree *rt = malloc(sizeof(*rt));
 | 
			
		||||
 | 
			
		||||
	if (rt) {
 | 
			
		||||
		rt->nr_entries = 0;
 | 
			
		||||
		rt->root.type = UNSET;
 | 
			
		||||
		rt->dtr = dtr;
 | 
			
		||||
		rt->dtr_context = dtr_context;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void _dtr(struct radix_tree *rt, union radix_value v)
 | 
			
		||||
{
 | 
			
		||||
	if (rt->dtr)
 | 
			
		||||
        	rt->dtr(rt->dtr_context, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the number of values removed
 | 
			
		||||
static unsigned _free_node(struct radix_tree *rt, struct value v)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i, nr = 0;
 | 
			
		||||
	struct value_chain *vc;
 | 
			
		||||
	struct prefix_chain *pc;
 | 
			
		||||
	struct node4 *n4;
 | 
			
		||||
	struct node16 *n16;
 | 
			
		||||
	struct node48 *n48;
 | 
			
		||||
	struct node256 *n256;
 | 
			
		||||
 | 
			
		||||
	switch (v.type) {
 | 
			
		||||
	case UNSET:
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case VALUE:
 | 
			
		||||
        	_dtr(rt, v.value);
 | 
			
		||||
        	nr = 1;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case VALUE_CHAIN:
 | 
			
		||||
		vc = v.value.ptr;
 | 
			
		||||
		_dtr(rt, vc->value);
 | 
			
		||||
		nr = 1 + _free_node(rt, vc->child);
 | 
			
		||||
		free(vc);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case PREFIX_CHAIN:
 | 
			
		||||
		pc = v.value.ptr;
 | 
			
		||||
		nr = _free_node(rt, pc->child);
 | 
			
		||||
		free(pc);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE4:
 | 
			
		||||
		n4 = (struct node4 *) v.value.ptr;
 | 
			
		||||
		for (i = 0; i < n4->nr_entries; i++)
 | 
			
		||||
			nr += _free_node(rt, n4->values[i]);
 | 
			
		||||
		free(n4);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE16:
 | 
			
		||||
		n16 = (struct node16 *) v.value.ptr;
 | 
			
		||||
		for (i = 0; i < n16->nr_entries; i++)
 | 
			
		||||
			nr += _free_node(rt, n16->values[i]);
 | 
			
		||||
		free(n16);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE48:
 | 
			
		||||
		n48 = (struct node48 *) v.value.ptr;
 | 
			
		||||
		for (i = 0; i < n48->nr_entries; i++)
 | 
			
		||||
			nr += _free_node(rt, n48->values[i]);
 | 
			
		||||
		free(n48);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE256:
 | 
			
		||||
		n256 = (struct node256 *) v.value.ptr;
 | 
			
		||||
		for (i = 0; i < 256; i++)
 | 
			
		||||
			nr += _free_node(rt, n256->values[i]);
 | 
			
		||||
		free(n256);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void radix_tree_destroy(struct radix_tree *rt)
 | 
			
		||||
{
 | 
			
		||||
	_free_node(rt, rt->root);
 | 
			
		||||
	free(rt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned radix_tree_size(struct radix_tree *rt)
 | 
			
		||||
{
 | 
			
		||||
	return rt->nr_entries;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv);
 | 
			
		||||
 | 
			
		||||
static bool _insert_unset(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	unsigned len = ke - kb;
 | 
			
		||||
 | 
			
		||||
	if (!len) {
 | 
			
		||||
		// value
 | 
			
		||||
		v->type = VALUE;
 | 
			
		||||
		v->value = rv;
 | 
			
		||||
		rt->nr_entries++;
 | 
			
		||||
	} else {
 | 
			
		||||
		// prefix -> value
 | 
			
		||||
		struct prefix_chain *pc = zalloc(sizeof(*pc) + len);
 | 
			
		||||
		if (!pc)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		pc->child.type = VALUE;
 | 
			
		||||
		pc->child.value = rv;
 | 
			
		||||
		pc->len = len;
 | 
			
		||||
		memcpy(pc->prefix, kb, len);
 | 
			
		||||
		v->type = PREFIX_CHAIN;
 | 
			
		||||
		v->value.ptr = pc;
 | 
			
		||||
		rt->nr_entries++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_value(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	unsigned len = ke - kb;
 | 
			
		||||
 | 
			
		||||
	if (!len)
 | 
			
		||||
		// overwrite
 | 
			
		||||
		v->value = rv;
 | 
			
		||||
 | 
			
		||||
	else {
 | 
			
		||||
		// value_chain -> value
 | 
			
		||||
		struct value_chain *vc = zalloc(sizeof(*vc));
 | 
			
		||||
		if (!vc)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		vc->value = v->value;
 | 
			
		||||
		if (!_insert(rt, &vc->child, kb, ke, rv)) {
 | 
			
		||||
			free(vc);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		v->type = VALUE_CHAIN;
 | 
			
		||||
		v->value.ptr = vc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_value_chain(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct value_chain *vc = v->value.ptr;
 | 
			
		||||
	return _insert(rt, &vc->child, kb, ke, rv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned min(unsigned lhs, unsigned rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (lhs <= rhs)
 | 
			
		||||
		return lhs;
 | 
			
		||||
	else
 | 
			
		||||
		return rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_prefix_chain(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct prefix_chain *pc = v->value.ptr;
 | 
			
		||||
 | 
			
		||||
	if (*kb == pc->prefix[0]) {
 | 
			
		||||
		// There's a common prefix let's split the chain into two and
 | 
			
		||||
		// recurse.
 | 
			
		||||
		struct prefix_chain *pc2;
 | 
			
		||||
		unsigned i, len = min(pc->len, ke - kb);
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < len; i++)
 | 
			
		||||
			if (kb[i] != pc->prefix[i])
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
		pc2 = zalloc(sizeof(*pc2) + pc->len - i);
 | 
			
		||||
		pc2->len = pc->len - i;
 | 
			
		||||
		memmove(pc2->prefix, pc->prefix + i, pc2->len);
 | 
			
		||||
		pc2->child = pc->child;
 | 
			
		||||
 | 
			
		||||
		// FIXME: this trashes pc so we can't back out
 | 
			
		||||
		pc->child.type = PREFIX_CHAIN;
 | 
			
		||||
		pc->child.value.ptr = pc2;
 | 
			
		||||
		pc->len = i;
 | 
			
		||||
 | 
			
		||||
		if (!_insert(rt, &pc->child, kb + i, ke, rv)) {
 | 
			
		||||
			free(pc2);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Stick an n4 in front.
 | 
			
		||||
		struct node4 *n4 = zalloc(sizeof(*n4));
 | 
			
		||||
		if (!n4)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		n4->keys[0] = *kb;
 | 
			
		||||
		if (!_insert(rt, n4->values, kb + 1, ke, rv)) {
 | 
			
		||||
			free(n4);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (pc->len) {
 | 
			
		||||
			n4->keys[1] = pc->prefix[0];
 | 
			
		||||
			if (pc->len == 1) {
 | 
			
		||||
				n4->values[1] = pc->child;
 | 
			
		||||
				free(pc);
 | 
			
		||||
			} else {
 | 
			
		||||
				memmove(pc->prefix, pc->prefix + 1, pc->len - 1);
 | 
			
		||||
				pc->len--;
 | 
			
		||||
				n4->values[1] = *v;
 | 
			
		||||
			}
 | 
			
		||||
			n4->nr_entries = 2;
 | 
			
		||||
		} else
 | 
			
		||||
			n4->nr_entries = 1;
 | 
			
		||||
 | 
			
		||||
		v->type = NODE4;
 | 
			
		||||
		v->value.ptr = n4;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_node4(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct node4 *n4 = v->value.ptr;
 | 
			
		||||
	if (n4->nr_entries == 4) {
 | 
			
		||||
		struct node16 *n16 = zalloc(sizeof(*n16));
 | 
			
		||||
		if (!n16)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		n16->nr_entries = 5;
 | 
			
		||||
		memcpy(n16->keys, n4->keys, sizeof(n4->keys));
 | 
			
		||||
		memcpy(n16->values, n4->values, sizeof(n4->values));
 | 
			
		||||
 | 
			
		||||
		n16->keys[4] = *kb;
 | 
			
		||||
		if (!_insert(rt, n16->values + 4, kb + 1, ke, rv)) {
 | 
			
		||||
			free(n16);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		free(n4);
 | 
			
		||||
		v->type = NODE16;
 | 
			
		||||
		v->value.ptr = n16;
 | 
			
		||||
	} else {
 | 
			
		||||
		n4 = v->value.ptr;
 | 
			
		||||
		if (!_insert(rt, n4->values + n4->nr_entries, kb + 1, ke, rv))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		n4->keys[n4->nr_entries] = *kb;
 | 
			
		||||
		n4->nr_entries++;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_node16(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct node16 *n16 = v->value.ptr;
 | 
			
		||||
 | 
			
		||||
	if (n16->nr_entries == 16) {
 | 
			
		||||
		unsigned i;
 | 
			
		||||
		struct node48 *n48 = zalloc(sizeof(*n48));
 | 
			
		||||
 | 
			
		||||
		if (!n48)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		n48->nr_entries = 17;
 | 
			
		||||
		memset(n48->keys, 48, sizeof(n48->keys));
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < 16; i++) {
 | 
			
		||||
			n48->keys[n16->keys[i]] = i;
 | 
			
		||||
			n48->values[i] = n16->values[i];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		n48->keys[*kb] = 16;
 | 
			
		||||
		if (!_insert(rt, n48->values + 16, kb + 1, ke, rv)) {
 | 
			
		||||
			free(n48);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		free(n16);
 | 
			
		||||
		v->type = NODE48;
 | 
			
		||||
		v->value.ptr = n48;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!_insert(rt, n16->values + n16->nr_entries, kb + 1, ke, rv))
 | 
			
		||||
			return false;
 | 
			
		||||
		n16->keys[n16->nr_entries] = *kb;
 | 
			
		||||
		n16->nr_entries++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_node48(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct node48 *n48 = v->value.ptr;
 | 
			
		||||
	if (n48->nr_entries == 48) {
 | 
			
		||||
		unsigned i;
 | 
			
		||||
		struct node256 *n256 = zalloc(sizeof(*n256));
 | 
			
		||||
		if (!n256)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < 256; i++) {
 | 
			
		||||
			if (n48->keys[i] >= 48)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			n256->values[i] = n48->values[n48->keys[i]];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!_insert(rt, n256->values + *kb, kb + 1, ke, rv)) {
 | 
			
		||||
			free(n256);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		free(n48);
 | 
			
		||||
		v->type = NODE256;
 | 
			
		||||
		v->value.ptr = n256;
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!_insert(rt, n48->values + n48->nr_entries, kb + 1, ke, rv))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		n48->keys[*kb] = n48->nr_entries;
 | 
			
		||||
		n48->nr_entries++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _insert_node256(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct node256 *n256 = v->value.ptr;
 | 
			
		||||
	bool was_unset = n256->values[*kb].type == UNSET;
 | 
			
		||||
 | 
			
		||||
	if (!_insert(rt, n256->values + *kb, kb + 1, ke, rv))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (was_unset)
 | 
			
		||||
        	n256->nr_entries++;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FIXME: the tree should not be touched if insert fails (eg, OOM)
 | 
			
		||||
static bool _insert(struct radix_tree *rt, struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	if (kb == ke) {
 | 
			
		||||
		if (v->type == UNSET) {
 | 
			
		||||
			v->type = VALUE;
 | 
			
		||||
			v->value = rv;
 | 
			
		||||
			rt->nr_entries++;
 | 
			
		||||
 | 
			
		||||
		} else if (v->type == VALUE) {
 | 
			
		||||
			v->value = rv;
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			struct value_chain *vc = zalloc(sizeof(*vc));
 | 
			
		||||
			if (!vc)
 | 
			
		||||
				return false;
 | 
			
		||||
 | 
			
		||||
			vc->value = rv;
 | 
			
		||||
			vc->child = *v;
 | 
			
		||||
			v->type = VALUE_CHAIN;
 | 
			
		||||
			v->value.ptr = vc;
 | 
			
		||||
			rt->nr_entries++;
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (v->type) {
 | 
			
		||||
	case UNSET:
 | 
			
		||||
		return _insert_unset(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case VALUE:
 | 
			
		||||
		return _insert_value(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case VALUE_CHAIN:
 | 
			
		||||
		return _insert_value_chain(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case PREFIX_CHAIN:
 | 
			
		||||
		return _insert_prefix_chain(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case NODE4:
 | 
			
		||||
		return _insert_node4(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case NODE16:
 | 
			
		||||
		return _insert_node16(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case NODE48:
 | 
			
		||||
		return _insert_node48(rt, v, kb, ke, rv);
 | 
			
		||||
 | 
			
		||||
	case NODE256:
 | 
			
		||||
		return _insert_node256(rt, v, kb, ke, rv);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// can't get here
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct lookup_result {
 | 
			
		||||
	struct value *v;
 | 
			
		||||
	uint8_t *kb;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct lookup_result _lookup_prefix(struct value *v, uint8_t *kb, uint8_t *ke)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	struct value_chain *vc;
 | 
			
		||||
	struct prefix_chain *pc;
 | 
			
		||||
	struct node4 *n4;
 | 
			
		||||
	struct node16 *n16;
 | 
			
		||||
	struct node48 *n48;
 | 
			
		||||
	struct node256 *n256;
 | 
			
		||||
 | 
			
		||||
	if (kb == ke)
 | 
			
		||||
		return (struct lookup_result) {.v = v, .kb = kb};
 | 
			
		||||
 | 
			
		||||
	switch (v->type) {
 | 
			
		||||
	case UNSET:
 | 
			
		||||
	case VALUE:
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case VALUE_CHAIN:
 | 
			
		||||
		vc = v->value.ptr;
 | 
			
		||||
		return _lookup_prefix(&vc->child, kb, ke);
 | 
			
		||||
 | 
			
		||||
	case PREFIX_CHAIN:
 | 
			
		||||
		pc = v->value.ptr;
 | 
			
		||||
		if (ke - kb < pc->len)
 | 
			
		||||
			return (struct lookup_result) {.v = v, .kb = kb};
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < pc->len; i++)
 | 
			
		||||
			if (kb[i] != pc->prefix[i])
 | 
			
		||||
				return (struct lookup_result) {.v = v, .kb = kb};
 | 
			
		||||
 | 
			
		||||
		return _lookup_prefix(&pc->child, kb + pc->len, ke);
 | 
			
		||||
 | 
			
		||||
	case NODE4:
 | 
			
		||||
		n4 = v->value.ptr;
 | 
			
		||||
		for (i = 0; i < n4->nr_entries; i++)
 | 
			
		||||
			if (n4->keys[i] == *kb)
 | 
			
		||||
				return _lookup_prefix(n4->values + i, kb + 1, ke);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE16:
 | 
			
		||||
		// FIXME: use binary search or simd?
 | 
			
		||||
		n16 = v->value.ptr;
 | 
			
		||||
		for (i = 0; i < n16->nr_entries; i++)
 | 
			
		||||
			if (n16->keys[i] == *kb)
 | 
			
		||||
				return _lookup_prefix(n16->values + i, kb + 1, ke);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE48:
 | 
			
		||||
		n48 = v->value.ptr;
 | 
			
		||||
		i = n48->keys[*kb];
 | 
			
		||||
		if (i < 48)
 | 
			
		||||
			return _lookup_prefix(n48->values + i, kb + 1, ke);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case NODE256:
 | 
			
		||||
		n256 = v->value.ptr;
 | 
			
		||||
		return _lookup_prefix(n256->values + *kb, kb + 1, ke);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (struct lookup_result) {.v = v, .kb = kb};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value rv)
 | 
			
		||||
{
 | 
			
		||||
	struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
 | 
			
		||||
	return _insert(rt, lr.v, lr.kb, ke, rv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Note the degrade functions also free the original node.
 | 
			
		||||
static void _degrade_to_n4(struct node16 *n16, struct value *result)
 | 
			
		||||
{
 | 
			
		||||
        struct node4 *n4 = zalloc(sizeof(*n4));
 | 
			
		||||
 | 
			
		||||
        n4->nr_entries = n16->nr_entries;
 | 
			
		||||
        memcpy(n4->keys, n16->keys, n16->nr_entries * sizeof(*n4->keys));
 | 
			
		||||
        memcpy(n4->values, n16->values, n16->nr_entries * sizeof(*n4->values));
 | 
			
		||||
        free(n16);
 | 
			
		||||
 | 
			
		||||
	result->type = NODE4;
 | 
			
		||||
	result->value.ptr = n4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _degrade_to_n16(struct node48 *n48, struct value *result)
 | 
			
		||||
{
 | 
			
		||||
        struct node4 *n16 = zalloc(sizeof(*n16));
 | 
			
		||||
 | 
			
		||||
        n16->nr_entries = n48->nr_entries;
 | 
			
		||||
        memcpy(n16->keys, n48->keys, n48->nr_entries * sizeof(*n16->keys));
 | 
			
		||||
        memcpy(n16->values, n48->values, n48->nr_entries * sizeof(*n16->values));
 | 
			
		||||
        free(n48);
 | 
			
		||||
 | 
			
		||||
	result->type = NODE16;
 | 
			
		||||
	result->value.ptr = n16;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _degrade_to_n48(struct node256 *n256, struct value *result)
 | 
			
		||||
{
 | 
			
		||||
        unsigned i, count = 0;
 | 
			
		||||
        struct node4 *n48 = zalloc(sizeof(*n48));
 | 
			
		||||
 | 
			
		||||
        n48->nr_entries = n256->nr_entries;
 | 
			
		||||
        for (i = 0; i < 256; i++) {
 | 
			
		||||
		if (n256->values[i].type == UNSET)
 | 
			
		||||
        		continue;
 | 
			
		||||
 | 
			
		||||
		n48->keys[count] = i;
 | 
			
		||||
		n48->values[count] = n256->values[i];
 | 
			
		||||
		count++;
 | 
			
		||||
        }
 | 
			
		||||
        free(n256);
 | 
			
		||||
 | 
			
		||||
	result->type = NODE48;
 | 
			
		||||
	result->value.ptr = n48;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _remove(struct radix_tree *rt, struct value *root, uint8_t *kb, uint8_t *ke)
 | 
			
		||||
{
 | 
			
		||||
	bool r;
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	struct value_chain *vc;
 | 
			
		||||
	struct prefix_chain *pc;
 | 
			
		||||
	struct node4 *n4;
 | 
			
		||||
	struct node16 *n16;
 | 
			
		||||
	struct node48 *n48;
 | 
			
		||||
	struct node256 *n256;
 | 
			
		||||
 | 
			
		||||
	if (kb == ke) {
 | 
			
		||||
        	if (root->type == VALUE) {
 | 
			
		||||
                	root->type = UNSET;
 | 
			
		||||
                	_dtr(rt, root->value);
 | 
			
		||||
                	return true;
 | 
			
		||||
 | 
			
		||||
                } else if (root->type == VALUE_CHAIN) {
 | 
			
		||||
			vc = root->value.ptr;
 | 
			
		||||
			_dtr(rt, vc->value);
 | 
			
		||||
			memcpy(root, &vc->child, sizeof(*root));
 | 
			
		||||
			free(vc);
 | 
			
		||||
			return true;
 | 
			
		||||
 | 
			
		||||
                } else
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (root->type) {
 | 
			
		||||
	case UNSET:
 | 
			
		||||
	case VALUE:
 | 
			
		||||
        	// this is a value for a prefix of the key
 | 
			
		||||
        	return false;
 | 
			
		||||
 | 
			
		||||
	case VALUE_CHAIN:
 | 
			
		||||
		vc = root->value.ptr;
 | 
			
		||||
		r = _remove(rt, &vc->child, kb, ke);
 | 
			
		||||
		if (r && (vc->child.type == UNSET)) {
 | 
			
		||||
			memcpy(root, &vc->child, sizeof(*root));
 | 
			
		||||
			free(vc);
 | 
			
		||||
		}
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	case PREFIX_CHAIN:
 | 
			
		||||
		pc = root->value.ptr;
 | 
			
		||||
		if (ke - kb < pc->len)
 | 
			
		||||
        		return false;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < pc->len; i++)
 | 
			
		||||
			if (kb[i] != pc->prefix[i])
 | 
			
		||||
        			return false;
 | 
			
		||||
 | 
			
		||||
		return _remove(rt, &pc->child, kb + pc->len, ke);
 | 
			
		||||
 | 
			
		||||
	case NODE4:
 | 
			
		||||
		n4 = root->value.ptr;
 | 
			
		||||
		for (i = 0; i < n4->nr_entries; i++) {
 | 
			
		||||
			if (n4->keys[i] == *kb) {
 | 
			
		||||
				r = _remove(rt, n4->values + i, kb + 1, ke);
 | 
			
		||||
				if (r && n4->values[i].type == UNSET) {
 | 
			
		||||
        				n4->nr_entries--;
 | 
			
		||||
        				if (i < n4->nr_entries)
 | 
			
		||||
                				// slide the entries down
 | 
			
		||||
        					memmove(n4->keys + i, n4->keys + i + 1,
 | 
			
		||||
                                                       sizeof(*n4->keys) * (n4->nr_entries - i));
 | 
			
		||||
					if (!n4->nr_entries)
 | 
			
		||||
						root->type = UNSET;
 | 
			
		||||
				}
 | 
			
		||||
				return r;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	case NODE16:
 | 
			
		||||
        	n16 = root->value.ptr;
 | 
			
		||||
		for (i = 0; i < n16->nr_entries; i++) {
 | 
			
		||||
			if (n16->keys[i] == *kb) {
 | 
			
		||||
				r = _remove(rt, n16->values + i, kb + 1, ke);
 | 
			
		||||
				if (r && n16->values[i].type == UNSET) {
 | 
			
		||||
        				n16->nr_entries--;
 | 
			
		||||
        				if (i < n16->nr_entries)
 | 
			
		||||
                				// slide the entries down
 | 
			
		||||
        					memmove(n16->keys + i, n16->keys + i + 1,
 | 
			
		||||
                                                        sizeof(*n16->keys) * (n16->nr_entries - i));
 | 
			
		||||
					if (n16->nr_entries <= 4)
 | 
			
		||||
        					_degrade_to_n4(n16, root);
 | 
			
		||||
				}
 | 
			
		||||
				return r;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	case NODE48:
 | 
			
		||||
		n48 = root->value.ptr;
 | 
			
		||||
		i = n48->keys[*kb];
 | 
			
		||||
		if (i < 48) {
 | 
			
		||||
        		r = _remove(rt, n48->values + i, kb + 1, ke);
 | 
			
		||||
        		if (r && n48->values[i].type == UNSET) {
 | 
			
		||||
                		n48->keys[*kb] = 48;
 | 
			
		||||
				n48->nr_entries--;
 | 
			
		||||
				if (n48->nr_entries <= 16)
 | 
			
		||||
        				_degrade_to_n16(n48, root);
 | 
			
		||||
        		}
 | 
			
		||||
        		return r;
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	case NODE256:
 | 
			
		||||
		n256 = root->value.ptr;
 | 
			
		||||
		r = _remove(rt, n256->values + (*kb), kb + 1, ke);
 | 
			
		||||
		if (r && n256->values[*kb].type == UNSET) {
 | 
			
		||||
			n256->nr_entries--;
 | 
			
		||||
			if (n256->nr_entries <= 48)
 | 
			
		||||
        			_degrade_to_n48(n256, root);
 | 
			
		||||
		}
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool radix_tree_remove(struct radix_tree *rt, uint8_t *key_begin, uint8_t *key_end)
 | 
			
		||||
{
 | 
			
		||||
	if (_remove(rt, &rt->root, key_begin, key_end)) {
 | 
			
		||||
        	rt->nr_entries--;
 | 
			
		||||
        	return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _prefix_chain_matches(struct lookup_result *lr, uint8_t *ke)
 | 
			
		||||
{
 | 
			
		||||
        // It's possible the top node is a prefix chain, and
 | 
			
		||||
        // the remaining key matches part of it.
 | 
			
		||||
        if (lr->v->type == PREFIX_CHAIN) {
 | 
			
		||||
                unsigned i, rlen = ke - lr->kb;
 | 
			
		||||
                struct prefix_chain *pc = lr->v->value.ptr;
 | 
			
		||||
                if (rlen < pc->len) {
 | 
			
		||||
                        for (i = 0; i < rlen; i++)
 | 
			
		||||
                                if (pc->prefix[i] != lr->kb[i])
 | 
			
		||||
                                        return false;
 | 
			
		||||
                        return true;
 | 
			
		||||
		}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned radix_tree_remove_prefix(struct radix_tree *rt, uint8_t *kb, uint8_t *ke)
 | 
			
		||||
{
 | 
			
		||||
        unsigned count = 0;
 | 
			
		||||
	struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
 | 
			
		||||
	if (lr.kb == ke || _prefix_chain_matches(&lr, ke)) {
 | 
			
		||||
        	count = _free_node(rt, *lr.v);
 | 
			
		||||
        	lr.v->type = UNSET;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rt->nr_entries -= count;
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool radix_tree_lookup(struct radix_tree *rt,
 | 
			
		||||
		       uint8_t *kb, uint8_t *ke, union radix_value *result)
 | 
			
		||||
{
 | 
			
		||||
	struct value_chain *vc;
 | 
			
		||||
	struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
 | 
			
		||||
	if (lr.kb == ke) {
 | 
			
		||||
		switch (lr.v->type) {
 | 
			
		||||
		case VALUE:
 | 
			
		||||
			*result = lr.v->value;
 | 
			
		||||
			return true;
 | 
			
		||||
 | 
			
		||||
		case VALUE_CHAIN:
 | 
			
		||||
			vc = lr.v->value.ptr;
 | 
			
		||||
			*result = vc->value;
 | 
			
		||||
			return true;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FIXME: build up the keys too
 | 
			
		||||
static bool _iterate(struct value *v, struct radix_tree_iterator *it)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	struct value_chain *vc;
 | 
			
		||||
	struct prefix_chain *pc;
 | 
			
		||||
	struct node4 *n4;
 | 
			
		||||
	struct node16 *n16;
 | 
			
		||||
	struct node48 *n48;
 | 
			
		||||
	struct node256 *n256;
 | 
			
		||||
 | 
			
		||||
	switch (v->type) {
 | 
			
		||||
	case UNSET:
 | 
			
		||||
        	// can't happen
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case VALUE:
 | 
			
		||||
        	return it->visit(it, NULL, NULL, v->value);
 | 
			
		||||
 | 
			
		||||
	case VALUE_CHAIN:
 | 
			
		||||
		vc = v->value.ptr;
 | 
			
		||||
		return it->visit(it, NULL, NULL, vc->value) && _iterate(&vc->child, it);
 | 
			
		||||
 | 
			
		||||
	case PREFIX_CHAIN:
 | 
			
		||||
		pc = v->value.ptr;
 | 
			
		||||
		return _iterate(&pc->child, it);
 | 
			
		||||
 | 
			
		||||
	case NODE4:
 | 
			
		||||
		n4 = (struct node4 *) v->value.ptr;
 | 
			
		||||
		for (i = 0; i < n4->nr_entries; i++)
 | 
			
		||||
			if (!_iterate(n4->values + i, it))
 | 
			
		||||
        			return false;
 | 
			
		||||
        	return true;
 | 
			
		||||
 | 
			
		||||
	case NODE16:
 | 
			
		||||
		n16 = (struct node16 *) v->value.ptr;
 | 
			
		||||
		for (i = 0; i < n16->nr_entries; i++)
 | 
			
		||||
        		if (!_iterate(n16->values + i, it))
 | 
			
		||||
        			return false;
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	case NODE48:
 | 
			
		||||
		n48 = (struct node48 *) v->value.ptr;
 | 
			
		||||
		for (i = 0; i < n48->nr_entries; i++)
 | 
			
		||||
        		if (!_iterate(n48->values + i, it))
 | 
			
		||||
        			return false;
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	case NODE256:
 | 
			
		||||
		n256 = (struct node256 *) v->value.ptr;
 | 
			
		||||
		for (i = 0; i < 256; i++)
 | 
			
		||||
        		if (n256->values[i].type != UNSET && !_iterate(n256->values + i, it))
 | 
			
		||||
        			return false;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// can't get here
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void radix_tree_iterate(struct radix_tree *rt, uint8_t *kb, uint8_t *ke,
 | 
			
		||||
                        struct radix_tree_iterator *it)
 | 
			
		||||
{
 | 
			
		||||
	struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
 | 
			
		||||
	if (lr.kb == ke || _prefix_chain_matches(&lr, ke))
 | 
			
		||||
        	_iterate(lr.v, it);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
@@ -1,58 +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>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
 | 
			
		||||
// 
 | 
			
		||||
// This file is part of LVM2.
 | 
			
		||||
//
 | 
			
		||||
// This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
// modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
// of the GNU Lesser General Public License v.2.1.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
// along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 | 
			
		||||
#ifndef BASE_MEMORY_CONTAINER_OF_H
 | 
			
		||||
#define BASE_MEMORY_CONTAINER_OF_H
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#define container_of(v, t, head) \
 | 
			
		||||
    ((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,31 +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>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
static inline void *zalloc(size_t len)
 | 
			
		||||
{
 | 
			
		||||
	void *ptr = malloc(len);
 | 
			
		||||
	if (ptr)
 | 
			
		||||
		memset(ptr, 0, len);
 | 
			
		||||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										2
									
								
								conf/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								conf/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -2,5 +2,3 @@ command_profile_template.profile
 | 
			
		||||
example.conf
 | 
			
		||||
lvmlocal.conf
 | 
			
		||||
metadata_profile_template.profile
 | 
			
		||||
configure.h
 | 
			
		||||
lvm-version.h
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
@@ -24,16 +24,15 @@ PROFILES=$(PROFILE_TEMPLATES) \
 | 
			
		||||
	$(srcdir)/cache-mq.profile \
 | 
			
		||||
	$(srcdir)/cache-smq.profile \
 | 
			
		||||
	$(srcdir)/thin-generic.profile \
 | 
			
		||||
	$(srcdir)/thin-performance.profile \
 | 
			
		||||
	$(srcdir)/lvmdbusd.profile
 | 
			
		||||
	$(srcdir)/thin-performance.profile
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
.PHONY: install_conf install_localconf install_profiles
 | 
			
		||||
 | 
			
		||||
generate:
 | 
			
		||||
	LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withgeneralpreamble --withcomments --ignorelocal --withspaces > example.conf.in
 | 
			
		||||
	LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withlocalpreamble --withcomments --withspaces local > lvmlocal.conf.in
 | 
			
		||||
	(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 \
 | 
			
		||||
@@ -48,8 +47,8 @@ install_localconf: $(CONFLOCAL)
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
install_profiles: $(PROFILES)
 | 
			
		||||
	$(INSTALL_DIR) $(profiledir)
 | 
			
		||||
	$(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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,17 +11,6 @@
 | 
			
		||||
# Refer to 'man lvm.conf' for further information about profiles and
 | 
			
		||||
# general configuration file layout.
 | 
			
		||||
#
 | 
			
		||||
allocation {
 | 
			
		||||
	cache_mode="writethrough"
 | 
			
		||||
	cache_settings {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
log {
 | 
			
		||||
	report_command_log=0
 | 
			
		||||
	command_log_sort="log_seq_num"
 | 
			
		||||
	command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
 | 
			
		||||
	command_log_selection="!(log_type=status && message=success)"
 | 
			
		||||
}
 | 
			
		||||
global {
 | 
			
		||||
	units="h"
 | 
			
		||||
	si_unit_consistency=1
 | 
			
		||||
@@ -29,9 +18,7 @@ global {
 | 
			
		||||
	lvdisplay_shows_full_device_path=0
 | 
			
		||||
}
 | 
			
		||||
report {
 | 
			
		||||
	output_format="basic"
 | 
			
		||||
	compact_output=0
 | 
			
		||||
	compact_output_cols=""
 | 
			
		||||
	aligned=1
 | 
			
		||||
	buffered=1
 | 
			
		||||
	headings=1
 | 
			
		||||
@@ -39,9 +26,8 @@ report {
 | 
			
		||||
	list_item_separator=","
 | 
			
		||||
	prefixes=0
 | 
			
		||||
	quoted=1
 | 
			
		||||
	columns_as_rows=0
 | 
			
		||||
	colums_as_rows=0
 | 
			
		||||
	binary_values_as_numeric=0
 | 
			
		||||
	time_format="%Y-%m-%d %T %z"
 | 
			
		||||
	devtypes_sort="devtype_name"
 | 
			
		||||
	devtypes_cols="devtype_name,devtype_max_partitions,devtype_description"
 | 
			
		||||
	devtypes_cols_verbose="devtype_name,devtype_max_partitions,devtype_description"
 | 
			
		||||
@@ -60,15 +46,4 @@ report {
 | 
			
		||||
	pvsegs_sort="pv_name,pvseg_start"
 | 
			
		||||
	pvsegs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
 | 
			
		||||
	pvsegs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
 | 
			
		||||
	vgs_cols_full="vg_all"
 | 
			
		||||
	pvs_cols_full="pv_all"
 | 
			
		||||
	lvs_cols_full="lv_all"
 | 
			
		||||
	pvsegs_cols_full="pvseg_all,pv_uuid,lv_uuid"
 | 
			
		||||
	segs_cols_full="seg_all,lv_uuid"
 | 
			
		||||
	vgs_sort_full="vg_name"
 | 
			
		||||
	pvs_sort_full="pv_name"
 | 
			
		||||
	lvs_sort_full="vg_name,lv_name"
 | 
			
		||||
	pvsegs_sort_full="pv_uuid,pvseg_start"
 | 
			
		||||
	segs_sort_full="lv_uuid,seg_start"
 | 
			
		||||
	mark_hidden_devices=1
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.
 | 
			
		||||
 | 
			
		||||
@@ -185,20 +185,6 @@ devices {
 | 
			
		||||
	# present on the system. sysfs must be part of the kernel and mounted.)
 | 
			
		||||
	sysfs_scan = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option devices/scan_lvs.
 | 
			
		||||
	# Scan LVM LVs for layered PVs, allowing LVs to be used as PVs.
 | 
			
		||||
	# When 1, LVM will detect PVs layered on LVs, and caution must be
 | 
			
		||||
	# taken to avoid a host accessing a layered VG that may not belong
 | 
			
		||||
	# to it, e.g. from a guest image. This generally requires excluding
 | 
			
		||||
	# the LVs with device filters. Also, when this setting is enabled,
 | 
			
		||||
	# every LVM command will scan every active LV on the system (unless
 | 
			
		||||
	# filtered), which can cause performance problems on systems with
 | 
			
		||||
	# many active LVs. When this setting is 0, LVM will not detect or
 | 
			
		||||
	# use PVs that exist on LVs, and will not allow a PV to be created on
 | 
			
		||||
	# an LV. The LVs are ignored using a built in device filter that
 | 
			
		||||
	# identifies and excludes LVs.
 | 
			
		||||
	scan_lvs = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option devices/multipath_component_detection.
 | 
			
		||||
	# Ignore devices that are components of DM multipath devices.
 | 
			
		||||
	multipath_component_detection = 1
 | 
			
		||||
@@ -313,19 +299,6 @@ devices {
 | 
			
		||||
	# generally do. If enabled, discards will only be issued if both the
 | 
			
		||||
	# storage and kernel provide support.
 | 
			
		||||
	issue_discards = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option devices/allow_changes_with_duplicate_pvs.
 | 
			
		||||
	# Allow VG modification while a PV appears on multiple devices.
 | 
			
		||||
	# When a PV appears on multiple devices, LVM attempts to choose the
 | 
			
		||||
	# best device to use for the PV. If the devices represent the same
 | 
			
		||||
	# underlying storage, the choice has minimal consequence. If the
 | 
			
		||||
	# devices represent different underlying storage, the wrong choice
 | 
			
		||||
	# can result in data loss if the VG is modified. Disabling this
 | 
			
		||||
	# setting is the safest option because it prevents modifying a VG
 | 
			
		||||
	# or activating LVs in it while a PV appears on multiple devices.
 | 
			
		||||
	# Enabling this setting allows the VG to be used as usual even with
 | 
			
		||||
	# uncertain devices.
 | 
			
		||||
	allow_changes_with_duplicate_pvs = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Configuration section allocation.
 | 
			
		||||
@@ -391,30 +364,10 @@ allocation {
 | 
			
		||||
	# The default setting changed in version 2.02.85.
 | 
			
		||||
	mirror_logs_require_separate_pvs = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/raid_stripe_all_devices.
 | 
			
		||||
	# Stripe across all PVs when RAID stripes are not specified.
 | 
			
		||||
	# If enabled, all PVs in the VG or on the command line are used for
 | 
			
		||||
	# raid0/4/5/6/10 when the command does not specify the number of
 | 
			
		||||
	# stripes to use.
 | 
			
		||||
	# This was the default behaviour until release 2.02.162.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# raid_stripe_all_devices = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/cache_pool_metadata_require_separate_pvs.
 | 
			
		||||
	# Cache pool metadata and data will always use different PVs.
 | 
			
		||||
	cache_pool_metadata_require_separate_pvs = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/cache_metadata_format.
 | 
			
		||||
	# Sets default metadata format for new cache.
 | 
			
		||||
	# 
 | 
			
		||||
	# Accepted values:
 | 
			
		||||
	#   0  Automatically detected best available format
 | 
			
		||||
	#   1  Original format
 | 
			
		||||
	#   2  Improved 2nd. generation format
 | 
			
		||||
	# 
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# cache_metadata_format = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/cache_mode.
 | 
			
		||||
	# The default cache mode used for new cache.
 | 
			
		||||
	# 
 | 
			
		||||
@@ -431,13 +384,13 @@ allocation {
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/cache_policy.
 | 
			
		||||
	# The default cache policy used for new cache volume.
 | 
			
		||||
	# Since kernel 4.2 the default policy is smq (Stochastic multiqueue),
 | 
			
		||||
	# Since kernel 4.2 the default policy is smq (Stochastic multique),
 | 
			
		||||
	# otherwise the older mq (Multiqueue) policy is selected.
 | 
			
		||||
	# This configuration option does not have a default value defined.
 | 
			
		||||
 | 
			
		||||
	# Configuration section allocation/cache_settings.
 | 
			
		||||
	# Settings for the cache policy.
 | 
			
		||||
	# See documentation for individual cache policies for more info.
 | 
			
		||||
	# Individual settings for policies.
 | 
			
		||||
	# See the help for individual policies for more info.
 | 
			
		||||
	# This configuration section has an automatic default value.
 | 
			
		||||
	# cache_settings {
 | 
			
		||||
	# }
 | 
			
		||||
@@ -454,12 +407,6 @@ allocation {
 | 
			
		||||
	# 32KiB to 1GiB in multiples of 32.
 | 
			
		||||
	# This configuration option does not have a default value defined.
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/cache_pool_max_chunks.
 | 
			
		||||
	# The maximum number of chunks in a cache pool.
 | 
			
		||||
	# For cache target v1.9 the recommended maximumm is 1000000 chunks.
 | 
			
		||||
	# Using cache pool with more chunks may degrade cache performance.
 | 
			
		||||
	# This configuration option does not have a default value defined.
 | 
			
		||||
 | 
			
		||||
	# Configuration option allocation/thin_pool_metadata_require_separate_pvs.
 | 
			
		||||
	# Thin pool metdata and data will always use different PVs.
 | 
			
		||||
	thin_pool_metadata_require_separate_pvs = 0
 | 
			
		||||
@@ -518,55 +465,6 @@ allocation {
 | 
			
		||||
# How LVM log information is reported.
 | 
			
		||||
log {
 | 
			
		||||
 | 
			
		||||
	# Configuration option log/report_command_log.
 | 
			
		||||
	# Enable or disable LVM log reporting.
 | 
			
		||||
	# If enabled, LVM will collect a log of operations, messages,
 | 
			
		||||
	# per-object return codes with object identification and associated
 | 
			
		||||
	# error numbers (errnos) during LVM command processing. Then the
 | 
			
		||||
	# log is either reported solely or in addition to any existing
 | 
			
		||||
	# reports, depending on LVM command used. If it is a reporting command
 | 
			
		||||
	# (e.g. pvs, vgs, lvs, lvm fullreport), then the log is reported in
 | 
			
		||||
	# addition to any existing reports. Otherwise, there's only log report
 | 
			
		||||
	# on output. For all applicable LVM commands, you can request that
 | 
			
		||||
	# the output has only log report by using --logonly command line
 | 
			
		||||
	# option. Use log/command_log_cols and log/command_log_sort settings
 | 
			
		||||
	# to define fields to display and sort fields for the log report.
 | 
			
		||||
	# You can also use log/command_log_selection to define selection
 | 
			
		||||
	# criteria used each time the log is reported.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# report_command_log = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option log/command_log_sort.
 | 
			
		||||
	# List of columns to sort by when reporting command log.
 | 
			
		||||
	# See <lvm command> --logonly --configreport log -o help
 | 
			
		||||
	# for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# command_log_sort = "log_seq_num"
 | 
			
		||||
 | 
			
		||||
	# Configuration option log/command_log_cols.
 | 
			
		||||
	# List of columns to report when reporting command log.
 | 
			
		||||
	# See <lvm command> --logonly --configreport log -o help
 | 
			
		||||
	# for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# command_log_cols = "log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
 | 
			
		||||
 | 
			
		||||
	# Configuration option log/command_log_selection.
 | 
			
		||||
	# Selection criteria used when reporting command log.
 | 
			
		||||
	# You can define selection criteria that are applied each
 | 
			
		||||
	# time log is reported. This way, it is possible to control the
 | 
			
		||||
	# amount of log that is displayed on output and you can select
 | 
			
		||||
	# only parts of the log that are important for you. To define
 | 
			
		||||
	# selection criteria, use fields from log report. See also
 | 
			
		||||
	# <lvm command> --logonly --configreport log -S help for the
 | 
			
		||||
	# list of possible fields and selection operators. You can also
 | 
			
		||||
	# define selection criteria for log report on command line directly
 | 
			
		||||
	# using <lvm command> --configreport log -S <selection criteria>
 | 
			
		||||
	# which has precedence over log/command_log_selection setting.
 | 
			
		||||
	# For more information about selection criteria in general, see
 | 
			
		||||
	# lvm(8) man page.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# command_log_selection = "!(log_type=status && message=success)"
 | 
			
		||||
 | 
			
		||||
	# Configuration option log/verbose.
 | 
			
		||||
	# Controls the messages sent to stdout or stderr.
 | 
			
		||||
	verbose = 0
 | 
			
		||||
@@ -625,9 +523,9 @@ log {
 | 
			
		||||
	# Select log messages by class.
 | 
			
		||||
	# Some debugging messages are assigned to a class and only appear in
 | 
			
		||||
	# debug output if the class is listed here. Classes currently
 | 
			
		||||
	# available: memory, devices, io, activation, allocation, lvmetad,
 | 
			
		||||
	# available: memory, devices, activation, allocation, lvmetad,
 | 
			
		||||
	# metadata, cache, locking, lvmpolld. Use "all" to see everything.
 | 
			
		||||
	debug_classes = [ "memory", "devices", "io", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld", "dbus" ]
 | 
			
		||||
	debug_classes = [ "memory", "devices", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld" ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Configuration section backup.
 | 
			
		||||
@@ -691,7 +589,7 @@ global {
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/units.
 | 
			
		||||
	# Default value for --units argument.
 | 
			
		||||
	units = "r"
 | 
			
		||||
	units = "h"
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/si_unit_consistency.
 | 
			
		||||
	# Distinguish between powers of 1024 and 1000 bytes.
 | 
			
		||||
@@ -716,17 +614,29 @@ global {
 | 
			
		||||
	activation = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/fallback_to_lvm1.
 | 
			
		||||
	# This setting is no longer used.
 | 
			
		||||
	# Try running LVM1 tools if LVM cannot communicate with DM.
 | 
			
		||||
	# This option only applies to 2.4 kernels and is provided to help
 | 
			
		||||
	# switch between device-mapper kernels and LVM1 kernels. The LVM1
 | 
			
		||||
	# tools need to be installed with .lvm1 suffices, e.g. vgscan.lvm1.
 | 
			
		||||
	# They will stop working once the lvm2 on-disk metadata format is used.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# fallback_to_lvm1 = 0
 | 
			
		||||
	# fallback_to_lvm1 = @DEFAULT_FALLBACK_TO_LVM1@
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/format.
 | 
			
		||||
	# This setting is no longer used.
 | 
			
		||||
	# The default metadata format that commands should use.
 | 
			
		||||
	# The -M 1|2 option overrides this setting.
 | 
			
		||||
	# 
 | 
			
		||||
	# Accepted values:
 | 
			
		||||
	#   lvm1
 | 
			
		||||
	#   lvm2
 | 
			
		||||
	# 
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# format = "lvm2"
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/format_libraries.
 | 
			
		||||
	# This setting is no longer used.
 | 
			
		||||
	# Shared libraries that process different metadata formats.
 | 
			
		||||
	# If support for LVM1 metadata was compiled as a shared library use
 | 
			
		||||
	# format_libraries = "liblvm2format1.so"
 | 
			
		||||
	# This configuration option does not have a default value defined.
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/segment_libraries.
 | 
			
		||||
@@ -823,6 +733,13 @@ global {
 | 
			
		||||
	# encountered the internal error. Please only enable for debugging.
 | 
			
		||||
	abort_on_internal_errors = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/detect_internal_vg_cache_corruption.
 | 
			
		||||
	# Internal verification of VG structures.
 | 
			
		||||
	# Check if CRC matches when a parsed VG is used multiple times. This
 | 
			
		||||
	# is useful to catch unexpected changes to cached VG structures.
 | 
			
		||||
	# Please only enable for debugging.
 | 
			
		||||
	detect_internal_vg_cache_corruption = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/metadata_read_only.
 | 
			
		||||
	# No operations that change on-disk metadata are permitted.
 | 
			
		||||
	# Additionally, read-only commands that encounter metadata in need of
 | 
			
		||||
@@ -905,11 +822,6 @@ global {
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# lvdisplay_shows_full_device_path = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/use_aio.
 | 
			
		||||
	# Use async I/O when reading and writing devices.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# use_aio = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/use_lvmetad.
 | 
			
		||||
	# Use lvmetad to cache metadata and reduce disk scanning.
 | 
			
		||||
	# When enabled (and running), lvmetad provides LVM commands with VG
 | 
			
		||||
@@ -939,23 +851,13 @@ global {
 | 
			
		||||
	# devices/global_filter.
 | 
			
		||||
	use_lvmetad = @DEFAULT_USE_LVMETAD@
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/lvmetad_update_wait_time.
 | 
			
		||||
	# Number of seconds a command will wait for lvmetad update to finish.
 | 
			
		||||
	# After waiting for this period, a command will not use lvmetad, and
 | 
			
		||||
	# will revert to disk scanning.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# lvmetad_update_wait_time = 10
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/use_lvmlockd.
 | 
			
		||||
	# Use lvmlockd for locking among hosts using LVM on shared storage.
 | 
			
		||||
	# Applicable only if LVM is compiled with lockd support in which
 | 
			
		||||
	# case there is also lvmlockd(8) man page available for more
 | 
			
		||||
	# information.
 | 
			
		||||
	# See lvmlockd(8) for more information.
 | 
			
		||||
	use_lvmlockd = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/lvmlockd_lock_retries.
 | 
			
		||||
	# Retry lvmlockd lock requests this many times.
 | 
			
		||||
	# Applicable only if LVM is compiled with lockd support
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# lvmlockd_lock_retries = 3
 | 
			
		||||
 | 
			
		||||
@@ -965,8 +867,7 @@ global {
 | 
			
		||||
	# LVs have been created, the internal LV needs to be extended. lvcreate
 | 
			
		||||
	# will automatically extend the internal LV when needed by the amount
 | 
			
		||||
	# specified here. Setting this to 0 disables the automatic extension
 | 
			
		||||
	# and can cause lvcreate to fail. Applicable only if LVM is compiled
 | 
			
		||||
	# with lockd support
 | 
			
		||||
	# and can cause lvcreate to fail.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# sanlock_lv_extend = 256
 | 
			
		||||
 | 
			
		||||
@@ -1025,7 +926,7 @@ global {
 | 
			
		||||
	# Configuration option global/cache_disabled_features.
 | 
			
		||||
	# Features to not use in the cache driver.
 | 
			
		||||
	# This can be helpful for testing, or to avoid using a feature that is
 | 
			
		||||
	# causing problems. Features include: policy_mq, policy_smq, metadata2.
 | 
			
		||||
	# causing problems. Features include: policy_mq, policy_smq.
 | 
			
		||||
	# 
 | 
			
		||||
	# Example
 | 
			
		||||
	# cache_disabled_features = [ "policy_smq" ]
 | 
			
		||||
@@ -1070,12 +971,6 @@ global {
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# cache_repair_options = [ "" ]
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/fsadm_executable.
 | 
			
		||||
	# The full path to the fsadm command.
 | 
			
		||||
	# LVM uses this command to help with lvresize -r operations.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# fsadm_executable = "@FSADM_PATH@"
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/system_id_source.
 | 
			
		||||
	# The method LVM uses to set the local system ID.
 | 
			
		||||
	# Volume Groups can also be given a system ID (by vgcreate, vgchange,
 | 
			
		||||
@@ -1119,24 +1014,7 @@ global {
 | 
			
		||||
	# a native systemd service, which allows it to be started on demand,
 | 
			
		||||
	# and to use its own control group. When this option is disabled, LVM
 | 
			
		||||
	# commands will supervise long running operations by forking themselves.
 | 
			
		||||
	# Applicable only if LVM is compiled with lvmpolld support.
 | 
			
		||||
	use_lvmpolld = @DEFAULT_USE_LVMPOLLD@
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/notify_dbus.
 | 
			
		||||
	# Enable D-Bus notification from LVM commands.
 | 
			
		||||
	# When enabled, an LVM command that changes PVs, changes VG metadata,
 | 
			
		||||
	# or changes the activation state of an LV will send a notification.
 | 
			
		||||
	notify_dbus = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option global/io_memory_size.
 | 
			
		||||
	# The amount of memory in KiB that LVM allocates to perform disk io.
 | 
			
		||||
	# LVM performance may benefit from more io memory when there are many
 | 
			
		||||
	# disks or VG metadata is large. Increasing this size may be necessary
 | 
			
		||||
	# when a single copy of VG metadata is larger than the current setting.
 | 
			
		||||
	# This value should usually not be decreased from the default; setting
 | 
			
		||||
	# it too low can result in lvm failing to read VGs.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# io_memory_size = 8192
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Configuration section activation.
 | 
			
		||||
@@ -1184,8 +1062,7 @@ activation {
 | 
			
		||||
	# Configuration option activation/missing_stripe_filler.
 | 
			
		||||
	# Method to fill missing stripes when activating an incomplete LV.
 | 
			
		||||
	# Using 'error' will make inaccessible parts of the device return I/O
 | 
			
		||||
	# errors on access. Using 'zero' will return success (and zero) on I/O
 | 
			
		||||
	# You can instead use a device path, in which case,
 | 
			
		||||
	# errors on access. You can instead use a device path, in which case,
 | 
			
		||||
	# that device will be used in place of missing stripes. Using anything
 | 
			
		||||
	# other than 'error' with mirrored or snapshotted volumes is likely to
 | 
			
		||||
	# result in data corruption.
 | 
			
		||||
@@ -1232,8 +1109,8 @@ activation {
 | 
			
		||||
	#   @*
 | 
			
		||||
	#     Selects an LV if a tag defined on the host is also set on the LV
 | 
			
		||||
	#     or VG. See tags/hosttags. If any host tags exist but volume_list
 | 
			
		||||
	#     is not defined, a default single-entry list containing '@*'
 | 
			
		||||
	#     is assumed.
 | 
			
		||||
	#     is not defined, a default single-entry list containing '@*' is
 | 
			
		||||
	#     assumed.
 | 
			
		||||
	# 
 | 
			
		||||
	# Example
 | 
			
		||||
	# volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
 | 
			
		||||
@@ -1269,11 +1146,11 @@ activation {
 | 
			
		||||
	#   @*
 | 
			
		||||
	#     Selects an LV if a tag defined on the host is also set on the LV
 | 
			
		||||
	#     or VG. See tags/hosttags. If any host tags exist but volume_list
 | 
			
		||||
	#     is not defined, a default single-entry list containing '@*'
 | 
			
		||||
	#     is assumed.
 | 
			
		||||
	#     is not defined, a default single-entry list containing '@*' is
 | 
			
		||||
	#     assumed.
 | 
			
		||||
	# 
 | 
			
		||||
	# Example
 | 
			
		||||
	# auto_activation_volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
 | 
			
		||||
	# volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
 | 
			
		||||
	# 
 | 
			
		||||
	# This configuration option does not have a default value defined.
 | 
			
		||||
 | 
			
		||||
@@ -1295,20 +1172,19 @@ activation {
 | 
			
		||||
	#   @*
 | 
			
		||||
	#     Selects an LV if a tag defined on the host is also set on the LV
 | 
			
		||||
	#     or VG. See tags/hosttags. If any host tags exist but volume_list
 | 
			
		||||
	#     is not defined, a default single-entry list containing '@*'
 | 
			
		||||
	#     is assumed.
 | 
			
		||||
	#     is not defined, a default single-entry list containing '@*' is
 | 
			
		||||
	#     assumed.
 | 
			
		||||
	# 
 | 
			
		||||
	# Example
 | 
			
		||||
	# read_only_volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
 | 
			
		||||
	# volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
 | 
			
		||||
	# 
 | 
			
		||||
	# This configuration option does not have a default value defined.
 | 
			
		||||
 | 
			
		||||
	# Configuration option activation/raid_region_size.
 | 
			
		||||
	# Size in KiB of each raid or mirror synchronization region.
 | 
			
		||||
	# The clean/dirty state of data is tracked for each region.
 | 
			
		||||
	# The value is rounded down to a power of two if necessary, and
 | 
			
		||||
	# is ignored if it is not a multiple of the machine memory page size.
 | 
			
		||||
	raid_region_size = 2048
 | 
			
		||||
	# For raid or mirror segment types, this is the amount of data that is
 | 
			
		||||
	# copied at once when initializing, or moved at once by pvmove.
 | 
			
		||||
	raid_region_size = 512
 | 
			
		||||
 | 
			
		||||
	# Configuration option activation/error_when_full.
 | 
			
		||||
	# Return errors if a thin pool runs out of space.
 | 
			
		||||
@@ -1539,33 +1415,6 @@ activation {
 | 
			
		||||
# This configuration section has an automatic default value.
 | 
			
		||||
# metadata {
 | 
			
		||||
 | 
			
		||||
	# Configuration option metadata/check_pv_device_sizes.
 | 
			
		||||
	# Check device sizes are not smaller than corresponding PV sizes.
 | 
			
		||||
	# If device size is less than corresponding PV size found in metadata,
 | 
			
		||||
	# there is always a risk of data loss. If this option is set, then LVM
 | 
			
		||||
	# issues a warning message each time it finds that the device size is
 | 
			
		||||
	# less than corresponding PV size. You should not disable this unless
 | 
			
		||||
	# you are absolutely sure about what you are doing!
 | 
			
		||||
	# This configuration option is advanced.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# check_pv_device_sizes = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option metadata/record_lvs_history.
 | 
			
		||||
	# When enabled, LVM keeps history records about removed LVs in
 | 
			
		||||
	# metadata. The information that is recorded in metadata for
 | 
			
		||||
	# historical LVs is reduced when compared to original
 | 
			
		||||
	# information kept in metadata for live LVs. Currently, this
 | 
			
		||||
	# feature is supported for thin and thin snapshot LVs only.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# record_lvs_history = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option metadata/lvs_history_retention_time.
 | 
			
		||||
	# Retention time in seconds after which a record about individual
 | 
			
		||||
	# historical logical volume is automatically destroyed.
 | 
			
		||||
	# A value of 0 disables this feature.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# lvs_history_retention_time = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option metadata/pvmetadatacopies.
 | 
			
		||||
	# Number of copies of metadata to store on each PV.
 | 
			
		||||
	# The --pvmetadatacopies option overrides this setting.
 | 
			
		||||
@@ -1644,42 +1493,14 @@ activation {
 | 
			
		||||
# This configuration section has an automatic default value.
 | 
			
		||||
# report {
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/output_format.
 | 
			
		||||
	# Format of LVM command's report output.
 | 
			
		||||
	# If there is more than one report per command, then the format
 | 
			
		||||
	# is applied for all reports. You can also change output format
 | 
			
		||||
	# directly on command line using --reportformat option which
 | 
			
		||||
	# has precedence over log/output_format setting.
 | 
			
		||||
	# Accepted values:
 | 
			
		||||
	#   basic
 | 
			
		||||
	#     Original format with columns and rows. If there is more than
 | 
			
		||||
	#     one report per command, each report is prefixed with report's
 | 
			
		||||
	#     name for identification.
 | 
			
		||||
	#   json
 | 
			
		||||
	#     JSON format.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# output_format = "basic"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/compact_output.
 | 
			
		||||
	# Do not print empty values for all report fields.
 | 
			
		||||
	# If enabled, all fields that don't have a value set for any of the
 | 
			
		||||
	# rows reported are skipped and not printed. Compact output is
 | 
			
		||||
	# applicable only if report/buffered is enabled. If you need to
 | 
			
		||||
	# compact only specified fields, use compact_output=0 and define
 | 
			
		||||
	# report/compact_output_cols configuration setting instead.
 | 
			
		||||
	# Do not print empty report fields.
 | 
			
		||||
	# Fields that don't have a value set for any of the rows reported are
 | 
			
		||||
	# skipped and not printed. Compact output is applicable only if
 | 
			
		||||
	# report/buffered is enabled.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# compact_output = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/compact_output_cols.
 | 
			
		||||
	# Do not print empty values for specified report fields.
 | 
			
		||||
	# If defined, specified fields that don't have a value set for any
 | 
			
		||||
	# of the rows reported are skipped and not printed. Compact output
 | 
			
		||||
	# is applicable only if report/buffered is enabled. If you need to
 | 
			
		||||
	# compact all fields, use compact_output=1 instead in which case
 | 
			
		||||
	# the compact_output_cols setting is then ignored.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# compact_output_cols = ""
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/aligned.
 | 
			
		||||
	# Align columns in report output.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
@@ -1720,11 +1541,11 @@ activation {
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# quoted = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/columns_as_rows.
 | 
			
		||||
	# Configuration option report/colums_as_rows.
 | 
			
		||||
	# Output each column as a row.
 | 
			
		||||
	# If set, this also implies report/prefixes=1.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# columns_as_rows = 0
 | 
			
		||||
	# colums_as_rows = 0
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/binary_values_as_numeric.
 | 
			
		||||
	# Use binary values 0 or 1 instead of descriptive literal values.
 | 
			
		||||
@@ -1975,77 +1796,6 @@ activation {
 | 
			
		||||
	# See 'pvs --segments -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# pvsegs_cols_verbose = "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/vgs_cols_full.
 | 
			
		||||
	# List of columns to report for lvm fullreport's 'vgs' subreport.
 | 
			
		||||
	# See 'vgs -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# vgs_cols_full = "vg_all"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/pvs_cols_full.
 | 
			
		||||
	# List of columns to report for lvm fullreport's 'vgs' subreport.
 | 
			
		||||
	# See 'pvs -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# pvs_cols_full = "pv_all"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/lvs_cols_full.
 | 
			
		||||
	# List of columns to report for lvm fullreport's 'lvs' subreport.
 | 
			
		||||
	# See 'lvs -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# lvs_cols_full = "lv_all"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/pvsegs_cols_full.
 | 
			
		||||
	# List of columns to report for lvm fullreport's 'pvseg' subreport.
 | 
			
		||||
	# See 'pvs --segments -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# pvsegs_cols_full = "pvseg_all,pv_uuid,lv_uuid"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/segs_cols_full.
 | 
			
		||||
	# List of columns to report for lvm fullreport's 'seg' subreport.
 | 
			
		||||
	# See 'lvs --segments -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# segs_cols_full = "seg_all,lv_uuid"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/vgs_sort_full.
 | 
			
		||||
	# List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.
 | 
			
		||||
	# See 'vgs -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# vgs_sort_full = "vg_name"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/pvs_sort_full.
 | 
			
		||||
	# List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.
 | 
			
		||||
	# See 'pvs -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# pvs_sort_full = "pv_name"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/lvs_sort_full.
 | 
			
		||||
	# List of columns to sort by when reporting lvm fullreport's 'lvs' subreport.
 | 
			
		||||
	# See 'lvs -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# lvs_sort_full = "vg_name,lv_name"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/pvsegs_sort_full.
 | 
			
		||||
	# List of columns to sort by when reporting for lvm fullreport's 'pvseg' subreport.
 | 
			
		||||
	# See 'pvs --segments -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# pvsegs_sort_full = "pv_uuid,pvseg_start"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/segs_sort_full.
 | 
			
		||||
	# List of columns to sort by when reporting lvm fullreport's 'seg' subreport.
 | 
			
		||||
	# See 'lvs --segments -o help' for the list of possible fields.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# segs_sort_full = "lv_uuid,seg_start"
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/mark_hidden_devices.
 | 
			
		||||
	# Use brackets [] to mark hidden devices.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# mark_hidden_devices = 1
 | 
			
		||||
 | 
			
		||||
	# Configuration option report/two_word_unknown_device.
 | 
			
		||||
	# Use the two words 'unknown device' in place of '[unknown]'.
 | 
			
		||||
	# This is displayed when the device for a PV is not known.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# two_word_unknown_device = 0
 | 
			
		||||
# }
 | 
			
		||||
 | 
			
		||||
# Configuration section dmeventd.
 | 
			
		||||
@@ -2078,15 +1828,6 @@ dmeventd {
 | 
			
		||||
	# warning is repeated when 85%, 90% and 95% of the pool is filled.
 | 
			
		||||
	thin_library = "libdevmapper-event-lvm2thin.so"
 | 
			
		||||
 | 
			
		||||
	# Configuration option dmeventd/thin_command.
 | 
			
		||||
	# The plugin runs command with each 5% increment when thin-pool data volume
 | 
			
		||||
	# or metadata volume gets above 50%.
 | 
			
		||||
	# Command which starts with 'lvm ' prefix is internal lvm command.
 | 
			
		||||
	# You can write your own handler to customise behaviour in more details.
 | 
			
		||||
	# User handler is specified with the full path starting with '/'.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# thin_command = "lvm lvextend --use-policies"
 | 
			
		||||
 | 
			
		||||
	# Configuration option dmeventd/executable.
 | 
			
		||||
	# The full path to the dmeventd binary.
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# DO NOT EDIT THIS FILE!
 | 
			
		||||
#
 | 
			
		||||
# LVM configuration profile used by lvmdbusd daemon.
 | 
			
		||||
#
 | 
			
		||||
# This sets up LVM to produce output in the most suitable format for processing
 | 
			
		||||
# by lvmdbusd daemon which utilizes LVM shell to execute LVM commands.
 | 
			
		||||
#
 | 
			
		||||
# Do not edit this file in any way. This profile is distributed together with
 | 
			
		||||
# lvmdbusd and it contains configuration that is important for lvmdbusd to
 | 
			
		||||
# cooperate and interface with LVM correctly.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
global {
 | 
			
		||||
	# use bytes for expected and deterministic output
 | 
			
		||||
	units=b
 | 
			
		||||
	# no need for suffix if we have units set
 | 
			
		||||
	suffix=0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
report {
 | 
			
		||||
	compact_output=0
 | 
			
		||||
	compact_output_cols=""
 | 
			
		||||
	binary_values_as_numeric=0
 | 
			
		||||
	# time in number of seconds since the Epoch
 | 
			
		||||
	time_format="%s"
 | 
			
		||||
	mark_hidden_devices=1
 | 
			
		||||
	# lvmdbusd expects JSON output
 | 
			
		||||
	output_format=json
 | 
			
		||||
	# *_cols_full for lvm fullreport's fields which lvmdbusd relies on to update its state
 | 
			
		||||
	vgs_cols_full="vg_name,vg_uuid,vg_fmt,vg_size,vg_free,vg_sysid,vg_extent_size,vg_extent_count,vg_free_count,vg_profile,max_lv,max_pv,pv_count,lv_count,snap_count,vg_seqno,vg_mda_count,vg_mda_free,vg_mda_size,vg_mda_used_count,vg_attr,vg_tags"
 | 
			
		||||
	pvs_cols_full="pv_name,pv_uuid,pv_fmt,pv_size,pv_free,pv_used,dev_size,pv_mda_size,pv_mda_free,pv_ba_start,pv_ba_size,pe_start,pv_pe_count,pv_pe_alloc_count,pv_attr,pv_tags,vg_name,vg_uuid"
 | 
			
		||||
	lvs_cols_full="lv_uuid,lv_name,lv_path,lv_size,vg_name,pool_lv_uuid,pool_lv,origin_uuid,origin,data_percent,lv_attr,lv_tags,vg_uuid,lv_active,data_lv,metadata_lv,lv_parent,lv_role,lv_layout"
 | 
			
		||||
	pvsegs_cols_full="pvseg_start,pvseg_size,segtype,pv_uuid,lv_uuid,pv_name"
 | 
			
		||||
	segs_cols_full="seg_pe_ranges,segtype,lv_uuid"
 | 
			
		||||
	vgs_sort_full="vg_name"
 | 
			
		||||
	pvs_sort_full="pv_name"
 | 
			
		||||
	lvs_sort_full="vg_name,lv_name"
 | 
			
		||||
	pvsegs_sort_full="pv_uuid,pvseg_start"
 | 
			
		||||
	segs_sort_full="lv_uuid,seg_start"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log {
 | 
			
		||||
	# lvmdbusd relies on command log report to inspect LVM command's execution status
 | 
			
		||||
	report_command_log=1
 | 
			
		||||
	# display only outermost LVM shell-related log that lvmdbusd inspects first after LVM command execution (it calls 'lastlog' for more detailed log afterwards if needed)
 | 
			
		||||
	command_log_selection="log_context=shell"
 | 
			
		||||
	command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
 | 
			
		||||
	command_log_sort="log_seq_num"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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.
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +51,6 @@ local {
 | 
			
		||||
	# Configuration option local/host_id.
 | 
			
		||||
	# The lvmlockd sanlock host_id.
 | 
			
		||||
	# This must be unique among all hosts, and must be between 1 and 2000.
 | 
			
		||||
	# Applicable only if LVM is compiled with lockd support
 | 
			
		||||
	# This configuration option has an automatic default value.
 | 
			
		||||
	# host_id = 0
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,146 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
 * This 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
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Coverity usage:
 | 
			
		||||
 *
 | 
			
		||||
 * translate model into xml
 | 
			
		||||
 * cov-make-library -of coverity_model.xml coverity_model.c
 | 
			
		||||
 *
 | 
			
		||||
 * compile (using outdir 'cov'):
 | 
			
		||||
 * cov-build --dir=cov make CC=gcc
 | 
			
		||||
 *
 | 
			
		||||
 * analyze (agressively, using 'cov')
 | 
			
		||||
 * cov-analyze --dir cov --wait-for-license --hfa --concurrency --enable-fnptr --enable-constraint-fpp --security --all --aggressiveness-level=high --field-offset-escape --user-model-file=coverity/coverity_model.xml
 | 
			
		||||
 *
 | 
			
		||||
 * generate html output (to 'html' from 'cov'):
 | 
			
		||||
 * cov-format-errors --dir cov  --html-output html
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct lv_segment;
 | 
			
		||||
struct logical_volume;
 | 
			
		||||
 | 
			
		||||
struct lv_segment *first_seg(const struct logical_volume *lv)
 | 
			
		||||
{
 | 
			
		||||
	return ((struct lv_segment **)lv)[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	const char *s = src;
 | 
			
		||||
	char *d = dest;
 | 
			
		||||
 | 
			
		||||
	while (n-- > 0)
 | 
			
		||||
		if ((*d++ = *s++) == (char) c)
 | 
			
		||||
			return d;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 2 lines bellow needs to be placed in coverity/config/user_nodefs.h
 | 
			
		||||
 * Not sure about any other way.
 | 
			
		||||
 * Without them, coverity shows warning since x86 system header files
 | 
			
		||||
 * are using inline assembly to reset fdset
 | 
			
		||||
 */
 | 
			
		||||
//#nodef FD_ZERO model_FD_ZERO
 | 
			
		||||
//void model_FD_ZERO(void *fdset);
 | 
			
		||||
 | 
			
		||||
void model_FD_ZERO(void *fdset)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 1024 / 8 / sizeof(long); ++i)
 | 
			
		||||
		((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
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
struct cmd_context;
 | 
			
		||||
struct profile;
 | 
			
		||||
 | 
			
		||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
 | 
			
		||||
{
 | 
			
		||||
        return "text";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *find_config_tree_str_allow_empty(struct cmd_context *cmd, int id, struct profile *profile)
 | 
			
		||||
{
 | 
			
		||||
        return "text";
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Until fixed coverity case# 00531860:
 | 
			
		||||
 *   A FORWARD_NULL false positive on a recursive function call
 | 
			
		||||
 *
 | 
			
		||||
 * model also these functions:
 | 
			
		||||
 */
 | 
			
		||||
/*
 | 
			
		||||
const struct dm_config_node;
 | 
			
		||||
const struct dm_config_node *find_config_tree_array(struct cmd_context *cmd, int id, struct profile *profile)
 | 
			
		||||
{
 | 
			
		||||
	const struct dm_config_node *cn;
 | 
			
		||||
 | 
			
		||||
	return cn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct dm_config_node *find_config_tree_node(struct cmd_context *cmd, int id, struct profile *profile)
 | 
			
		||||
{
 | 
			
		||||
	const struct dm_config_node *cn;
 | 
			
		||||
 | 
			
		||||
	return cn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int find_config_tree_bool(struct cmd_context *cmd, int id, struct profile *profile)
 | 
			
		||||
{
 | 
			
		||||
	int b;
 | 
			
		||||
 | 
			
		||||
	return b;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
@@ -41,19 +41,11 @@ ifeq ("@BUILD_LVMPOLLD@", "yes")
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_LVMLOCKD@", "yes")
 | 
			
		||||
  SUBDIRS += lvmlockd
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_LVMDBUSD@", "yes")
 | 
			
		||||
  SUBDIRS += lvmdbusd
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ("@BUILD_DMFILEMAPD@", "yes")
 | 
			
		||||
  SUBDIRS += dmfilemapd
 | 
			
		||||
  SUBDIRS += lvmlockd 
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(MAKECMDGOALS),distclean)
 | 
			
		||||
  SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd
 | 
			
		||||
  SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
@@ -31,9 +31,9 @@ SALCK_LIBS = @SALCK_LIBS@
 | 
			
		||||
SALCK_CFLAGS = @SALCK_CFLAGS@
 | 
			
		||||
 | 
			
		||||
SOURCES = \
 | 
			
		||||
	clvmd-command.c\
 | 
			
		||||
	clvmd.c\
 | 
			
		||||
	lvm-functions.c\
 | 
			
		||||
	clvmd-command.c  \
 | 
			
		||||
	clvmd.c          \
 | 
			
		||||
	lvm-functions.c  \
 | 
			
		||||
	refresh_clvmd.c
 | 
			
		||||
 | 
			
		||||
ifneq (,$(findstring cman,, "@CLVMD@,"))
 | 
			
		||||
@@ -72,17 +72,26 @@ endif
 | 
			
		||||
TARGETS = \
 | 
			
		||||
	clvmd
 | 
			
		||||
 | 
			
		||||
LVMLIBS = $(LVMINTERNAL_LIBS)
 | 
			
		||||
 | 
			
		||||
ifeq ("@DMEVENTD@", "yes")
 | 
			
		||||
	LVMLIBS += -ldevmapper-event
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS) -laio
 | 
			
		||||
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) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
 | 
			
		||||
	      -o clvmd $(OBJECTS) $(LMLIBS) $(LIBS)
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -o clvmd $(OBJECTS) \
 | 
			
		||||
		$(LVMLIBS) $(LMLIBS) $(LIBS)
 | 
			
		||||
 | 
			
		||||
.PHONY: install_clvmd
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Definitions for CLVMD server and clients */
 | 
			
		||||
@@ -50,7 +50,7 @@ struct clvm_header {
 | 
			
		||||
#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"
 | 
			
		||||
static const char CLVMD_SOCKNAME[]= DEFAULT_RUN_DIR "/clvmd.sock";
 | 
			
		||||
 | 
			
		||||
/* Internal commands & replies */
 | 
			
		||||
#define CLVMD_CMD_REPLY    1
 | 
			
		||||
@@ -76,10 +76,8 @@ struct clvm_header {
 | 
			
		||||
#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
 | 
			
		||||
#define NODE_ALL	"*"
 | 
			
		||||
#define NODE_LOCAL	"."
 | 
			
		||||
#define NODE_REMOTE	"^"
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -108,6 +108,7 @@ int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
@@ -170,10 +171,8 @@ int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
 | 
			
		||||
 | 
			
		||||
	/* Check the status of the command and return the error text */
 | 
			
		||||
	if (status) {
 | 
			
		||||
		if (*buf)
 | 
			
		||||
			*retlen = dm_snprintf(*buf, buflen, "%s", strerror(status)) + 1;
 | 
			
		||||
		else
 | 
			
		||||
			*retlen = 0;
 | 
			
		||||
		*retlen = 1 + ((*buf) ? dm_snprintf(*buf, buflen, "%s",
 | 
			
		||||
						    strerror(status)) : -1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
@@ -207,7 +206,7 @@ static int lock_vg(struct local_client *client)
 | 
			
		||||
	lock_mode = ((int) lock_cmd & LCK_TYPE_MASK);
 | 
			
		||||
	/* lock_flags = args[1]; */
 | 
			
		||||
	lockname = &args[2];
 | 
			
		||||
	DEBUGLOG("(%p) doing PRE command LOCK_VG '%s' at %x\n", client, lockname, lock_cmd);
 | 
			
		||||
	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)))
 | 
			
		||||
@@ -324,7 +323,7 @@ void cmd_client_cleanup(struct local_client *client)
 | 
			
		||||
	int lkid;
 | 
			
		||||
	char *lockname;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) Client thread cleanup\n", client);
 | 
			
		||||
	DEBUGLOG("Client thread cleanup (%p)\n", client);
 | 
			
		||||
	if (!client->bits.localsock.private)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
@@ -333,7 +332,7 @@ void cmd_client_cleanup(struct local_client *client)
 | 
			
		||||
	dm_hash_iterate(v, lock_hash) {
 | 
			
		||||
		lkid = (int)(long)dm_hash_get_data(lock_hash, v);
 | 
			
		||||
		lockname = dm_hash_get_key(lock_hash, v);
 | 
			
		||||
		DEBUGLOG("(%p) Cleanup: Unlocking lock %s %x\n", client, lockname, lkid);
 | 
			
		||||
		DEBUGLOG("Cleanup (%p): Unlocking lock %s %x\n", client, lockname, lkid);
 | 
			
		||||
		(void) sync_unlock(lockname, lkid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -532,7 +532,6 @@ static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
 | 
			
		||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
 | 
			
		||||
				 const char *errtext)
 | 
			
		||||
{
 | 
			
		||||
	static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
	struct iovec iov[2];
 | 
			
		||||
	cs_error_t err;
 | 
			
		||||
	int target_node;
 | 
			
		||||
@@ -547,10 +546,7 @@ static int _cluster_send_message(const void *buf, int msglen, const char *csid,
 | 
			
		||||
	iov[1].iov_base = (char *)buf;
 | 
			
		||||
	iov[1].iov_len = msglen;
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&_mutex);
 | 
			
		||||
	err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
 | 
			
		||||
	pthread_mutex_unlock(&_mutex);
 | 
			
		||||
 | 
			
		||||
	return cs_to_errno(err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -425,6 +425,8 @@ static void _add_up_node(const char *csid)
 | 
			
		||||
	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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -58,7 +58,6 @@
 | 
			
		||||
/* Head of the fd list. Also contains
 | 
			
		||||
   the cluster_socket details */
 | 
			
		||||
static struct local_client local_client_head;
 | 
			
		||||
static int _local_client_count = 0;
 | 
			
		||||
 | 
			
		||||
static unsigned short global_xid = 0;	/* Last transaction ID issued */
 | 
			
		||||
 | 
			
		||||
@@ -69,37 +68,6 @@ static unsigned max_csid_len;
 | 
			
		||||
static unsigned max_cluster_message;
 | 
			
		||||
static unsigned max_cluster_member_name_len;
 | 
			
		||||
 | 
			
		||||
static void _add_client(struct local_client *new_client, struct local_client *existing_client)
 | 
			
		||||
{
 | 
			
		||||
	_local_client_count++;
 | 
			
		||||
	DEBUGLOG("(%p) Adding listener for fd %d. (Now %d monitored fds.)\n", new_client, new_client->fd, _local_client_count);
 | 
			
		||||
	new_client->next = existing_client->next;
 | 
			
		||||
	existing_client->next = new_client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int add_client(struct local_client *new_client)
 | 
			
		||||
{
 | 
			
		||||
	_add_client(new_client, &local_client_head);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 0 if delfd is found and removed from list */
 | 
			
		||||
static int _del_client(struct local_client *delfd)
 | 
			
		||||
{
 | 
			
		||||
	struct local_client *lastfd, *thisfd;
 | 
			
		||||
 | 
			
		||||
	for (lastfd = &local_client_head; (thisfd = lastfd->next); lastfd = thisfd)
 | 
			
		||||
		if (thisfd == delfd) {
 | 
			
		||||
			DEBUGLOG("(%p) Removing listener for fd %d\n", thisfd, thisfd->fd);
 | 
			
		||||
			lastfd->next = delfd->next;
 | 
			
		||||
			_local_client_count--;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Structure of items on the LVM thread list */
 | 
			
		||||
struct lvm_thread_cmd {
 | 
			
		||||
	struct dm_list list;
 | 
			
		||||
@@ -124,7 +92,6 @@ static const size_t STACK_SIZE = 128 * 1024;
 | 
			
		||||
static pthread_attr_t stack_attr;
 | 
			
		||||
static int lvm_thread_exit = 0;
 | 
			
		||||
static pthread_mutex_t lvm_thread_mutex;
 | 
			
		||||
static pthread_mutex_t _debuglog_mutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
static pthread_cond_t lvm_thread_cond;
 | 
			
		||||
static pthread_barrier_t lvm_start_barrier;
 | 
			
		||||
static struct dm_list lvm_cmd_head;
 | 
			
		||||
@@ -187,7 +154,7 @@ static void usage(const char *prog, FILE *file)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(file, "Usage: %s [options]\n"
 | 
			
		||||
		"   -C       Sets debug level (from -d) on all clvmd instances clusterwide\n"
 | 
			
		||||
		"   -d[<n>]  Set debug logging (0:none, 1:stderr (implies -f option), 2:syslog)\n"
 | 
			
		||||
		"   -d[n]    Set debug logging (0:none, 1:stderr (implies -f option), 2:syslog)\n"
 | 
			
		||||
		"   -E<uuid> Take this lock uuid as exclusively locked resource (for restart)\n"
 | 
			
		||||
		"   -f       Don't fork, run in the foreground\n"
 | 
			
		||||
		"   -h       Show this help information\n"
 | 
			
		||||
@@ -251,17 +218,14 @@ void debuglog(const char *fmt, ...)
 | 
			
		||||
 | 
			
		||||
	switch (clvmd_get_debug()) {
 | 
			
		||||
	case DEBUG_STDERR:
 | 
			
		||||
		pthread_mutex_lock(&_debuglog_mutex);
 | 
			
		||||
		va_start(ap,fmt);
 | 
			
		||||
		time(&P);
 | 
			
		||||
		fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime_r(&P, buf_ctime) + 4);
 | 
			
		||||
		vfprintf(stderr, fmt, ap);
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
		fflush(stderr);
 | 
			
		||||
		pthread_mutex_unlock(&_debuglog_mutex);
 | 
			
		||||
		break;
 | 
			
		||||
	case DEBUG_SYSLOG:
 | 
			
		||||
		pthread_mutex_lock(&_debuglog_mutex);
 | 
			
		||||
		if (!syslog_init) {
 | 
			
		||||
			openlog("clvmd", LOG_PID, LOG_DAEMON);
 | 
			
		||||
			syslog_init = 1;
 | 
			
		||||
@@ -270,7 +234,6 @@ void debuglog(const char *fmt, ...)
 | 
			
		||||
		va_start(ap,fmt);
 | 
			
		||||
		vsyslog(LOG_DEBUG, fmt, ap);
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
		pthread_mutex_unlock(&_debuglog_mutex);
 | 
			
		||||
		break;
 | 
			
		||||
	case DEBUG_OFF:
 | 
			
		||||
		break;
 | 
			
		||||
@@ -411,7 +374,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
	/* Deal with command-line arguments */
 | 
			
		||||
	opterr = 0;
 | 
			
		||||
	optind = 0;
 | 
			
		||||
	while ((opt = getopt_long(argc, argv, "Vhfd:t:RST:CI:E:",
 | 
			
		||||
	while ((opt = getopt_long(argc, argv, "vVhfd:t:RST:CI:E:",
 | 
			
		||||
				  longopts, NULL)) != -1) {
 | 
			
		||||
		switch (opt) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
@@ -554,7 +517,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
	/* Initialise the LVM thread variables */
 | 
			
		||||
	dm_list_init(&lvm_cmd_head);
 | 
			
		||||
	if (pthread_attr_init(&stack_attr) ||
 | 
			
		||||
	    pthread_attr_setstacksize(&stack_attr, STACK_SIZE + getpagesize())) {
 | 
			
		||||
	    pthread_attr_setstacksize(&stack_attr, STACK_SIZE)) {
 | 
			
		||||
		log_sys_error("pthread_attr_init", "");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
@@ -621,7 +584,6 @@ int main(int argc, char *argv[])
 | 
			
		||||
	local_client_head.fd = clops->get_main_cluster_fd();
 | 
			
		||||
	local_client_head.type = CLUSTER_MAIN_SOCK;
 | 
			
		||||
	local_client_head.callback = clops->cluster_fd_callback;
 | 
			
		||||
	_local_client_count++;
 | 
			
		||||
 | 
			
		||||
	/* Add the local socket to the list */
 | 
			
		||||
	if (!(newfd = dm_zalloc(sizeof(struct local_client)))) {
 | 
			
		||||
@@ -632,20 +594,17 @@ int main(int argc, char *argv[])
 | 
			
		||||
	newfd->fd = local_sock;
 | 
			
		||||
	newfd->type = LOCAL_RENDEZVOUS;
 | 
			
		||||
	newfd->callback = local_rendezvous_callback;
 | 
			
		||||
 | 
			
		||||
	(void) add_client(newfd);
 | 
			
		||||
	newfd->next = local_client_head.next;
 | 
			
		||||
	local_client_head.next = newfd;
 | 
			
		||||
 | 
			
		||||
	/* This needs to be started after cluster initialisation
 | 
			
		||||
	   as it may need to take out locks */
 | 
			
		||||
	DEBUGLOG("Starting LVM thread\n");
 | 
			
		||||
	DEBUGLOG("(%p) Main cluster socket fd %d with local socket %d (%p)\n",
 | 
			
		||||
		 &local_client_head, local_client_head.fd, newfd->fd, newfd);
 | 
			
		||||
	DEBUGLOG("Main cluster socket fd %d (%p) with local socket %d (%p)\n",
 | 
			
		||||
		 local_client_head.fd, &local_client_head, newfd->fd, newfd);
 | 
			
		||||
 | 
			
		||||
	/* Don't let anyone else to do work until we are started */
 | 
			
		||||
	if (pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params)) {
 | 
			
		||||
		log_sys_error("pthread_create", "");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
	pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params);
 | 
			
		||||
 | 
			
		||||
	/* Don't start until the LVM thread is ready */
 | 
			
		||||
	pthread_barrier_wait(&lvm_start_barrier);
 | 
			
		||||
@@ -675,7 +634,6 @@ int main(int argc, char *argv[])
 | 
			
		||||
 | 
			
		||||
	while ((delfd = local_client_head.next)) {
 | 
			
		||||
		local_client_head.next = delfd->next;
 | 
			
		||||
		_local_client_count--;
 | 
			
		||||
		/* Failing cleanup_zombie leaks... */
 | 
			
		||||
		if (delfd->type == LOCAL_SOCK && !cleanup_zombie(delfd))
 | 
			
		||||
			cmd_client_cleanup(delfd); /* calls sync_unlock */
 | 
			
		||||
@@ -737,13 +695,13 @@ static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
 | 
			
		||||
		pthread_mutex_init(&newfd->bits.localsock.mutex, NULL);
 | 
			
		||||
 | 
			
		||||
		if (fcntl(client_fd, F_SETFD, 1))
 | 
			
		||||
			DEBUGLOG("(%p) Setting CLOEXEC on client fd %d failed: %s\n", thisfd, client_fd, strerror(errno));
 | 
			
		||||
			DEBUGLOG("Setting CLOEXEC on client fd failed: %s\n", strerror(errno));
 | 
			
		||||
 | 
			
		||||
		newfd->fd = client_fd;
 | 
			
		||||
		newfd->type = LOCAL_SOCK;
 | 
			
		||||
		newfd->callback = local_sock_callback;
 | 
			
		||||
		newfd->bits.localsock.all_success = 1;
 | 
			
		||||
		DEBUGLOG("(%p) Got new connection on fd %d\n", newfd, newfd->fd);
 | 
			
		||||
		DEBUGLOG("Got new connection on fd %d (%p)\n", newfd->fd, newfd);
 | 
			
		||||
		*new_client = newfd;
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
@@ -765,8 +723,8 @@ static int local_pipe_callback(struct local_client *thisfd, char *buf,
 | 
			
		||||
	if (len == sizeof(int))
 | 
			
		||||
		memcpy(&status, buffer, sizeof(int));
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) Read on pipe %d, %d bytes, status %d\n",
 | 
			
		||||
		 thisfd, thisfd->fd, len, status);
 | 
			
		||||
	DEBUGLOG("Read on pipe %d, %d bytes, status %d\n",
 | 
			
		||||
		 thisfd->fd, len, status);
 | 
			
		||||
 | 
			
		||||
	/* EOF on pipe or an error, close it */
 | 
			
		||||
	if (len <= 0) {
 | 
			
		||||
@@ -789,11 +747,11 @@ static int local_pipe_callback(struct local_client *thisfd, char *buf,
 | 
			
		||||
		}
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else {
 | 
			
		||||
		DEBUGLOG("(%p) Background routine status was %d, sock_client %p\n",
 | 
			
		||||
			 thisfd, status, sock_client);
 | 
			
		||||
		DEBUGLOG("Background routine status was %d, sock_client (%p)\n",
 | 
			
		||||
			 status, sock_client);
 | 
			
		||||
		/* But has the client gone away ?? */
 | 
			
		||||
		if (!sock_client) {
 | 
			
		||||
			DEBUGLOG("(%p) Got pipe response for dead client, ignoring it\n", thisfd);
 | 
			
		||||
			DEBUGLOG("Got pipe response for dead client, ignoring it\n");
 | 
			
		||||
		} else {
 | 
			
		||||
			/* If error then just return that code */
 | 
			
		||||
			if (status)
 | 
			
		||||
@@ -833,7 +791,7 @@ static void timedout_callback(struct local_client *client, const char *csid,
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	clops->name_from_csid(csid, nodename);
 | 
			
		||||
	DEBUGLOG("(%p) Checking for a reply from %s\n", client, nodename);
 | 
			
		||||
	DEBUGLOG("Checking for a reply from %s\n", nodename);
 | 
			
		||||
	pthread_mutex_lock(&client->bits.localsock.mutex);
 | 
			
		||||
 | 
			
		||||
	reply = client->bits.localsock.replies;
 | 
			
		||||
@@ -843,7 +801,7 @@ static void timedout_callback(struct local_client *client, const char *csid,
 | 
			
		||||
	pthread_mutex_unlock(&client->bits.localsock.mutex);
 | 
			
		||||
 | 
			
		||||
	if (!reply) {
 | 
			
		||||
		DEBUGLOG("(%p) Node %s timed-out\n", client, nodename);
 | 
			
		||||
		DEBUGLOG("Node %s timed-out\n", nodename);
 | 
			
		||||
		add_reply_to_list(client, ETIMEDOUT, csid,
 | 
			
		||||
				  "Command timed out", 18);
 | 
			
		||||
	}
 | 
			
		||||
@@ -858,7 +816,7 @@ static void timedout_callback(struct local_client *client, const char *csid,
 | 
			
		||||
*/
 | 
			
		||||
static void request_timed_out(struct local_client *client)
 | 
			
		||||
{
 | 
			
		||||
	DEBUGLOG("(%p) Request timed-out. padding\n", client);
 | 
			
		||||
	DEBUGLOG("Request timed-out. padding\n");
 | 
			
		||||
	clops->cluster_do_node_callback(client, timedout_callback);
 | 
			
		||||
 | 
			
		||||
	if (!client->bits.localsock.threadid)
 | 
			
		||||
@@ -892,11 +850,13 @@ static void main_loop(int cmd_timeout)
 | 
			
		||||
	while (!quit) {
 | 
			
		||||
		fd_set in;
 | 
			
		||||
		int select_status;
 | 
			
		||||
		struct local_client *thisfd, *nextfd;
 | 
			
		||||
		struct local_client *thisfd;
 | 
			
		||||
		struct timeval tv = { cmd_timeout, 0 };
 | 
			
		||||
		int quorate = clops->is_quorate();
 | 
			
		||||
		int client_count = 0;
 | 
			
		||||
		int max_fd = 0;
 | 
			
		||||
		struct local_client *lastfd = &local_client_head;
 | 
			
		||||
		struct local_client *nextfd = local_client_head.next;
 | 
			
		||||
 | 
			
		||||
		/* Wait on the cluster FD and all local sockets/pipes */
 | 
			
		||||
		local_client_head.fd = clops->get_main_cluster_fd();
 | 
			
		||||
@@ -912,22 +872,21 @@ static void main_loop(int cmd_timeout)
 | 
			
		||||
 			fprintf(stderr, "WARNING: Your cluster may freeze up if the number of clvmd file descriptors (%d) exceeds %d.\n", max_fd + 1, FD_SETSIZE);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (thisfd = &local_client_head; thisfd; thisfd = nextfd) {
 | 
			
		||||
			nextfd = thisfd->next;
 | 
			
		||||
		for (thisfd = &local_client_head; thisfd; thisfd = nextfd, nextfd = thisfd ? thisfd->next : NULL) {
 | 
			
		||||
 | 
			
		||||
			if (thisfd->removeme && !cleanup_zombie(thisfd)) {
 | 
			
		||||
				/* cleanup_zombie might have removed the next list element */
 | 
			
		||||
				nextfd = thisfd->next;
 | 
			
		||||
 | 
			
		||||
				(void) _del_client(thisfd);
 | 
			
		||||
 | 
			
		||||
				DEBUGLOG("(%p) removeme set with %d monitored fds remaining\n", thisfd, _local_client_count);
 | 
			
		||||
				struct local_client *free_fd = thisfd;
 | 
			
		||||
				lastfd->next = nextfd;
 | 
			
		||||
				DEBUGLOG("removeme set for %p with %d monitored fds remaining\n", free_fd, client_count - 1);
 | 
			
		||||
 | 
			
		||||
				/* Queue cleanup, this also frees the client struct */
 | 
			
		||||
				add_to_lvmqueue(thisfd, NULL, 0, NULL);
 | 
			
		||||
				add_to_lvmqueue(free_fd, NULL, 0, NULL);
 | 
			
		||||
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			lastfd = thisfd;
 | 
			
		||||
 | 
			
		||||
			if (thisfd->removeme)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
@@ -977,15 +936,16 @@ static void main_loop(int cmd_timeout)
 | 
			
		||||
						    type == CLUSTER_INTERNAL)
 | 
			
		||||
							goto closedown;
 | 
			
		||||
 | 
			
		||||
						DEBUGLOG("(%p) ret == %d, errno = %d. removing client\n",
 | 
			
		||||
							 thisfd, ret, errno);
 | 
			
		||||
						DEBUGLOG("ret == %d, errno = %d. removing client\n",
 | 
			
		||||
							 ret, errno);
 | 
			
		||||
						thisfd->removeme = 1;
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					/* New client...simply add it to the list */
 | 
			
		||||
					if (newfd) {
 | 
			
		||||
						_add_client(newfd, thisfd);
 | 
			
		||||
						newfd->next = thisfd->next;
 | 
			
		||||
						thisfd->next = newfd;
 | 
			
		||||
						thisfd = newfd;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
@@ -1003,8 +963,8 @@ static void main_loop(int cmd_timeout)
 | 
			
		||||
				    thisfd->bits.localsock.expected_replies !=
 | 
			
		||||
				    thisfd->bits.localsock.num_replies) {
 | 
			
		||||
					/* Send timed out message + replies we already have */
 | 
			
		||||
					DEBUGLOG("Request to client %p timed-out (send: %ld, now: %ld)\n",
 | 
			
		||||
						 thisfd, thisfd->bits.localsock.sent_time, the_time);
 | 
			
		||||
					DEBUGLOG("Request timed-out (send: %ld, now: %ld)\n",
 | 
			
		||||
						 thisfd->bits.localsock.sent_time, the_time);
 | 
			
		||||
 | 
			
		||||
					thisfd->bits.localsock.all_success = 0;
 | 
			
		||||
 | 
			
		||||
@@ -1105,31 +1065,31 @@ static void be_daemon(int timeout)
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:       /* Parent */
 | 
			
		||||
		(void) close(devnull);
 | 
			
		||||
		(void) close(child_pipe[1]);
 | 
			
		||||
		wait_for_child(child_pipe[0], timeout); /* noreturn */
 | 
			
		||||
		wait_for_child(child_pipe[0], timeout);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Detach ourself from the calling environment */
 | 
			
		||||
	if ((dup2(devnull, STDIN_FILENO) == -1) ||
 | 
			
		||||
	    (dup2(devnull, STDOUT_FILENO) == -1) ||
 | 
			
		||||
	    (dup2(devnull, STDERR_FILENO) == -1)) {
 | 
			
		||||
	if (close(0) || close(1) || close(2)) {
 | 
			
		||||
		perror("Error closing terminal FDs");
 | 
			
		||||
		exit(4);
 | 
			
		||||
	}
 | 
			
		||||
	setsid();
 | 
			
		||||
 | 
			
		||||
	if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0
 | 
			
		||||
	    || dup2(devnull, 2) < 0) {
 | 
			
		||||
		perror("Error setting terminal FDs to /dev/null");
 | 
			
		||||
		log_error("Error setting terminal FDs to /dev/null: %m");
 | 
			
		||||
		exit(5);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((devnull > STDERR_FILENO) && close(devnull)) {
 | 
			
		||||
		log_sys_error("close", "/dev/null");
 | 
			
		||||
		exit(7);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (chdir("/")) {
 | 
			
		||||
		log_error("Error setting current directory to /: %m");
 | 
			
		||||
		exit(6);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setsid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int verify_message(char *buf, int len)
 | 
			
		||||
@@ -1216,8 +1176,8 @@ static int cleanup_zombie(struct local_client *thisfd)
 | 
			
		||||
	if (!thisfd->bits.localsock.cleanup_needed)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) EOF on local socket %d: inprogress=%d\n",
 | 
			
		||||
		 thisfd, thisfd->fd, thisfd->bits.localsock.in_progress);
 | 
			
		||||
	DEBUGLOG("EOF on local socket: inprogress=%d\n",
 | 
			
		||||
		 thisfd->bits.localsock.in_progress);
 | 
			
		||||
 | 
			
		||||
	if ((pipe_client = thisfd->bits.localsock.pipe_client))
 | 
			
		||||
		pipe_client = pipe_client->bits.pipe.client;
 | 
			
		||||
@@ -1239,7 +1199,7 @@ static int cleanup_zombie(struct local_client *thisfd)
 | 
			
		||||
 | 
			
		||||
	/* Kill the subthread & free resources */
 | 
			
		||||
	if (thisfd->bits.localsock.threadid) {
 | 
			
		||||
		DEBUGLOG("(%p) Waiting for pre&post thread\n", pipe_client);
 | 
			
		||||
		DEBUGLOG("Waiting for pre&post thread (%p)\n", pipe_client);
 | 
			
		||||
		pthread_mutex_lock(&thisfd->bits.localsock.mutex);
 | 
			
		||||
		thisfd->bits.localsock.state = PRE_COMMAND;
 | 
			
		||||
		thisfd->bits.localsock.finished = 1;
 | 
			
		||||
@@ -1250,22 +1210,26 @@ static int cleanup_zombie(struct local_client *thisfd)
 | 
			
		||||
					  (void **) &status)))
 | 
			
		||||
			log_sys_error("pthread_join", "");
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("(%p) Joined pre&post thread\n", pipe_client);
 | 
			
		||||
		DEBUGLOG("Joined pre&post thread\n");
 | 
			
		||||
 | 
			
		||||
		thisfd->bits.localsock.threadid = 0;
 | 
			
		||||
 | 
			
		||||
		/* Remove the pipe client */
 | 
			
		||||
		if (thisfd->bits.localsock.pipe_client) {
 | 
			
		||||
			struct local_client *delfd = thisfd->bits.localsock.pipe_client;
 | 
			
		||||
			struct local_client *delfd;
 | 
			
		||||
			struct local_client *lastfd;
 | 
			
		||||
 | 
			
		||||
			(void) close(delfd->fd);	/* Close pipe */
 | 
			
		||||
			(void) close(thisfd->bits.localsock.pipe_client->fd);	/* Close pipe */
 | 
			
		||||
			(void) close(thisfd->bits.localsock.pipe);
 | 
			
		||||
 | 
			
		||||
			/* Remove pipe client */
 | 
			
		||||
			if (!_del_client(delfd)) {
 | 
			
		||||
				dm_free(delfd);
 | 
			
		||||
				thisfd->bits.localsock.pipe_client = NULL;
 | 
			
		||||
			}
 | 
			
		||||
			for (lastfd = &local_client_head; (delfd = lastfd->next); lastfd = delfd)
 | 
			
		||||
				if (thisfd->bits.localsock.pipe_client == delfd) {
 | 
			
		||||
					thisfd->bits.localsock.pipe_client = NULL;
 | 
			
		||||
					lastfd->next = delfd->next;
 | 
			
		||||
					dm_free(delfd);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1296,7 +1260,7 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
	if (len == -1 && errno == EINTR)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) Read on local socket %d, len = %d\n", thisfd, thisfd->fd, len);
 | 
			
		||||
	DEBUGLOG("Read on local socket %d, len = %d\n", thisfd->fd, len);
 | 
			
		||||
 | 
			
		||||
	if (len && verify_message(buffer, len) < 0) {
 | 
			
		||||
		log_error("read_from_local_sock from %d len %d bad verify.",
 | 
			
		||||
@@ -1370,15 +1334,15 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
		char *argptr = inheader->node + strlen(inheader->node) + 1;
 | 
			
		||||
 | 
			
		||||
		while (missing_len > 0) {
 | 
			
		||||
			DEBUGLOG("(%p) got %d bytes, need another %d (total %d)\n",
 | 
			
		||||
				 thisfd, argslen, missing_len, inheader->arglen);
 | 
			
		||||
			DEBUGLOG("got %d bytes, need another %d (total %d)\n",
 | 
			
		||||
				 argslen, missing_len, inheader->arglen);
 | 
			
		||||
			len = read(thisfd->fd, argptr + argslen, missing_len);
 | 
			
		||||
			if (len == -1 && errno == EINTR)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (len <= 0) {
 | 
			
		||||
				/* EOF or error on socket */
 | 
			
		||||
				DEBUGLOG("(%p) EOF on local socket\n", thisfd);
 | 
			
		||||
				DEBUGLOG("EOF on local socket\n");
 | 
			
		||||
				dm_free(thisfd->bits.localsock.cmd);
 | 
			
		||||
				thisfd->bits.localsock.cmd = NULL;
 | 
			
		||||
				return 0;
 | 
			
		||||
@@ -1406,7 +1370,7 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
			.status = ENOENT
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("(%p) Unknown node: '%s'\n", thisfd, inheader->node);
 | 
			
		||||
		DEBUGLOG("Unknown node: '%s'\n", inheader->node);
 | 
			
		||||
		send_message(&reply, sizeof(reply), our_csid, thisfd->fd,
 | 
			
		||||
			     "Error sending ENOENT reply to local user");
 | 
			
		||||
		thisfd->bits.localsock.expected_replies = 0;
 | 
			
		||||
@@ -1432,7 +1396,7 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
			.status = EBUSY
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("(%p) Creating pipe failed: %s\n", thisfd, strerror(errno));
 | 
			
		||||
		DEBUGLOG("Creating pipe failed: %s\n", strerror(errno));
 | 
			
		||||
		send_message(&reply, sizeof(reply), our_csid, thisfd->fd,
 | 
			
		||||
			     "Error sending EBUSY reply to local user");
 | 
			
		||||
		return len;
 | 
			
		||||
@@ -1452,7 +1416,7 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
		return len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) Creating pipe, [%d, %d]\n", thisfd, comms_pipe[0], comms_pipe[1]);
 | 
			
		||||
	DEBUGLOG("Creating pipe, [%d, %d]\n", comms_pipe[0], comms_pipe[1]);
 | 
			
		||||
 | 
			
		||||
	if (fcntl(comms_pipe[0], F_SETFD, 1))
 | 
			
		||||
		DEBUGLOG("setting CLOEXEC on pipe[0] failed: %s\n", strerror(errno));
 | 
			
		||||
@@ -1463,8 +1427,8 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
	newfd->type = THREAD_PIPE;
 | 
			
		||||
	newfd->callback = local_pipe_callback;
 | 
			
		||||
	newfd->bits.pipe.client = thisfd;
 | 
			
		||||
 | 
			
		||||
	_add_client(newfd, thisfd);
 | 
			
		||||
	newfd->next = thisfd->next;
 | 
			
		||||
	thisfd->next = newfd;
 | 
			
		||||
 | 
			
		||||
	/* Store a cross link to the pipe */
 | 
			
		||||
	thisfd->bits.localsock.pipe_client = newfd;
 | 
			
		||||
@@ -1477,10 +1441,10 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
	thisfd->bits.localsock.in_progress = TRUE;
 | 
			
		||||
	thisfd->bits.localsock.state = PRE_COMMAND;
 | 
			
		||||
	thisfd->bits.localsock.cleanup_needed = 1;
 | 
			
		||||
	DEBUGLOG("(%p) Creating pre&post thread for pipe fd %d\n", newfd, newfd->fd);
 | 
			
		||||
	DEBUGLOG("Creating pre&post thread for pipe fd %d (%p)\n", newfd->fd, newfd);
 | 
			
		||||
	status = pthread_create(&thisfd->bits.localsock.threadid,
 | 
			
		||||
				&stack_attr, pre_and_post_thread, thisfd);
 | 
			
		||||
	DEBUGLOG("(%p) Created pre&post thread, state = %d\n", newfd, status);
 | 
			
		||||
	DEBUGLOG("Created pre&post thread, state = %d\n", status);
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
@@ -1488,6 +1452,13 @@ static int read_from_local_sock(struct local_client *thisfd)
 | 
			
		||||
/* Add a file descriptor from the cluster or comms interface to
 | 
			
		||||
   our list of FDs for select
 | 
			
		||||
*/
 | 
			
		||||
int add_client(struct local_client *new_client)
 | 
			
		||||
{
 | 
			
		||||
	new_client->next = local_client_head.next;
 | 
			
		||||
	local_client_head.next = new_client;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called when the pre-command has completed successfully - we
 | 
			
		||||
   now execute the real command on all the requested nodes */
 | 
			
		||||
@@ -1498,8 +1469,8 @@ static int distribute_command(struct local_client *thisfd)
 | 
			
		||||
	int len = thisfd->bits.localsock.cmd_len;
 | 
			
		||||
 | 
			
		||||
	thisfd->xid = global_xid++;
 | 
			
		||||
	DEBUGLOG("(%p) distribute command: XID = %d, flags=0x%x (%s%s)\n",
 | 
			
		||||
		 thisfd, thisfd->xid, inheader->flags,
 | 
			
		||||
	DEBUGLOG("distribute command: XID = %d, flags=0x%x (%s%s)\n",
 | 
			
		||||
		 thisfd->xid, inheader->flags,
 | 
			
		||||
		(inheader->flags & CLVMD_FLAG_LOCAL) ? "LOCAL" : "",
 | 
			
		||||
		(inheader->flags & CLVMD_FLAG_REMOTE) ? "REMOTE" : "");
 | 
			
		||||
 | 
			
		||||
@@ -1521,7 +1492,7 @@ static int distribute_command(struct local_client *thisfd)
 | 
			
		||||
			 */
 | 
			
		||||
			add_to_lvmqueue(thisfd, inheader, len, NULL);
 | 
			
		||||
 | 
			
		||||
			DEBUGLOG("(%p) Sending message to all cluster nodes\n", thisfd);
 | 
			
		||||
			DEBUGLOG("Sending message to all cluster nodes\n");
 | 
			
		||||
			inheader->xid = thisfd->xid;
 | 
			
		||||
			send_message(inheader, len, NULL, -1,
 | 
			
		||||
				     "Error forwarding message to cluster");
 | 
			
		||||
@@ -1540,11 +1511,11 @@ static int distribute_command(struct local_client *thisfd)
 | 
			
		||||
 | 
			
		||||
			/* Are we the requested node ?? */
 | 
			
		||||
			if (memcmp(csid, our_csid, max_csid_len) == 0) {
 | 
			
		||||
				DEBUGLOG("(%p) Doing command on local node only\n", thisfd);
 | 
			
		||||
				DEBUGLOG("Doing command on local node only\n");
 | 
			
		||||
				add_to_lvmqueue(thisfd, inheader, len, NULL);
 | 
			
		||||
			} else {
 | 
			
		||||
				DEBUGLOG("(%p) Sending message to single node: %s\n",
 | 
			
		||||
					 thisfd, inheader->node);
 | 
			
		||||
				DEBUGLOG("Sending message to single node: %s\n",
 | 
			
		||||
					 inheader->node);
 | 
			
		||||
				inheader->xid = thisfd->xid;
 | 
			
		||||
				send_message(inheader, len, csid, -1,
 | 
			
		||||
					     "Error forwarding message to cluster node");
 | 
			
		||||
@@ -1555,7 +1526,7 @@ static int distribute_command(struct local_client *thisfd)
 | 
			
		||||
		thisfd->bits.localsock.in_progress = TRUE;
 | 
			
		||||
		thisfd->bits.localsock.expected_replies = 1;
 | 
			
		||||
		thisfd->bits.localsock.num_replies = 0;
 | 
			
		||||
		DEBUGLOG("(%p) Doing command explicitly on local node only\n", thisfd);
 | 
			
		||||
		DEBUGLOG("Doing command explicitly on local node only\n");
 | 
			
		||||
		add_to_lvmqueue(thisfd, inheader, len, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1681,7 +1652,7 @@ static void add_reply_to_list(struct local_client *client, int status,
 | 
			
		||||
 | 
			
		||||
	reply->status = status;
 | 
			
		||||
	clops->name_from_csid(csid, reply->node);
 | 
			
		||||
	DEBUGLOG("(%p) Reply from node %s: %d bytes\n", client, reply->node, len);
 | 
			
		||||
	DEBUGLOG("Reply from node %s: %d bytes\n", reply->node, len);
 | 
			
		||||
 | 
			
		||||
	if (len > 0) {
 | 
			
		||||
		if (!(reply->replymsg = dm_malloc(len)))
 | 
			
		||||
@@ -1708,8 +1679,8 @@ static void add_reply_to_list(struct local_client *client, int status,
 | 
			
		||||
			client->bits.localsock.state = POST_COMMAND;
 | 
			
		||||
			pthread_cond_signal(&client->bits.localsock.cond);
 | 
			
		||||
		}
 | 
			
		||||
		DEBUGLOG("(%p) Got %d replies, expecting: %d\n",
 | 
			
		||||
			 client, client->bits.localsock.num_replies,
 | 
			
		||||
		DEBUGLOG("Got %d replies, expecting: %d\n",
 | 
			
		||||
			 client->bits.localsock.num_replies,
 | 
			
		||||
			 client->bits.localsock.expected_replies);
 | 
			
		||||
	}
 | 
			
		||||
	pthread_mutex_unlock(&client->bits.localsock.mutex);
 | 
			
		||||
@@ -1724,7 +1695,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
 | 
			
		||||
	sigset_t ss;
 | 
			
		||||
	int pipe_fd = client->bits.localsock.pipe;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) Pre&post thread pipe fd %d\n", client, pipe_fd);
 | 
			
		||||
	DEBUGLOG("Pre&post thread (%p), pipe fd %d\n", client, pipe_fd);
 | 
			
		||||
	pthread_mutex_lock(&client->bits.localsock.mutex);
 | 
			
		||||
 | 
			
		||||
	/* Ignore SIGUSR1 (handled by master process) but enable
 | 
			
		||||
@@ -1744,7 +1715,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
 | 
			
		||||
		if ((status = do_pre_command(client)))
 | 
			
		||||
			client->bits.localsock.all_success = 0;
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("(%p) Pre&post thread writes status %d down to pipe fd %d\n",
 | 
			
		||||
		DEBUGLOG("Pre&post thread (%p) writes status %d down to pipe fd %d\n",
 | 
			
		||||
			 client, status, pipe_fd);
 | 
			
		||||
 | 
			
		||||
		/* Tell the parent process we have finished this bit */
 | 
			
		||||
@@ -1762,13 +1733,13 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
 | 
			
		||||
		/* We may need to wait for the condition variable before running the post command */
 | 
			
		||||
		if (client->bits.localsock.state != POST_COMMAND &&
 | 
			
		||||
		    !client->bits.localsock.finished) {
 | 
			
		||||
			DEBUGLOG("(%p) Pre&post thread waiting to do post command, state = %d\n",
 | 
			
		||||
			DEBUGLOG("Pre&post thread (%p) waiting to do post command, state = %d\n",
 | 
			
		||||
				 client, client->bits.localsock.state);
 | 
			
		||||
			pthread_cond_wait(&client->bits.localsock.cond,
 | 
			
		||||
					  &client->bits.localsock.mutex);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("(%p) Pre&post thread got post command condition...\n", client);
 | 
			
		||||
		DEBUGLOG("Pre&post thread (%p) got post command condition...\n", client);
 | 
			
		||||
 | 
			
		||||
		/* POST function must always run, even if the client aborts */
 | 
			
		||||
		status = 0;
 | 
			
		||||
@@ -1782,15 +1753,15 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
 | 
			
		||||
next_pre:
 | 
			
		||||
		if (client->bits.localsock.state != PRE_COMMAND &&
 | 
			
		||||
		    !client->bits.localsock.finished) {
 | 
			
		||||
			DEBUGLOG("(%p) Pre&post thread waiting for next pre command\n", client);
 | 
			
		||||
			DEBUGLOG("Pre&post thread (%p) waiting for next pre command\n", client);
 | 
			
		||||
			pthread_cond_wait(&client->bits.localsock.cond,
 | 
			
		||||
					  &client->bits.localsock.mutex);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DEBUGLOG("(%p) Pre&post thread got pre command condition...\n", client);
 | 
			
		||||
		DEBUGLOG("Pre&post thread (%p) got pre command condition...\n", client);
 | 
			
		||||
	}
 | 
			
		||||
	pthread_mutex_unlock(&client->bits.localsock.mutex);
 | 
			
		||||
	DEBUGLOG("(%p) Pre&post thread finished\n", client);
 | 
			
		||||
	DEBUGLOG("Pre&post thread (%p) finished\n", client);
 | 
			
		||||
 | 
			
		||||
	pthread_exit(NULL);
 | 
			
		||||
}
 | 
			
		||||
@@ -1808,8 +1779,8 @@ static int process_local_command(struct clvm_header *msg, int msglen,
 | 
			
		||||
	if (!(replybuf = dm_malloc(max_cluster_message)))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) process_local_command: %s msg=%p, msglen =%d\n",
 | 
			
		||||
		 client, decode_cmd(msg->cmd), msg, msglen);
 | 
			
		||||
	DEBUGLOG("process_local_command: %s msg=%p, msglen =%d, client=%p\n",
 | 
			
		||||
		 decode_cmd(msg->cmd), msg, msglen, client);
 | 
			
		||||
 | 
			
		||||
	/* If remote flag is set, just set a successful status code. */
 | 
			
		||||
	if (msg->flags & CLVMD_FLAG_REMOTE)
 | 
			
		||||
@@ -1824,8 +1795,8 @@ static int process_local_command(struct clvm_header *msg, int msglen,
 | 
			
		||||
	if (xid == client->xid)
 | 
			
		||||
		add_reply_to_list(client, status, our_csid, replybuf, replylen);
 | 
			
		||||
	else
 | 
			
		||||
		DEBUGLOG("(%p) Local command took too long, discarding xid %d, current is %d\n",
 | 
			
		||||
			 client, xid, client->xid);
 | 
			
		||||
		DEBUGLOG("Local command took too long, discarding xid %d, current is %d\n",
 | 
			
		||||
			 xid, client->xid);
 | 
			
		||||
 | 
			
		||||
	dm_free(replybuf);
 | 
			
		||||
 | 
			
		||||
@@ -1867,7 +1838,7 @@ static void send_local_reply(struct local_client *client, int status, int fd)
 | 
			
		||||
	char *ptr;
 | 
			
		||||
	int message_len = 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) Send local reply\n", client);
 | 
			
		||||
	DEBUGLOG("Send local reply\n");
 | 
			
		||||
 | 
			
		||||
	/* Work out the total size of the reply */
 | 
			
		||||
	while (thisreply) {
 | 
			
		||||
@@ -1884,7 +1855,7 @@ static void send_local_reply(struct local_client *client, int status, int fd)
 | 
			
		||||
	/* Add in the size of our header */
 | 
			
		||||
	message_len = message_len + sizeof(struct clvm_header);
 | 
			
		||||
	if (!(replybuf = dm_malloc(message_len))) {
 | 
			
		||||
		DEBUGLOG("(%p) Memory allocation fails\n", client);
 | 
			
		||||
		DEBUGLOG("Memory allocation fails\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1999,9 +1970,6 @@ static int send_message(void *buf, int msglen, const char *csid, int fd,
 | 
			
		||||
		return clops->cluster_send_message(buf, msglen, csid, errtext);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fd < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Make sure it all goes */
 | 
			
		||||
	for (ptr = 0; ptr < msglen;) {
 | 
			
		||||
		if ((len = write(fd, (char*)buf + ptr, msglen - ptr)) <= 0) {
 | 
			
		||||
@@ -2016,7 +1984,6 @@ static int send_message(void *buf, int msglen, const char *csid, int fd,
 | 
			
		||||
				(void) nanosleep (&delay, &remtime);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			DEBUGLOG("%s", errtext);
 | 
			
		||||
			log_error("%s", errtext);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
@@ -2030,7 +1997,7 @@ static int process_work_item(struct lvm_thread_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	/* If msg is NULL then this is a cleanup request */
 | 
			
		||||
	if (cmd->msg == NULL) {
 | 
			
		||||
		DEBUGLOG("(%p) process_work_item: free\n", cmd->client);
 | 
			
		||||
		DEBUGLOG("process_work_item: free %p\n", cmd->client);
 | 
			
		||||
		cmd_client_cleanup(cmd->client);
 | 
			
		||||
		pthread_mutex_destroy(&cmd->client->bits.localsock.mutex);
 | 
			
		||||
		pthread_cond_destroy(&cmd->client->bits.localsock.cond);
 | 
			
		||||
@@ -2039,11 +2006,11 @@ static int process_work_item(struct lvm_thread_cmd *cmd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!cmd->remote) {
 | 
			
		||||
		DEBUGLOG("(%p) process_work_item: local\n", cmd->client);
 | 
			
		||||
		DEBUGLOG("process_work_item: local\n");
 | 
			
		||||
		process_local_command(cmd->msg, cmd->msglen, cmd->client,
 | 
			
		||||
				      cmd->xid);
 | 
			
		||||
	} else {
 | 
			
		||||
		DEBUGLOG("(%p) process_work_item: remote\n", cmd->client);
 | 
			
		||||
		DEBUGLOG("process_work_item: remote\n");
 | 
			
		||||
		process_remote_command(cmd->msg, cmd->msglen, cmd->client->fd,
 | 
			
		||||
				       cmd->csid);
 | 
			
		||||
	}
 | 
			
		||||
@@ -2137,8 +2104,8 @@ static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
 | 
			
		||||
	} else
 | 
			
		||||
		cmd->remote = 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("(%p) add_to_lvmqueue: cmd=%p, msg=%p, len=%d, csid=%p, xid=%d\n",
 | 
			
		||||
		 client, cmd, msg, msglen, csid, cmd->xid);
 | 
			
		||||
	DEBUGLOG("add_to_lvmqueue: cmd=%p. client=%p, msg=%p, len=%d, csid=%p, xid=%d\n",
 | 
			
		||||
		 cmd, client, msg, msglen, csid, cmd->xid);
 | 
			
		||||
	pthread_mutex_lock(&lvm_thread_mutex);
 | 
			
		||||
	if (lvm_thread_exit) {
 | 
			
		||||
		pthread_mutex_unlock(&lvm_thread_mutex);
 | 
			
		||||
@@ -2154,14 +2121,6 @@ static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return 0 if we can talk to an existing clvmd */
 | 
			
		||||
/*
 | 
			
		||||
 * FIXME:
 | 
			
		||||
 *
 | 
			
		||||
 * This function returns only -1 or 0, but there are
 | 
			
		||||
 * different levels of errors, some of them should stop
 | 
			
		||||
 * further execution of clvmd thus another state is needed
 | 
			
		||||
 * and some error message need to be only informational.
 | 
			
		||||
 */
 | 
			
		||||
static int check_local_clvmd(void)
 | 
			
		||||
{
 | 
			
		||||
	int local_socket;
 | 
			
		||||
@@ -2181,11 +2140,7 @@ static int check_local_clvmd(void)
 | 
			
		||||
 | 
			
		||||
	if (connect(local_socket,(struct sockaddr *) &sockaddr,
 | 
			
		||||
		    sizeof(sockaddr))) {
 | 
			
		||||
		/* connection failure is expected state */
 | 
			
		||||
		if (errno == ENOENT)
 | 
			
		||||
			log_sys_debug("connect", "local socket");
 | 
			
		||||
		else
 | 
			
		||||
			log_sys_error("connect", "local socket");
 | 
			
		||||
		log_sys_error("connect", "local socket");
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -2286,8 +2241,7 @@ static void check_all_callback(struct local_client *client, const char *csid,
 | 
			
		||||
   If not, returns -1 and prints out a list of errant nodes */
 | 
			
		||||
static int check_all_clvmds_running(struct local_client *client)
 | 
			
		||||
{
 | 
			
		||||
	DEBUGLOG("(%p) check_all_clvmds_running\n", client);
 | 
			
		||||
 | 
			
		||||
	DEBUGLOG("check_all_clvmds_running\n");
 | 
			
		||||
	return clops->cluster_do_node_callback(client, check_all_callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2326,11 +2280,13 @@ static void ntoh_clvm(struct clvm_header *hdr)
 | 
			
		||||
static void sigusr2_handler(int sig)
 | 
			
		||||
{
 | 
			
		||||
	DEBUGLOG("SIGUSR2 received\n");
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sigterm_handler(int sig)
 | 
			
		||||
{
 | 
			
		||||
	quit = 1;
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sighup_handler(int sig)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _CLVMD_H
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "clvmd-common.h"
 | 
			
		||||
@@ -291,7 +291,6 @@ static int hold_lock(char *resource, int mode, int flags)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lvi->lock_mode = mode;
 | 
			
		||||
		lvi->lock_id = 0;
 | 
			
		||||
		status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id);
 | 
			
		||||
		saved_errno = errno;
 | 
			
		||||
		if (status) {
 | 
			
		||||
@@ -639,6 +638,16 @@ int post_lock_lv(unsigned char command, unsigned char lock_flags,
 | 
			
		||||
	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");
 | 
			
		||||
@@ -651,9 +660,9 @@ int do_refresh_cache(void)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	init_full_scan_done(0);
 | 
			
		||||
	init_ignore_suspended_devices(1);
 | 
			
		||||
	lvmcache_label_scan(cmd);
 | 
			
		||||
	label_scan_destroy(cmd); /* destroys bcache (to close devs), keeps lvmcache */
 | 
			
		||||
	lvmcache_label_scan(cmd, 2);
 | 
			
		||||
	dm_pool_empty(cmd->mem);
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&lvm_lock);
 | 
			
		||||
@@ -796,7 +805,8 @@ static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
 | 
			
		||||
	if (level != _LOG_ERR && level != _LOG_FATAL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	(void) dm_strncpy(last_error, message, sizeof(last_error));
 | 
			
		||||
	strncpy(last_error, message, sizeof(last_error));
 | 
			
		||||
	last_error[sizeof(last_error)-1] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This checks some basic cluster-LVM configuration stuff */
 | 
			
		||||
@@ -832,7 +842,7 @@ void lvm_do_backup(const char *vgname)
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&lvm_lock);
 | 
			
		||||
 | 
			
		||||
	vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, 0, 0, WARN_PV_READ, &consistent);
 | 
			
		||||
	vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, WARN_PV_READ, &consistent);
 | 
			
		||||
 | 
			
		||||
	if (vg && consistent)
 | 
			
		||||
		check_current_backup(vg);
 | 
			
		||||
@@ -889,12 +899,8 @@ int init_clvm(struct dm_hash_table *excl_uuid)
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -921,7 +927,6 @@ void destroy_lvm(void)
 | 
			
		||||
	if (cmd) {
 | 
			
		||||
		memlock_dec_daemon(cmd);
 | 
			
		||||
		destroy_toolcontext(cmd);
 | 
			
		||||
		udev_fin_library_context();
 | 
			
		||||
		cmd = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Functions in lvm-functions.c */
 | 
			
		||||
@@ -25,6 +25,7 @@ extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* FIXME Remove duplicated functions from this file. */
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
@@ -29,7 +29,7 @@ include $(top_builddir)/make.tmpl
 | 
			
		||||
LIBS += -ldevmapper
 | 
			
		||||
LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
 | 
			
		||||
CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
 | 
			
		||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS)
 | 
			
		||||
 | 
			
		||||
cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "cluster.h"
 | 
			
		||||
@@ -166,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;
 | 
			
		||||
 | 
			
		||||
@@ -185,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;
 | 
			
		||||
@@ -206,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;
 | 
			
		||||
@@ -1441,7 +1440,7 @@ static void cpg_leave_callback(struct clog_cpg *match,
 | 
			
		||||
			free(rq);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for (i = 0, j = 0; (int) i < match->checkpoints_needed; i++, j++) {
 | 
			
		||||
	for (i = 0, j = 0; i < match->checkpoints_needed; i++, j++) {
 | 
			
		||||
		match->checkpoint_requesters[j] = match->checkpoint_requesters[i];
 | 
			
		||||
		if (match->checkpoint_requesters[i] == left->nodeid) {
 | 
			
		||||
			LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)",
 | 
			
		||||
@@ -1631,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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LVM_CLOG_CLUSTER_H
 | 
			
		||||
#define _LVM_CLOG_CLUSTER_H
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LVM_CLOG_COMMON_H
 | 
			
		||||
#define _LVM_CLOG_COMMON_H
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,11 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "functions.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/sysmacros.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
@@ -377,7 +376,7 @@ static int _clog_ctr(char *uuid, uint64_t luid,
 | 
			
		||||
	uint32_t block_on_error = 0;
 | 
			
		||||
 | 
			
		||||
	int disk_log;
 | 
			
		||||
	char disk_path[PATH_MAX];
 | 
			
		||||
	char disk_path[128];
 | 
			
		||||
	int unlink_path = 0;
 | 
			
		||||
	long page_size;
 | 
			
		||||
	int pages;
 | 
			
		||||
@@ -451,19 +450,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);
 | 
			
		||||
@@ -578,12 +573,6 @@ static int clog_ctr(struct dm_ulog_request *rq)
 | 
			
		||||
	for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++)
 | 
			
		||||
		*p = '\0';
 | 
			
		||||
 | 
			
		||||
	if (!argc) {
 | 
			
		||||
		LOG_ERROR("Received constructor request with bad data %s",
 | 
			
		||||
			  rq->data);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argv = malloc(argc * sizeof(char *));
 | 
			
		||||
	if (!argv)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
@@ -1456,7 +1445,7 @@ static int disk_status_info(struct log_c *lc, struct dm_ulog_request *rq)
 | 
			
		||||
	char *data = (char *)rq->data;
 | 
			
		||||
	struct stat statbuf;
 | 
			
		||||
 | 
			
		||||
	if (fstat(lc->disk_fd, &statbuf)) {
 | 
			
		||||
	if(fstat(lc->disk_fd, &statbuf)) {
 | 
			
		||||
		rq->error = -errno;
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1519,7 +1508,7 @@ static int disk_status_table(struct log_c *lc, struct dm_ulog_request *rq)
 | 
			
		||||
	char *data = (char *)rq->data;
 | 
			
		||||
	struct stat statbuf;
 | 
			
		||||
 | 
			
		||||
	if (fstat(lc->disk_fd, &statbuf)) {
 | 
			
		||||
	if(fstat(lc->disk_fd, &statbuf)) {
 | 
			
		||||
		rq->error = -errno;
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LVM_CLOG_FUNCTIONS_H
 | 
			
		||||
#define _LVM_CLOG_FUNCTIONS_H
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "link_mon.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LVM_CLOG_LINK_MON_H
 | 
			
		||||
#define _LVM_CLOG_LINK_MON_H
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LVM_CLOG_LOCAL_H
 | 
			
		||||
#define _LVM_CLOG_LOCAL_H
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
#include "logging.h"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,14 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _LVM_CLOG_LOGGING_H
 | 
			
		||||
#define _LVM_CLOG_LOGGING_H
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#define _FILE_OFFSET_BITS 64
 | 
			
		||||
 | 
			
		||||
#include "configure.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# You should 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
@@ -56,16 +56,18 @@ include $(top_builddir)/make.tmpl
 | 
			
		||||
all: device-mapper
 | 
			
		||||
device-mapper: $(TARGETS)
 | 
			
		||||
 | 
			
		||||
LIBS += -ldevmapper
 | 
			
		||||
LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
 | 
			
		||||
 | 
			
		||||
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
LIBS += -ldevmapper $(PTHREAD_LIBS)
 | 
			
		||||
 | 
			
		||||
dmeventd: $(LIB_SHARED) dmeventd.o
 | 
			
		||||
	$(CC) $(CFLAGS) -L. $(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 $(interfacebuilddir)/libdevmapper.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) dmeventd.o \
 | 
			
		||||
		-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS)
 | 
			
		||||
	$(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
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __DMEVENTD_DOT_H__
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the device-mapper userspace tools.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,12 +9,12 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "dm-logging.h"
 | 
			
		||||
#include "dmlib.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
//#include "libmultilog.h"
 | 
			
		||||
#include "dmeventd.h"
 | 
			
		||||
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
@@ -23,11 +23,7 @@
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <arpa/inet.h>		/* for htonl, ntohl */
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <syslog.h>
 | 
			
		||||
 | 
			
		||||
static int _debug_level = 0;
 | 
			
		||||
static int _use_syslog = 0;
 | 
			
		||||
static int _sequence_nr = 0;
 | 
			
		||||
 | 
			
		||||
struct dm_event_handler {
 | 
			
		||||
@@ -198,7 +194,7 @@ static int _check_message_id(struct dm_event_daemon_message *msg)
 | 
			
		||||
	if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
 | 
			
		||||
	    (pid != getpid()) || (seq_nr != _sequence_nr)) {
 | 
			
		||||
		log_error("Ignoring out-of-sequence reply from dmeventd. "
 | 
			
		||||
			  "Expected %d:%d but received %s.", getpid(),
 | 
			
		||||
			  "Expected %d:%d but received %s", getpid(),
 | 
			
		||||
			  _sequence_nr, msg->data);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
@@ -233,7 +229,7 @@ static int _daemon_read(struct dm_event_fifos *fifos,
 | 
			
		||||
			FD_SET(fifos->server, &fds);
 | 
			
		||||
			ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
 | 
			
		||||
			if (ret < 0 && errno != EINTR) {
 | 
			
		||||
				log_error("Unable to read from event server.");
 | 
			
		||||
				log_error("Unable to read from event server");
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
			if ((ret == 0) && (i > 4) && !bytes) {
 | 
			
		||||
@@ -250,9 +246,10 @@ static int _daemon_read(struct dm_event_fifos *fifos,
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			if ((errno == EINTR) || (errno == EAGAIN))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			log_error("Unable to read from event server.");
 | 
			
		||||
			return 0;
 | 
			
		||||
			else {
 | 
			
		||||
				log_error("Unable to read from event server.");
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bytes += ret;
 | 
			
		||||
@@ -298,7 +295,7 @@ static int _daemon_write(struct dm_event_fifos *fifos,
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			if (errno == EINTR)
 | 
			
		||||
				continue;
 | 
			
		||||
			log_error("Unable to talk to event daemon.");
 | 
			
		||||
			log_error("Unable to talk to event daemon");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		if (ret == 0)
 | 
			
		||||
@@ -307,7 +304,7 @@ 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.");
 | 
			
		||||
			log_error("Unable to talk to event daemon");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -319,7 +316,7 @@ static int _daemon_write(struct dm_event_fifos *fifos,
 | 
			
		||||
			FD_SET(fifos->client, &fds);
 | 
			
		||||
			ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
 | 
			
		||||
			if ((ret < 0) && (errno != EINTR)) {
 | 
			
		||||
				log_error("Unable to talk to event daemon.");
 | 
			
		||||
				log_error("Unable to talk to event daemon");
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		} while (ret < 1);
 | 
			
		||||
@@ -328,9 +325,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;
 | 
			
		||||
@@ -358,7 +356,7 @@ int daemon_talk(struct dm_event_fifos *fifos,
 | 
			
		||||
			  getpid(), _sequence_nr,
 | 
			
		||||
			  dso_name ? : "-", dev_name ? : "-", evmask, timeout)))
 | 
			
		||||
	    < 0) {
 | 
			
		||||
		log_error("_daemon_talk: message allocation failed.");
 | 
			
		||||
		log_error("_daemon_talk: message allocation failed");
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
	msg->cmd = cmd;
 | 
			
		||||
@@ -410,56 +408,28 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
 | 
			
		||||
	char default_dmeventd_path[] = DMEVENTD_PATH;
 | 
			
		||||
	char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL };
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * FIXME Explicitly verify the code's requirement that client_path is secure:
 | 
			
		||||
	 * - All parent directories owned by root without group/other write access unless sticky.
 | 
			
		||||
	 */
 | 
			
		||||
	if (stat(fifos->client_path, &statbuf))
 | 
			
		||||
		goto start_server;
 | 
			
		||||
 | 
			
		||||
	/* If client fifo path exists, only use it if it is root-owned fifo mode 0600 */
 | 
			
		||||
	if ((lstat(fifos->client_path, &statbuf) < 0)) {
 | 
			
		||||
		if (errno == ENOENT)
 | 
			
		||||
			/* Jump ahead if fifo does not already exist. */
 | 
			
		||||
			goto start_server;
 | 
			
		||||
		else {
 | 
			
		||||
			log_sys_error("stat", fifos->client_path);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (!S_ISFIFO(statbuf.st_mode)) {
 | 
			
		||||
		log_error("%s must be a fifo.", fifos->client_path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (statbuf.st_uid) {
 | 
			
		||||
		log_error("%s must be owned by uid 0.", fifos->client_path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (statbuf.st_mode & (S_IEXEC | S_IRWXG | S_IRWXO)) {
 | 
			
		||||
		log_error("%s must have mode 0600.", fifos->client_path);
 | 
			
		||||
	if (!S_ISFIFO(statbuf.st_mode)) {
 | 
			
		||||
		log_error("%s is not a fifo.", fifos->client_path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Anyone listening?  If not, errno will be ENXIO */
 | 
			
		||||
	fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
 | 
			
		||||
	if (fifos->client >= 0) {
 | 
			
		||||
		/* Should never happen if all the above checks passed. */
 | 
			
		||||
		if ((fstat(fifos->client, &statbuf) < 0) ||
 | 
			
		||||
		    !S_ISFIFO(statbuf.st_mode) || statbuf.st_uid ||
 | 
			
		||||
		    (statbuf.st_mode & (S_IEXEC | S_IRWXG | S_IRWXO))) {
 | 
			
		||||
			log_error("%s is no longer a secure root-owned fifo with mode 0600.", fifos->client_path);
 | 
			
		||||
			if (close(fifos->client))
 | 
			
		||||
				log_sys_debug("close", fifos->client_path);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* server is running and listening */
 | 
			
		||||
		if (close(fifos->client))
 | 
			
		||||
			log_sys_debug("close", fifos->client_path);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (errno != ENXIO && errno != ENOENT)  {
 | 
			
		||||
	} else if (errno != ENXIO) {
 | 
			
		||||
		/* problem */
 | 
			
		||||
		log_sys_error("open", fifos->client_path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
start_server:
 | 
			
		||||
      start_server:
 | 
			
		||||
	/* server is not running */
 | 
			
		||||
 | 
			
		||||
	if ((args[0][0] == '/') && stat(args[0], &statbuf)) {
 | 
			
		||||
@@ -474,11 +444,11 @@ start_server:
 | 
			
		||||
 | 
			
		||||
	else if (!pid) {
 | 
			
		||||
		execvp(args[0], args);
 | 
			
		||||
		log_error("Unable to exec dmeventd: %s.", strerror(errno));
 | 
			
		||||
		log_error("Unable to exec dmeventd: %s", strerror(errno));
 | 
			
		||||
		_exit(EXIT_FAILURE);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (waitpid(pid, &status, 0) < 0)
 | 
			
		||||
			log_error("Unable to start dmeventd: %s.",
 | 
			
		||||
			log_error("Unable to start dmeventd: %s",
 | 
			
		||||
				  strerror(errno));
 | 
			
		||||
		else if (WEXITSTATUS(status))
 | 
			
		||||
			log_error("Unable to start dmeventd.");
 | 
			
		||||
@@ -551,7 +521,7 @@ static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
 | 
			
		||||
	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
 | 
			
		||||
		log_error("_get_device_info: dm_task creation for info failed.");
 | 
			
		||||
		log_error("_get_device_info: dm_task creation for info failed");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -569,17 +539,17 @@ static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
 | 
			
		||||
 | 
			
		||||
	/* FIXME Add name or uuid or devno to messages */
 | 
			
		||||
	if (!dm_task_run(dmt)) {
 | 
			
		||||
		log_error("_get_device_info: dm_task_run() failed.");
 | 
			
		||||
		log_error("_get_device_info: dm_task_run() failed");
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_get_info(dmt, &info)) {
 | 
			
		||||
		log_error("_get_device_info: failed to get info for device.");
 | 
			
		||||
		log_error("_get_device_info: failed to get info for device");
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!info.exists) {
 | 
			
		||||
		log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found.",
 | 
			
		||||
		log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found",
 | 
			
		||||
			  dmevh->uuid ? : "",
 | 
			
		||||
			  (!dmevh->uuid && dmevh->dev_name) ? dmevh->dev_name : "",
 | 
			
		||||
			  (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? "(" : "",
 | 
			
		||||
@@ -613,8 +583,8 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (!_init_client(dmeventd_path, &fifos)) {
 | 
			
		||||
		ret = -ESRCH;
 | 
			
		||||
		goto_out;
 | 
			
		||||
		stack;
 | 
			
		||||
		return -ESRCH;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
 | 
			
		||||
@@ -624,7 +594,7 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag
 | 
			
		||||
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
 | 
			
		||||
out:
 | 
			
		||||
 | 
			
		||||
	/* what is the opposite of init? */
 | 
			
		||||
	fini_fifos(&fifos);
 | 
			
		||||
 | 
			
		||||
@@ -645,16 +615,15 @@ 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"))
 | 
			
		||||
		log_warn("WARNING: %s: dmeventd plugins are deprecated.", dmevh->dso);
 | 
			
		||||
		log_warn("WARNING: %s: dmeventd plugins are deprecated", dmevh->dso);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
 | 
			
		||||
			     dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
 | 
			
		||||
		log_error("%s: event registration failed: %s.",
 | 
			
		||||
		log_error("%s: event registration failed: %s",
 | 
			
		||||
			  dm_task_get_name(dmt),
 | 
			
		||||
			  msg.data ? msg.data : strerror(-err));
 | 
			
		||||
		ret = 0;
 | 
			
		||||
@@ -681,7 +650,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
 | 
			
		||||
 | 
			
		||||
	if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
 | 
			
		||||
			    dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
 | 
			
		||||
		log_error("%s: event deregistration failed: %s.",
 | 
			
		||||
		log_error("%s: event deregistration failed: %s",
 | 
			
		||||
			  dm_task_get_name(dmt),
 | 
			
		||||
			  msg.data ? msg.data : strerror(-err));
 | 
			
		||||
		ret = 0;
 | 
			
		||||
@@ -754,11 +723,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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -854,99 +823,6 @@ int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dm_event_log_set(int debug_log_level, int use_syslog)
 | 
			
		||||
{
 | 
			
		||||
	_debug_level = debug_log_level;
 | 
			
		||||
	_use_syslog = use_syslog;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	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)) {
 | 
			
		||||
	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)
 | 
			
		||||
			return;
 | 
			
		||||
		prio = LOG_INFO;
 | 
			
		||||
		indent = "    ";
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_NOTICE:
 | 
			
		||||
		if (log_with_debug && _debug_level < 1)
 | 
			
		||||
			return;
 | 
			
		||||
		prio = LOG_NOTICE;
 | 
			
		||||
		indent = "  ";
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_WARN:
 | 
			
		||||
		prio = LOG_WARNING;
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_ERR:
 | 
			
		||||
		prio = LOG_ERR;
 | 
			
		||||
		stream = stderr;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		prio = LOG_CRIT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Serialize to keep lines readable */
 | 
			
		||||
	pthread_mutex_lock(&_log_mutex);
 | 
			
		||||
 | 
			
		||||
	if (_use_syslog) {
 | 
			
		||||
		vsyslog(prio, format, ap);
 | 
			
		||||
	} else {
 | 
			
		||||
		now = time(NULL);
 | 
			
		||||
		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);
 | 
			
		||||
		if (_debug_level > 3)
 | 
			
		||||
			fprintf(stream, "%28s:%4d %s", file, line, indent);
 | 
			
		||||
		vfprintf(stream, _(format), ap);
 | 
			
		||||
		fputc('\n', stream);
 | 
			
		||||
		fflush(stream);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 */
 | 
			
		||||
 | 
			
		||||
static char *_skip_string(char *src, const int delimiter)
 | 
			
		||||
@@ -980,7 +856,7 @@ int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
 | 
			
		||||
			     0, 0))) {
 | 
			
		||||
		char *p = _skip_string(msg.data, ' ');
 | 
			
		||||
		if (!p) {
 | 
			
		||||
			log_error("Malformed reply from dmeventd '%s'.",
 | 
			
		||||
			log_error("malformed reply from dmeventd '%s'\n",
 | 
			
		||||
				  msg.data);
 | 
			
		||||
			dm_free(msg.data);
 | 
			
		||||
			return -EIO;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the device-mapper userspace tools.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -105,25 +105,6 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next);
 | 
			
		||||
int dm_event_register_handler(const struct dm_event_handler *dmevh);
 | 
			
		||||
int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
 | 
			
		||||
 | 
			
		||||
/* Set debug level for logging, and whether to log on stdout/stderr or syslog */
 | 
			
		||||
void dm_event_log_set(int debug_log_level, int use_syslog);
 | 
			
		||||
 | 
			
		||||
/* Log messages acroding to current debug level  */
 | 
			
		||||
__attribute__((format(printf, 6, 0)))
 | 
			
		||||
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);
 | 
			
		||||
/* Macro to route print_log do dm_event_log() */
 | 
			
		||||
#define DM_EVENT_LOG_FN(subsys) \
 | 
			
		||||
void print_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(subsys, level, file, line, dm_errno_or_class, format, ap);\
 | 
			
		||||
	va_end(ap);\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
 | 
			
		||||
   detailed descriptions. */
 | 
			
		||||
// FIXME  misuse of bitmask as enum
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
#
 | 
			
		||||
@@ -10,13 +10,33 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,15 +9,19 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
#include "log.h"
 | 
			
		||||
 | 
			
		||||
#include "lvm2cmd.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <syslog.h>
 | 
			
		||||
 | 
			
		||||
extern int dmeventd_debug;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * register_device() is called first and performs initialisation.
 | 
			
		||||
@@ -31,27 +35,49 @@ 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")
 | 
			
		||||
 | 
			
		||||
static void _lvm2_print_log(int level, const char *file, int line,
 | 
			
		||||
			    int dm_errno_or_class, const char *msg)
 | 
			
		||||
{
 | 
			
		||||
	print_log(level, file, line, dm_errno_or_class, "%s", msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Currently only one event can be processed at a time.
 | 
			
		||||
 */
 | 
			
		||||
static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FIXME Do not pass things directly to syslog, rather use the existing logging
 | 
			
		||||
 * facilities to sort logging ... however that mechanism needs to be somehow
 | 
			
		||||
 * configurable and we don't have that option yet
 | 
			
		||||
 */
 | 
			
		||||
static void _temporary_log_fn(int level,
 | 
			
		||||
			      const char *file __attribute__((unused)),
 | 
			
		||||
			      int line __attribute__((unused)),
 | 
			
		||||
			      int dm_errno __attribute__((unused)),
 | 
			
		||||
			      const char *message)
 | 
			
		||||
{
 | 
			
		||||
	level &= ~(_LOG_STDERR | _LOG_ONCE);
 | 
			
		||||
 | 
			
		||||
	switch (level) {
 | 
			
		||||
	case _LOG_DEBUG:
 | 
			
		||||
		if (dmeventd_debug >= 3)
 | 
			
		||||
			syslog(LOG_DEBUG, "%s", message);
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_INFO:
 | 
			
		||||
		if (dmeventd_debug >= 2)
 | 
			
		||||
			syslog(LOG_INFO, "%s", message);
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_NOTICE:
 | 
			
		||||
		if (dmeventd_debug >= 1)
 | 
			
		||||
			syslog(LOG_NOTICE, "%s", message);
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_WARN:
 | 
			
		||||
		syslog(LOG_WARNING, "%s", message);
 | 
			
		||||
		break;
 | 
			
		||||
	case _LOG_ERR:
 | 
			
		||||
		syslog(LOG_ERR, "%s", message);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		syslog(LOG_CRIT, "%s", message);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dmeventd_lvm2_lock(void)
 | 
			
		||||
{
 | 
			
		||||
	pthread_mutex_lock(&_event_mutex);
 | 
			
		||||
@@ -68,26 +94,24 @@ int dmeventd_lvm2_init(void)
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_lock(&_register_mutex);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Need some space for allocations.  1024 should be more
 | 
			
		||||
	 * than enough for what we need (device mapper name splitting)
 | 
			
		||||
	 */
 | 
			
		||||
	if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024)))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!_lvm_handle) {
 | 
			
		||||
		lvm2_log_fn(_lvm2_print_log);
 | 
			
		||||
 | 
			
		||||
		if (!(_lvm_handle = lvm2_init()))
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Need some space for allocations.  1024 should be more
 | 
			
		||||
		 * than enough for what we need (device mapper name splitting)
 | 
			
		||||
		 */
 | 
			
		||||
		if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024))) {
 | 
			
		||||
			lvm2_exit(_lvm_handle);
 | 
			
		||||
			_lvm_handle = NULL;
 | 
			
		||||
		if (!getenv("LVM_LOG_FILE_EPOCH"))
 | 
			
		||||
			lvm2_log_fn(_temporary_log_fn);
 | 
			
		||||
		if (!(_lvm_handle = lvm2_init())) {
 | 
			
		||||
			dm_pool_destroy(_mem_pool);
 | 
			
		||||
			_mem_pool = NULL;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lvm2_disable_dmeventd_monitoring(_lvm_handle);
 | 
			
		||||
		/* FIXME Temporary: move to dmeventd core */
 | 
			
		||||
		lvm2_run(_lvm_handle, "_memlock_inc");
 | 
			
		||||
		log_debug("lvm plugin initilized.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_register_count++;
 | 
			
		||||
@@ -103,14 +127,11 @@ void dmeventd_lvm2_exit(void)
 | 
			
		||||
	pthread_mutex_lock(&_register_mutex);
 | 
			
		||||
 | 
			
		||||
	if (!--_register_count) {
 | 
			
		||||
		log_debug("lvm plugin shuting down.");
 | 
			
		||||
		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.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pthread_mutex_unlock(&_register_mutex);
 | 
			
		||||
@@ -129,15 +150,12 @@ 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.",
 | 
			
		||||
			  device);
 | 
			
		||||
		syslog(LOG_ERR, "Unable to determine VG name from %s.\n",
 | 
			
		||||
		       device);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -146,45 +164,12 @@ 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);
 | 
			
		||||
 | 
			
		||||
	if (r < 0) {
 | 
			
		||||
		log_error("Unable to form LVM command. (too long).");
 | 
			
		||||
		syslog(LOG_ERR, "Unable to form LVM command. (too long).\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -39,36 +39,4 @@ struct dm_pool *dmeventd_lvm2_pool(void);
 | 
			
		||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
 | 
			
		||||
			  const char *cmd, const char *device);
 | 
			
		||||
 | 
			
		||||
#define dmeventd_lvm2_run_with_lock(cmdline) \
 | 
			
		||||
	({\
 | 
			
		||||
		int rc;\
 | 
			
		||||
		dmeventd_lvm2_lock();\
 | 
			
		||||
		rc = dmeventd_lvm2_run(cmdline);\
 | 
			
		||||
		dmeventd_lvm2_unlock();\
 | 
			
		||||
		rc;\
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
#define dmeventd_lvm2_init_with_pool(name, st) \
 | 
			
		||||
	({\
 | 
			
		||||
		struct dm_pool *mem;\
 | 
			
		||||
		st = NULL;\
 | 
			
		||||
		if (dmeventd_lvm2_init()) {\
 | 
			
		||||
			if ((mem = dm_pool_create(name, 2048)) &&\
 | 
			
		||||
			    (st = dm_pool_zalloc(mem, sizeof(*st))))\
 | 
			
		||||
				st->mem = mem;\
 | 
			
		||||
			else {\
 | 
			
		||||
				if (mem)\
 | 
			
		||||
					dm_pool_destroy(mem);\
 | 
			
		||||
				dmeventd_lvm2_exit();\
 | 
			
		||||
			}\
 | 
			
		||||
		}\
 | 
			
		||||
		st;\
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
#define dmeventd_lvm2_exit_with_pool(pool) \
 | 
			
		||||
	do {\
 | 
			
		||||
		dm_pool_destroy(pool->mem);\
 | 
			
		||||
		dmeventd_lvm2_exit();\
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
#endif /* _DMEVENTD_LVMWRAP_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2005-2012 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,30 +9,26 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "activate.h"	/* For TARGET_NAME* */
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
 | 
			
		||||
#include <syslog.h> /* FIXME Replace syslog with multilog */
 | 
			
		||||
/* FIXME Missing openlog? */
 | 
			
		||||
/* FIXME Replace most syslogs with log_error() style messages and add complete context. */
 | 
			
		||||
/* FIXME Reformat to 80 char lines. */
 | 
			
		||||
 | 
			
		||||
#define ME_IGNORE    0
 | 
			
		||||
#define ME_INSYNC    1
 | 
			
		||||
#define ME_FAILURE   2
 | 
			
		||||
 | 
			
		||||
struct dso_state {
 | 
			
		||||
	struct dm_pool *mem;
 | 
			
		||||
	char cmd_lvconvert[512];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DM_EVENT_LOG_FN("mirr")
 | 
			
		||||
 | 
			
		||||
static void _process_status_code(dm_status_mirror_health_t health,
 | 
			
		||||
				 uint32_t major, uint32_t minor,
 | 
			
		||||
				 const char *dev_type, int *r)
 | 
			
		||||
static int _process_status_code(const char status_code, const char *dev_name,
 | 
			
		||||
				const char *dev_type, int r)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 *    A => Alive - No failures
 | 
			
		||||
@@ -42,170 +38,206 @@ static void _process_status_code(dm_status_mirror_health_t health,
 | 
			
		||||
	 *    R => Read - A read failure occurred, mirror data unaffected
 | 
			
		||||
	 *    U => Unclassified failure (bug)
 | 
			
		||||
	 */ 
 | 
			
		||||
	switch (health) {
 | 
			
		||||
	case DM_STATUS_MIRROR_ALIVE:
 | 
			
		||||
		return;
 | 
			
		||||
	case DM_STATUS_MIRROR_FLUSH_FAILED:
 | 
			
		||||
		log_error("%s device %u:%u flush failed.",
 | 
			
		||||
			  dev_type, major, minor);
 | 
			
		||||
		*r = ME_FAILURE;
 | 
			
		||||
		break;
 | 
			
		||||
	case DM_STATUS_MIRROR_SYNC_FAILED:
 | 
			
		||||
		log_error("%s device %u:%u sync failed.",
 | 
			
		||||
			  dev_type, major, minor);
 | 
			
		||||
		break;
 | 
			
		||||
	case DM_STATUS_MIRROR_READ_FAILED:
 | 
			
		||||
		log_error("%s device %u:%u read failed.",
 | 
			
		||||
			  dev_type, major, minor);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		log_error("%s device %u:%u has failed (%c).",
 | 
			
		||||
			  dev_type, major, minor, (char)health);
 | 
			
		||||
		*r = ME_FAILURE;
 | 
			
		||||
		break;
 | 
			
		||||
	if (status_code == 'F') {
 | 
			
		||||
		syslog(LOG_ERR, "%s device %s flush failed.",
 | 
			
		||||
		       dev_type, dev_name);
 | 
			
		||||
		r = ME_FAILURE;
 | 
			
		||||
	} else if (status_code == 'S')
 | 
			
		||||
		syslog(LOG_ERR, "%s device %s sync failed.",
 | 
			
		||||
		       dev_type, dev_name);
 | 
			
		||||
	else if (status_code == 'R')
 | 
			
		||||
		syslog(LOG_ERR, "%s device %s read failed.",
 | 
			
		||||
		       dev_type, dev_name);
 | 
			
		||||
	else if (status_code != 'A') {
 | 
			
		||||
		syslog(LOG_ERR, "%s device %s has failed (%c).",
 | 
			
		||||
		       dev_type, dev_name, status_code);
 | 
			
		||||
		r = ME_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _get_mirror_event(struct dso_state *state, char *params)
 | 
			
		||||
{
 | 
			
		||||
	int r = ME_INSYNC;
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for bad mirror devices */
 | 
			
		||||
	for (i = 0; i < ms->dev_count; ++i)
 | 
			
		||||
		_process_status_code(ms->devs[i].health,
 | 
			
		||||
				     ms->devs[i].major, ms->devs[i].minor,
 | 
			
		||||
				     i ? "Secondary mirror" : "Primary mirror", &r);
 | 
			
		||||
 | 
			
		||||
	/* Check for bad disk log device */
 | 
			
		||||
	for (i = 0; i < ms->log_count; ++i)
 | 
			
		||||
		_process_status_code(ms->logs[i].health,
 | 
			
		||||
				     ms->logs[i].major, ms->logs[i].minor,
 | 
			
		||||
				     "Log", &r);
 | 
			
		||||
 | 
			
		||||
	/* Ignore if not in-sync */
 | 
			
		||||
	if ((r == ME_INSYNC) && (ms->insync_regions != ms->total_regions))
 | 
			
		||||
		r = ME_IGNORE;
 | 
			
		||||
 | 
			
		||||
	dm_pool_free(state->mem, ms);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _remove_failed_devices(const char *cmd_lvconvert, const char *device)
 | 
			
		||||
static int _get_mirror_event(char *params)
 | 
			
		||||
{
 | 
			
		||||
	int i, r = ME_INSYNC;
 | 
			
		||||
	char **args = NULL;
 | 
			
		||||
	char *dev_status_str;
 | 
			
		||||
	char *log_status_str;
 | 
			
		||||
	char *sync_str;
 | 
			
		||||
	char *p = NULL;
 | 
			
		||||
	int log_argc, num_devs;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * dm core parms:	     0 409600 mirror
 | 
			
		||||
	 * Mirror core parms:	     2 253:4 253:5 400/400
 | 
			
		||||
	 * New-style failure params: 1 AA
 | 
			
		||||
	 * New-style log params:     3 cluster 253:3 A
 | 
			
		||||
	 *			 or  3 disk 253:3 A
 | 
			
		||||
	 *			 or  1 core
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* number of devices */
 | 
			
		||||
	if (!dm_split_words(params, 1, 0, &p))
 | 
			
		||||
		goto out_parse;
 | 
			
		||||
 | 
			
		||||
	if (!(num_devs = atoi(p)) ||
 | 
			
		||||
	    (num_devs > DEFAULT_MIRROR_MAX_IMAGES) || (num_devs < 0))
 | 
			
		||||
		goto out_parse;
 | 
			
		||||
	p += strlen(p) + 1;
 | 
			
		||||
 | 
			
		||||
	/* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */
 | 
			
		||||
	args = dm_malloc((num_devs + 7) * sizeof(char *));
 | 
			
		||||
	if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5)
 | 
			
		||||
		goto out_parse;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Code differs from lib/mirror/mirrored.c */
 | 
			
		||||
	dev_status_str = args[2 + num_devs];
 | 
			
		||||
	log_argc = atoi(args[3 + num_devs]);
 | 
			
		||||
	log_status_str = args[3 + num_devs + log_argc];
 | 
			
		||||
	sync_str = args[num_devs];
 | 
			
		||||
 | 
			
		||||
	/* Check for bad mirror devices */
 | 
			
		||||
	for (i = 0; i < num_devs; i++)
 | 
			
		||||
		r = _process_status_code(dev_status_str[i], args[i],
 | 
			
		||||
			i ? "Secondary mirror" : "Primary mirror", r);
 | 
			
		||||
 | 
			
		||||
	/* Check for bad disk log device */
 | 
			
		||||
	if (log_argc > 1)
 | 
			
		||||
		r = _process_status_code(log_status_str[0],
 | 
			
		||||
					 args[2 + num_devs + log_argc],
 | 
			
		||||
					 "Log", r);
 | 
			
		||||
 | 
			
		||||
	if (r == ME_FAILURE)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	p = strstr(sync_str, "/");
 | 
			
		||||
	if (p) {
 | 
			
		||||
		p[0] = '\0';
 | 
			
		||||
		if (strcmp(sync_str, p+1))
 | 
			
		||||
			r = ME_IGNORE;
 | 
			
		||||
		p[0] = '/';
 | 
			
		||||
	} else
 | 
			
		||||
		goto out_parse;
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	dm_free(args);
 | 
			
		||||
	return r;
 | 
			
		||||
 | 
			
		||||
out_parse:
 | 
			
		||||
	dm_free(args);
 | 
			
		||||
	syslog(LOG_ERR, "Unable to parse mirror status string.");
 | 
			
		||||
	return ME_IGNORE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _remove_failed_devices(const char *device)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
#define CMD_SIZE 256	/* FIXME Use system restriction */
 | 
			
		||||
	char cmd_str[CMD_SIZE];
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str),
 | 
			
		||||
				   "lvscan --cache", device))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	r = dmeventd_lvm2_run(cmd_str);
 | 
			
		||||
 | 
			
		||||
	if (!r)
 | 
			
		||||
		syslog(LOG_INFO, "Re-scan of mirror device %s failed.", device);
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str),
 | 
			
		||||
				  "lvconvert --config devices{ignore_suspended_devices=1} "
 | 
			
		||||
				  "--repair --use-policies", device))
 | 
			
		||||
		return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */
 | 
			
		||||
 | 
			
		||||
	/* 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(cmd_str);
 | 
			
		||||
 | 
			
		||||
	log_info("Repair of mirrored device %s finished successfully.", device);
 | 
			
		||||
	syslog(LOG_INFO, "Repair of mirrored device %s %s.", device,
 | 
			
		||||
	       (r) ? "finished successfully" : "failed");
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
	return (r) ? 0 : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void process_event(struct dm_task *dmt,
 | 
			
		||||
		   enum dm_event_mask event __attribute__((unused)),
 | 
			
		||||
		   void **user)
 | 
			
		||||
		   void **unused __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	void *next = NULL;
 | 
			
		||||
	uint64_t start, length;
 | 
			
		||||
	char *target_type = NULL;
 | 
			
		||||
	char *params;
 | 
			
		||||
	const char *device = dm_task_get_name(dmt);
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_lock();
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		next = dm_get_next_target(dmt, next, &start, &length,
 | 
			
		||||
					  &target_type, ¶ms);
 | 
			
		||||
 | 
			
		||||
		if (!target_type) {
 | 
			
		||||
			log_info("%s mapping lost.", device);
 | 
			
		||||
			syslog(LOG_INFO, "%s mapping lost.", device);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (strcmp(target_type, TARGET_NAME_MIRROR)) {
 | 
			
		||||
			log_info("%s has unmirrored portion.", device);
 | 
			
		||||
		if (strcmp(target_type, "mirror")) {
 | 
			
		||||
			syslog(LOG_INFO, "%s has unmirrored portion.", device);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch(_get_mirror_event(state, params)) {
 | 
			
		||||
		switch(_get_mirror_event(params)) {
 | 
			
		||||
		case ME_INSYNC:
 | 
			
		||||
			/* FIXME: all we really know is that this
 | 
			
		||||
			   _part_ of the device is in sync
 | 
			
		||||
			   Also, this is not an error
 | 
			
		||||
			*/
 | 
			
		||||
			log_notice("%s is now in-sync.", device);
 | 
			
		||||
			syslog(LOG_NOTICE, "%s is now in-sync.", device);
 | 
			
		||||
			break;
 | 
			
		||||
		case ME_FAILURE:
 | 
			
		||||
			log_error("Device failure in %s.", device);
 | 
			
		||||
			if (!_remove_failed_devices(state->cmd_lvconvert, device))
 | 
			
		||||
			syslog(LOG_ERR, "Device failure in %s.", device);
 | 
			
		||||
			if (_remove_failed_devices(device))
 | 
			
		||||
				/* FIXME Why are all the error return codes unused? Get rid of them? */
 | 
			
		||||
				log_error("Failed to remove faulty devices in %s.",
 | 
			
		||||
					  device);
 | 
			
		||||
				syslog(LOG_ERR, "Failed to remove faulty devices in %s.",
 | 
			
		||||
				       device);
 | 
			
		||||
			/* Should check before warning user that device is now linear
 | 
			
		||||
			else
 | 
			
		||||
				log_notice("%s is now a linear device.",
 | 
			
		||||
					   device);
 | 
			
		||||
				syslog(LOG_NOTICE, "%s is now a linear device.\n",
 | 
			
		||||
					device);
 | 
			
		||||
			*/
 | 
			
		||||
			break;
 | 
			
		||||
		case ME_IGNORE:
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* FIXME Provide value then! */
 | 
			
		||||
			log_warn("WARNING: %s received unknown event.", device);
 | 
			
		||||
			syslog(LOG_INFO, "Unknown event received.");
 | 
			
		||||
		}
 | 
			
		||||
	} while (next);
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int register_device(const char *device,
 | 
			
		||||
		    const char *uuid __attribute__((unused)),
 | 
			
		||||
		    int major __attribute__((unused)),
 | 
			
		||||
		    int minor __attribute__((unused)),
 | 
			
		||||
		    void **user)
 | 
			
		||||
		    void **unused __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state;
 | 
			
		||||
	if (!dmeventd_lvm2_init())
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	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))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
	log_info("Monitoring mirror device %s for events.", device);
 | 
			
		||||
	syslog(LOG_INFO, "Monitoring mirror device %s for events.", device);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor mirror %s.", 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)
 | 
			
		||||
		      void **unused __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
	log_info("No longer monitoring mirror device %s for events.",
 | 
			
		||||
		 device);
 | 
			
		||||
	syslog(LOG_INFO, "No longer monitoring mirror device %s for events.",
 | 
			
		||||
	       device);
 | 
			
		||||
	dmeventd_lvm2_exit();
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,174 +9,172 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
#include "dmeventd_lvm.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_lvconvert[512];
 | 
			
		||||
	uint64_t raid_devs[RAID_DEVS_ELEMS];
 | 
			
		||||
	int failed;
 | 
			
		||||
	int warned;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DM_EVENT_LOG_FN("raid")
 | 
			
		||||
 | 
			
		||||
#include <syslog.h> /* FIXME Replace syslog with multilog */
 | 
			
		||||
/* FIXME Missing openlog? */
 | 
			
		||||
/* FIXME Replace most syslogs with log_error() style messages and add complete context. */
 | 
			
		||||
/* FIXME Reformat to 80 char lines. */
 | 
			
		||||
 | 
			
		||||
static int _process_raid_event(struct dso_state *state, char *params, const char *device)
 | 
			
		||||
/*
 | 
			
		||||
 * run_repair is a close copy to
 | 
			
		||||
 * plugins/mirror/dmeventd_mirror.c:_remove_failed_devices()
 | 
			
		||||
 */
 | 
			
		||||
static int run_repair(const char *device)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_status_raid *status;
 | 
			
		||||
	const char *d;
 | 
			
		||||
	int dead = 0, r = 1;
 | 
			
		||||
	uint32_t dev;
 | 
			
		||||
	int r;
 | 
			
		||||
#define CMD_SIZE 256	/* FIXME Use system restriction */
 | 
			
		||||
	char cmd_str[CMD_SIZE];
 | 
			
		||||
 | 
			
		||||
	if (!dm_get_status_raid(state->mem, params, &status)) {
 | 
			
		||||
		log_error("Failed to process status line for %s.", device);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str),
 | 
			
		||||
				  "lvscan --cache", device))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	d = status->dev_health;
 | 
			
		||||
	while ((d = strchr(d, 'D'))) {
 | 
			
		||||
		dev = (uint32_t)(d - status->dev_health);
 | 
			
		||||
	r = dmeventd_lvm2_run(cmd_str);
 | 
			
		||||
 | 
			
		||||
		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 (!r)
 | 
			
		||||
		syslog(LOG_INFO, "Re-scan of RAID device %s failed.", device);
 | 
			
		||||
 | 
			
		||||
		d++;
 | 
			
		||||
		dead = 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str),
 | 
			
		||||
				  "lvconvert --config devices{ignore_suspended_devices=1} "
 | 
			
		||||
				  "--repair --use-policies", device))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* if repair goes OK, report success even if lvscan has failed */
 | 
			
		||||
	r = dmeventd_lvm2_run(cmd_str);
 | 
			
		||||
 | 
			
		||||
	if (!r)
 | 
			
		||||
		syslog(LOG_INFO, "Repair of RAID device %s failed.", device);
 | 
			
		||||
 | 
			
		||||
	return (r) ? 0 : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _process_raid_event(char *params, const char *device)
 | 
			
		||||
{
 | 
			
		||||
	int i, n, failure = 0;
 | 
			
		||||
	char *p, *a[4];
 | 
			
		||||
	char *raid_type;
 | 
			
		||||
	char *num_devices;
 | 
			
		||||
	char *health_chars;
 | 
			
		||||
	char *resync_ratio;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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...
 | 
			
		||||
	 * RAID parms:     <raid_type> <#raid_disks> \
 | 
			
		||||
	 *                 <health chars> <resync ratio>
 | 
			
		||||
	 */
 | 
			
		||||
	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 (!dm_split_words(params, 4, 0, a)) {
 | 
			
		||||
		syslog(LOG_ERR, "Failed to process status line for %s\n",
 | 
			
		||||
		       device);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	raid_type = a[0];
 | 
			
		||||
	num_devices = a[1];
 | 
			
		||||
	health_chars = a[2];
 | 
			
		||||
	resync_ratio = a[3];
 | 
			
		||||
 | 
			
		||||
	if (!(n = atoi(num_devices))) {
 | 
			
		||||
		syslog(LOG_ERR, "Failed to parse number of devices for %s: %s",
 | 
			
		||||
		       device, num_devices);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dead) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Use the first event to run a repair ignoring any additonal ones.
 | 
			
		||||
		 *
 | 
			
		||||
		 * We presume lvconvert to do pre-repair
 | 
			
		||||
		 * checks to avoid bloat in this plugin.
 | 
			
		||||
		 */
 | 
			
		||||
		if (state->failed)
 | 
			
		||||
			goto out; /* already reported */
 | 
			
		||||
 | 
			
		||||
		state->failed = 1;
 | 
			
		||||
 | 
			
		||||
		/* 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);
 | 
			
		||||
			r = 0;
 | 
			
		||||
	for (i = 0; i < n; i++) {
 | 
			
		||||
		switch (health_chars[i]) {
 | 
			
		||||
		case 'A':
 | 
			
		||||
			/* Device is 'A'live and well */
 | 
			
		||||
		case 'a':
 | 
			
		||||
			/* Device is 'a'live, but not yet in-sync */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'D':
 | 
			
		||||
			syslog(LOG_ERR,
 | 
			
		||||
			       "Device #%d of %s array, %s, has failed.",
 | 
			
		||||
			       i, raid_type, device);
 | 
			
		||||
			failure++;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* Unhandled character returned from kernel */
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	} 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");
 | 
			
		||||
		if (failure)
 | 
			
		||||
			return run_repair(device);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	dm_pool_free(state->mem, status);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
	p = strstr(resync_ratio, "/");
 | 
			
		||||
	if (!p) {
 | 
			
		||||
		syslog(LOG_ERR, "Failed to parse resync_ratio for %s: %s",
 | 
			
		||||
		       device, resync_ratio);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	p[0] = '\0';
 | 
			
		||||
	syslog(LOG_INFO, "%s array, %s, is %s in-sync.",
 | 
			
		||||
	       raid_type, device, strcmp(resync_ratio, p+1) ? "not" : "now");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void process_event(struct dm_task *dmt,
 | 
			
		||||
		   enum dm_event_mask event __attribute__((unused)),
 | 
			
		||||
		   void **user)
 | 
			
		||||
		   void **unused __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	void *next = NULL;
 | 
			
		||||
	uint64_t start, length;
 | 
			
		||||
	char *target_type = NULL;
 | 
			
		||||
	char *params;
 | 
			
		||||
	const char *device = dm_task_get_name(dmt);
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_lock();
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		next = dm_get_next_target(dmt, next, &start, &length,
 | 
			
		||||
					  &target_type, ¶ms);
 | 
			
		||||
 | 
			
		||||
		if (!target_type) {
 | 
			
		||||
			log_info("%s mapping lost.", device);
 | 
			
		||||
			syslog(LOG_INFO, "%s mapping lost.", device);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (strcmp(target_type, "raid")) {
 | 
			
		||||
			log_info("%s has non-raid portion.", device);
 | 
			
		||||
			syslog(LOG_INFO, "%s has non-raid portion.", device);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!_process_raid_event(state, params, device))
 | 
			
		||||
			log_error("Failed to process event for %s.",
 | 
			
		||||
				  device);
 | 
			
		||||
		if (_process_raid_event(params, device))
 | 
			
		||||
			syslog(LOG_ERR, "Failed to process event for %s",
 | 
			
		||||
			       device);
 | 
			
		||||
	} while (next);
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int register_device(const char *device,
 | 
			
		||||
		    const char *uuid __attribute__((unused)),
 | 
			
		||||
		    int major __attribute__((unused)),
 | 
			
		||||
		    int minor __attribute__((unused)),
 | 
			
		||||
		    void **user)
 | 
			
		||||
		    void **unused __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state;
 | 
			
		||||
	if (!dmeventd_lvm2_init())
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	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))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
	log_info("Monitoring RAID device %s for events.", device);
 | 
			
		||||
	syslog(LOG_INFO, "Monitoring RAID device %s for events.", device);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor RAID %s.", 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)
 | 
			
		||||
		      void **unused __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
	log_info("No longer monitoring RAID device %s for events.",
 | 
			
		||||
		 device);
 | 
			
		||||
	syslog(LOG_INFO, "No longer monitoring RAID device %s for events.",
 | 
			
		||||
	       device);
 | 
			
		||||
	dmeventd_lvm2_exit();
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2007-2015 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2007-2011 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,36 +9,35 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/sysmacros.h>
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <syslog.h> /* FIXME Replace syslog with multilog */
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
/* FIXME Missing openlog? */
 | 
			
		||||
 | 
			
		||||
/* First warning when snapshot is 80% full. */
 | 
			
		||||
#define WARNING_THRESH	(DM_PERCENT_1 * 80)
 | 
			
		||||
#define WARNING_THRESH 80
 | 
			
		||||
/* Run a check every 5%. */
 | 
			
		||||
#define CHECK_STEP	(DM_PERCENT_1 *  5)
 | 
			
		||||
#define CHECK_STEP 5
 | 
			
		||||
/* Do not bother checking snapshots less than 50% full. */
 | 
			
		||||
#define CHECK_MINIMUM	(DM_PERCENT_1 * 50)
 | 
			
		||||
#define CHECK_MINIMUM 50
 | 
			
		||||
 | 
			
		||||
#define UMOUNT_COMMAND "/bin/umount"
 | 
			
		||||
 | 
			
		||||
struct dso_state {
 | 
			
		||||
	struct dm_pool *mem;
 | 
			
		||||
	dm_percent_t percent_check;
 | 
			
		||||
	int percent_check;
 | 
			
		||||
	uint64_t known_size;
 | 
			
		||||
	char cmd_lvextend[512];
 | 
			
		||||
	char cmd_str[1024];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DM_EVENT_LOG_FN("snap")
 | 
			
		||||
 | 
			
		||||
static int _run(const char *cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
        va_list ap;
 | 
			
		||||
@@ -63,7 +62,7 @@ static int _run(const char *cmd, ...)
 | 
			
		||||
                va_end(ap);
 | 
			
		||||
 | 
			
		||||
                execvp(cmd, (char **)argv);
 | 
			
		||||
                log_sys_error("exec", cmd);
 | 
			
		||||
                syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno));
 | 
			
		||||
                exit(127);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -82,56 +81,18 @@ static int _run(const char *cmd, ...)
 | 
			
		||||
 | 
			
		||||
static int _extend(const char *cmd)
 | 
			
		||||
{
 | 
			
		||||
	log_debug("Extending snapshot via %s.", cmd);
 | 
			
		||||
	return dmeventd_lvm2_run_with_lock(cmd);
 | 
			
		||||
	return dmeventd_lvm2_run(cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SNAPSHOT_REMOVE
 | 
			
		||||
/* Remove invalid snapshot from dm-table */
 | 
			
		||||
/* Experimental for now and not used by default */
 | 
			
		||||
static int _remove(const char *uuid)
 | 
			
		||||
{
 | 
			
		||||
	int r = 1;
 | 
			
		||||
	uint32_t cookie = 0;
 | 
			
		||||
	struct dm_task *dmt;
 | 
			
		||||
 | 
			
		||||
	if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_set_uuid(dmt, uuid)) {
 | 
			
		||||
		r = 0;
 | 
			
		||||
		goto_out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dm_task_retry_remove(dmt);
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_set_cookie(dmt, &cookie, 0)) {
 | 
			
		||||
		r = 0;
 | 
			
		||||
		goto_out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!dm_task_run(dmt)) {
 | 
			
		||||
		r = 0;
 | 
			
		||||
		goto_out;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	dm_task_destroy(dmt);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
#endif /* SNAPSHOT_REMOVE */
 | 
			
		||||
 | 
			
		||||
static void _umount(const char *device, int major, int minor)
 | 
			
		||||
{
 | 
			
		||||
	FILE *mounts;
 | 
			
		||||
	char buffer[4096];
 | 
			
		||||
	char *words[3];
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	const char procmounts[] = "/proc/mounts";
 | 
			
		||||
 | 
			
		||||
	if (!(mounts = fopen(procmounts, "r"))) {
 | 
			
		||||
		log_sys_error("fopen", procmounts);
 | 
			
		||||
		log_error("Not umounting %s.", device);
 | 
			
		||||
	if (!(mounts = fopen("/proc/mounts", "r"))) {
 | 
			
		||||
		syslog(LOG_ERR, "Could not read /proc/mounts. Not umounting %s.\n", device);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -149,24 +110,23 @@ static void _umount(const char *device, int major, int minor)
 | 
			
		||||
			continue; /* can't stat, skip this one */
 | 
			
		||||
 | 
			
		||||
		if (S_ISBLK(st.st_mode) &&
 | 
			
		||||
		    (int) major(st.st_rdev) == major &&
 | 
			
		||||
		    (int) minor(st.st_rdev) == minor) {
 | 
			
		||||
			log_error("Unmounting invalid snapshot %s from %s.", device, words[1]);
 | 
			
		||||
			if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
 | 
			
		||||
				log_error("Failed to umount snapshot %s from %s: %s.",
 | 
			
		||||
					  device, words[1], strerror(errno));
 | 
			
		||||
		    major(st.st_rdev) == major &&
 | 
			
		||||
		    minor(st.st_rdev) == minor) {
 | 
			
		||||
			syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.\n", device, words[1]);
 | 
			
		||||
                        if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
 | 
			
		||||
                                syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.\n",
 | 
			
		||||
                                       device, words[1], strerror(errno));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fclose(mounts))
 | 
			
		||||
		log_sys_error("close", procmounts);
 | 
			
		||||
		syslog(LOG_ERR, "Failed to close /proc/mounts.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void process_event(struct dm_task *dmt,
 | 
			
		||||
		   enum dm_event_mask event __attribute__((unused)),
 | 
			
		||||
		   void **user)
 | 
			
		||||
		   void **private)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	void *next = NULL;
 | 
			
		||||
	uint64_t start, length;
 | 
			
		||||
	char *target_type = NULL;
 | 
			
		||||
@@ -174,47 +134,28 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
	struct dm_status_snapshot *status = NULL;
 | 
			
		||||
	const char *device = dm_task_get_name(dmt);
 | 
			
		||||
	int percent;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	struct dso_state *state = *private;
 | 
			
		||||
 | 
			
		||||
	/* No longer monitoring, waiting for remove */
 | 
			
		||||
	if (!state->percent_check)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_lock();
 | 
			
		||||
 | 
			
		||||
	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
 | 
			
		||||
	if (!target_type || strcmp(target_type, "snapshot")) {
 | 
			
		||||
		log_error("Target %s is not snapshot.", target_type);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!target_type)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!dm_get_status_snapshot(state->mem, params, &status)) {
 | 
			
		||||
		log_error("Cannot parse snapshot %s state: %s.", device, params);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!dm_get_status_snapshot(state->mem, params, &status))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the snapshot has been invalidated or we failed to parse
 | 
			
		||||
	 * the status string. Report the full status string to syslog.
 | 
			
		||||
	 */
 | 
			
		||||
	if (status->invalid || status->overflow || !status->total_sectors) {
 | 
			
		||||
		log_warn("WARNING: Snapshot %s changed state to: %s and should be removed.",
 | 
			
		||||
			 device, params);
 | 
			
		||||
		state->percent_check = 0;
 | 
			
		||||
		if (dm_task_get_info(dmt, &info))
 | 
			
		||||
	if (status->invalid) {
 | 
			
		||||
		struct dm_info info;
 | 
			
		||||
		if (dm_task_get_info(dmt, &info)) {
 | 
			
		||||
			dmeventd_lvm2_unlock();
 | 
			
		||||
			_umount(device, info.major, info.minor);
 | 
			
		||||
#ifdef SNAPSHOT_REMOVE
 | 
			
		||||
		/* Maybe configurable ? */
 | 
			
		||||
		_remove(dm_task_get_uuid(dmt));
 | 
			
		||||
#endif
 | 
			
		||||
		pthread_kill(pthread_self(), SIGALRM);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (length <= (status->used_sectors - status->metadata_sectors)) {
 | 
			
		||||
		/* TODO eventually recognize earlier when room is enough */
 | 
			
		||||
		log_info("Dropping monitoring of fully provisioned snapshot %s.",
 | 
			
		||||
			 device);
 | 
			
		||||
		pthread_kill(pthread_self(), SIGALRM);
 | 
			
		||||
		goto out;
 | 
			
		||||
			return;
 | 
			
		||||
		} /* else; too bad, but this is best-effort thing... */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Snapshot size had changed. Clear the threshold. */
 | 
			
		||||
@@ -223,51 +164,69 @@ void process_event(struct dm_task *dmt,
 | 
			
		||||
		state->known_size = status->total_sectors;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	percent = dm_make_percent(status->used_sectors, status->total_sectors);
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the snapshot has been invalidated or we failed to parse
 | 
			
		||||
	 * the status string. Report the full status string to syslog.
 | 
			
		||||
	 */
 | 
			
		||||
	if (status->invalid || !status->total_sectors) {
 | 
			
		||||
		syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
 | 
			
		||||
		state->percent_check = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	percent = (int) (100 * status->used_sectors / status->total_sectors);
 | 
			
		||||
	if (percent >= state->percent_check) {
 | 
			
		||||
		/* Usage has raised more than CHECK_STEP since the last
 | 
			
		||||
		   time. Run actions. */
 | 
			
		||||
		state->percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
 | 
			
		||||
 | 
			
		||||
		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));
 | 
			
		||||
 | 
			
		||||
			syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
 | 
			
		||||
		/* Try to extend the snapshot, in accord with user-set policies */
 | 
			
		||||
		if (!_extend(state->cmd_lvextend))
 | 
			
		||||
			log_error("Failed to extend snapshot %s.", device);
 | 
			
		||||
		if (!_extend(state->cmd_str))
 | 
			
		||||
			syslog(LOG_ERR, "Failed to extend snapshot %s.\n", device);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	dm_pool_free(state->mem, status);
 | 
			
		||||
	if (status)
 | 
			
		||||
		dm_pool_free(state->mem, status);
 | 
			
		||||
	dmeventd_lvm2_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int register_device(const char *device,
 | 
			
		||||
		    const char *uuid __attribute__((unused)),
 | 
			
		||||
		    int major __attribute__((unused)),
 | 
			
		||||
		    int minor __attribute__((unused)),
 | 
			
		||||
		    void **user)
 | 
			
		||||
		    void **private)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_pool *statemem = NULL;
 | 
			
		||||
	struct dso_state *state;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_init_with_pool("snapshot_state", state))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	if (!dmeventd_lvm2_init())
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend,
 | 
			
		||||
				   sizeof(state->cmd_lvextend),
 | 
			
		||||
	if (!(statemem = dm_pool_create("snapshot_state", 512)) ||
 | 
			
		||||
	    !(state = dm_pool_zalloc(statemem, sizeof(*state))))
 | 
			
		||||
		goto bad;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(statemem, state->cmd_str,
 | 
			
		||||
				   sizeof(state->cmd_str),
 | 
			
		||||
				   "lvextend --use-policies", device))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
		goto bad;
 | 
			
		||||
 | 
			
		||||
	state->mem = statemem;
 | 
			
		||||
	state->percent_check = CHECK_MINIMUM;
 | 
			
		||||
	*user = state;
 | 
			
		||||
	*private = state;
 | 
			
		||||
 | 
			
		||||
	log_info("Monitoring snapshot %s.", device);
 | 
			
		||||
	syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor snapshot %s.", device);
 | 
			
		||||
 | 
			
		||||
	if (state)
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
	if (statemem)
 | 
			
		||||
		dm_pool_destroy(statemem);
 | 
			
		||||
	dmeventd_lvm2_exit();
 | 
			
		||||
out:
 | 
			
		||||
	syslog(LOG_ERR, "Failed to monitor snapshot %s.\n", device);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -276,12 +235,13 @@ int unregister_device(const char *device,
 | 
			
		||||
		      const char *uuid __attribute__((unused)),
 | 
			
		||||
		      int major __attribute__((unused)),
 | 
			
		||||
		      int minor __attribute__((unused)),
 | 
			
		||||
		      void **user)
 | 
			
		||||
		      void **private)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	struct dso_state *state = *private;
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
	log_info("No longer monitoring snapshot %s.", device);
 | 
			
		||||
	syslog(LOG_INFO, "No longer monitoring snapshot %s\n", device);
 | 
			
		||||
	dm_pool_destroy(state->mem);
 | 
			
		||||
	dmeventd_lvm2_exit();
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2011-2013 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of LVM2.
 | 
			
		||||
 *
 | 
			
		||||
@@ -9,15 +9,39 @@
 | 
			
		||||
 *
 | 
			
		||||
 * You should 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
 | 
			
		||||
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "lib.h"	/* using here lvm log */
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "lib.h"
 | 
			
		||||
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <syslog.h> /* FIXME Replace syslog with multilog */
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
/* FIXME Missing openlog? */
 | 
			
		||||
 | 
			
		||||
/* First warning when thin is 80% full. */
 | 
			
		||||
#define WARNING_THRESH 80
 | 
			
		||||
/* Run a check every 5%. */
 | 
			
		||||
#define CHECK_STEP 5
 | 
			
		||||
/* Do not bother checking thins less than 50% full. */
 | 
			
		||||
#define CHECK_MINIMUM 50
 | 
			
		||||
 | 
			
		||||
#define UMOUNT_COMMAND "/bin/umount"
 | 
			
		||||
 | 
			
		||||
#define THIN_DEBUG 0
 | 
			
		||||
 | 
			
		||||
struct dso_state {
 | 
			
		||||
	struct dm_pool *mem;
 | 
			
		||||
	int metadata_percent_check;
 | 
			
		||||
	int data_percent_check;
 | 
			
		||||
	uint64_t known_metadata_size;
 | 
			
		||||
	uint64_t known_data_size;
 | 
			
		||||
	char cmd_str[1024];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* TODO - move this mountinfo code into library to be reusable */
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
@@ -25,376 +49,338 @@
 | 
			
		||||
#else
 | 
			
		||||
#  define MAJOR(x) major((x))
 | 
			
		||||
#  define MINOR(x) minor((x))
 | 
			
		||||
#  define MKDEV(x,y) makedev((x),(y))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* First warning when thin data or metadata is 80% full. */
 | 
			
		||||
#define WARNING_THRESH	(DM_PERCENT_1 * 80)
 | 
			
		||||
/* Umount thin LVs when thin data or metadata LV is >=
 | 
			
		||||
 * and lvextend --use-policies has failed. */
 | 
			
		||||
#define UMOUNT_THRESH	(DM_PERCENT_1 * 95)
 | 
			
		||||
/* Run a check every 5%. */
 | 
			
		||||
#define CHECK_STEP	(DM_PERCENT_1 *  5)
 | 
			
		||||
/* Do not bother checking thin data or metadata is less than 50% full. */
 | 
			
		||||
#define CHECK_MINIMUM	(DM_PERCENT_1 * 50)
 | 
			
		||||
 | 
			
		||||
#define UMOUNT_COMMAND "/bin/umount"
 | 
			
		||||
 | 
			
		||||
#define MAX_FAILS	(256)  /* ~42 mins between cmd call retry with 10s delay */
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DM_EVENT_LOG_FN("thin")
 | 
			
		||||
 | 
			
		||||
static int _run_command(struct dso_state *state)
 | 
			
		||||
/* 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)
 | 
			
		||||
{
 | 
			
		||||
	char val[16];
 | 
			
		||||
	int i;
 | 
			
		||||
	struct dm_task *dmt;
 | 
			
		||||
	const struct dm_deps *deps;
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	int major, minor;
 | 
			
		||||
	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 (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.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
	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 THIN_DEBUG
 | 
			
		||||
	{
 | 
			
		||||
		char dev_name[PATH_MAX];
 | 
			
		||||
		if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name)))
 | 
			
		||||
			syslog(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
 | 
			
		||||
/* 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(names = dm_task_get_names(dmt))) {
 | 
			
		||||
		r = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 _extend(struct dso_state *state)
 | 
			
		||||
{
 | 
			
		||||
#if THIN_DEBUG
 | 
			
		||||
	log_debug("dmeventd executes: %s.", state->cmd_str);
 | 
			
		||||
	syslog(LOG_INFO, "dmeventd executes: %s.\n", state->cmd_str);
 | 
			
		||||
#endif
 | 
			
		||||
	if (state->argv[0])
 | 
			
		||||
		return _run_command(state);
 | 
			
		||||
	return dmeventd_lvm2_run(state->cmd_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
static int _run(const char *cmd, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int argc = 1; /* for argv[0], i.e. cmd */
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	const char **argv;
 | 
			
		||||
	pid_t pid = fork();
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	if (pid == 0) { /* child */
 | 
			
		||||
		va_start(ap, cmd);
 | 
			
		||||
		while (va_arg(ap, const char *))
 | 
			
		||||
			++argc;
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
 | 
			
		||||
		/* + 1 for the terminating NULL */
 | 
			
		||||
		argv = alloca(sizeof(const char *) * (argc + 1));
 | 
			
		||||
 | 
			
		||||
		argv[0] = cmd;
 | 
			
		||||
                va_start(ap, cmd);
 | 
			
		||||
		while ((argv[++i] = va_arg(ap, const char *)));
 | 
			
		||||
		va_end(ap);
 | 
			
		||||
 | 
			
		||||
		execvp(cmd, (char **)argv);
 | 
			
		||||
		syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno));
 | 
			
		||||
		exit(127);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->fails = 0;
 | 
			
		||||
	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 {
 | 
			
		||||
	struct dm_info info;
 | 
			
		||||
	dm_bitset_t minors; /* Bitset for active thin pool minors */
 | 
			
		||||
	const char *device;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _umount_device(char *buffer, unsigned major, unsigned minor,
 | 
			
		||||
			  char *target, void *cb_data)
 | 
			
		||||
{
 | 
			
		||||
	struct mountinfo_s *data = cb_data;
 | 
			
		||||
 | 
			
		||||
	if ((major == data->info.major) && dm_bit(data->minors, minor)) {
 | 
			
		||||
		syslog(LOG_INFO, "Unmounting thin volume %s from %s.\n",
 | 
			
		||||
		       data->device, target);
 | 
			
		||||
		if (!_run(UMOUNT_COMMAND, "-fl", target, NULL))
 | 
			
		||||
			syslog(LOG_ERR, "Failed to umount thin %s from %s: %s.\n",
 | 
			
		||||
			       data->device, target, strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if executed command has finished
 | 
			
		||||
 * Only 1 command may run */
 | 
			
		||||
static int _wait_for_pid(struct dso_state *state)
 | 
			
		||||
/*
 | 
			
		||||
 * Find all thin pool users and try to umount them.
 | 
			
		||||
 * TODO: work with read-only thin pool support
 | 
			
		||||
 */
 | 
			
		||||
static void _umount(struct dm_task *dmt, const char *device)
 | 
			
		||||
{
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	/* TODO: Convert to use hash to reduce memory usage */
 | 
			
		||||
	static const size_t MINORS = (1U << 20); /* 20 bit */
 | 
			
		||||
	struct mountinfo_s data = {
 | 
			
		||||
		.device = device,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (state->pid == -1)
 | 
			
		||||
		return 1;
 | 
			
		||||
	if (!dm_task_get_info(dmt, &data.info))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!waitpid(state->pid, &status, WNOHANG))
 | 
			
		||||
		return 0;
 | 
			
		||||
	dmeventd_lvm2_unlock();
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	if (!(data.minors = dm_bitset_create(NULL, MINORS))) {
 | 
			
		||||
		syslog(LOG_ERR, "Failed to allocate bitset. Not unmounting %s.\n", device);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->pid = -1;
 | 
			
		||||
	if (!_find_all_devs(data.minors, data.info.major, data.info.minor)) {
 | 
			
		||||
		syslog(LOG_ERR, "Failed to detect mounted volumes for %s.\n", device);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
	if (!dm_mountinfo_read(_umount_device, &data)) {
 | 
			
		||||
		syslog(LOG_ERR, "Could not parse mountinfo file.\n");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (data.minors)
 | 
			
		||||
		dm_bitset_destroy(data.minors);
 | 
			
		||||
	dmeventd_lvm2_lock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void process_event(struct dm_task *dmt,
 | 
			
		||||
		   enum dm_event_mask event __attribute__((unused)),
 | 
			
		||||
		   void **user)
 | 
			
		||||
		   void **private)
 | 
			
		||||
{
 | 
			
		||||
	const char *device = dm_task_get_name(dmt);
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	int percent;
 | 
			
		||||
	struct dso_state *state = *private;
 | 
			
		||||
	struct dm_status_thin_pool *tps = NULL;
 | 
			
		||||
	void *next = NULL;
 | 
			
		||||
	uint64_t start, length;
 | 
			
		||||
	char *target_type = NULL;
 | 
			
		||||
	char *params;
 | 
			
		||||
	int needs_policy = 0;
 | 
			
		||||
	struct dm_task *new_dmt = NULL;
 | 
			
		||||
 | 
			
		||||
#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));
 | 
			
		||||
#endif
 | 
			
		||||
	if (!_wait_for_pid(state)) {
 | 
			
		||||
		log_warn("WARNING: Skipping event, child %d is still running (%s).",
 | 
			
		||||
			 state->pid, state->cmd_str);
 | 
			
		||||
#if 0
 | 
			
		||||
	/* No longer monitoring, waiting for remove */
 | 
			
		||||
	if (!state->meta_percent_check && !state->data_percent_check)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	dmeventd_lvm2_lock();
 | 
			
		||||
 | 
			
		||||
	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
 | 
			
		||||
 | 
			
		||||
	if (!target_type || (strcmp(target_type, "thin-pool") != 0)) {
 | 
			
		||||
		log_error("Invalid target type.");
 | 
			
		||||
		syslog(LOG_ERR, "Invalid target type.\n");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
 | 
			
		||||
		log_error("Failed to parse status.");
 | 
			
		||||
		syslog(LOG_ERR, "Failed to parse status.\n");
 | 
			
		||||
		_umount(dmt, device);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if THIN_DEBUG
 | 
			
		||||
	log_debug("Thin pool status " FMTu64 "/" FMTu64 "  "
 | 
			
		||||
		  FMTu64 "/" FMTu64 ".",
 | 
			
		||||
		  tps->used_metadata_blocks, tps->total_metadata_blocks,
 | 
			
		||||
		  tps->used_data_blocks, tps->total_data_blocks);
 | 
			
		||||
	syslog(LOG_INFO, "%p: Got status %" PRIu64 " / %" PRIu64
 | 
			
		||||
	       " %" PRIu64  " / %" PRIu64 ".\n", state,
 | 
			
		||||
	       tps->used_metadata_blocks, tps->total_metadata_blocks,
 | 
			
		||||
	       tps->used_data_blocks, tps->total_data_blocks);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Thin pool size had changed. Clear the threshold. */
 | 
			
		||||
	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 = 100 * 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;
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
			return;
 | 
			
		||||
		/* FIXME: extension of metadata needs to be written! */
 | 
			
		||||
		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
 | 
			
		||||
			syslog(LOG_WARNING, "Thin metadata %s is now %i%% full.\n",
 | 
			
		||||
			       device, percent);
 | 
			
		||||
		 /* Try to extend the metadata, in accord with user-set policies */
 | 
			
		||||
		if (!_extend(state)) {
 | 
			
		||||
			syslog(LOG_ERR, "Failed to extend thin metadata %s.\n",
 | 
			
		||||
			       device);
 | 
			
		||||
			_umount(dmt, device);
 | 
			
		||||
		}
 | 
			
		||||
		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 */
 | 
			
		||||
		/* FIXME: hmm READ-ONLY switch should happen in error path */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (needs_policy)
 | 
			
		||||
		_use_policy(dmt, state);
 | 
			
		||||
	percent = 100 * 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. */
 | 
			
		||||
			syslog(LOG_WARNING, "Thin %s is now %i%% full.\n", device, percent);
 | 
			
		||||
		/* Try to extend the thin data, in accord with user-set policies */
 | 
			
		||||
		if (!_extend(state)) {
 | 
			
		||||
			syslog(LOG_ERR, "Failed to extend thin %s.\n", device);
 | 
			
		||||
			state->data_percent_check = 0;
 | 
			
		||||
			_umount(dmt, device);
 | 
			
		||||
		}
 | 
			
		||||
		/* FIXME: hmm READ-ONLY switch should happen in error path */
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	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.");
 | 
			
		||||
	dmeventd_lvm2_unlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int register_device(const char *device,
 | 
			
		||||
		    const char *uuid __attribute__((unused)),
 | 
			
		||||
		    int major __attribute__((unused)),
 | 
			
		||||
		    int minor __attribute__((unused)),
 | 
			
		||||
		    void **user)
 | 
			
		||||
		    void **private)
 | 
			
		||||
{
 | 
			
		||||
	struct dm_pool *statemem = NULL;
 | 
			
		||||
	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_init())
 | 
			
		||||
		goto bad;
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str),
 | 
			
		||||
				   "_dmeventd_thin_command", device))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
	if (!(statemem = dm_pool_create("thin_pool_state", 2048)) ||
 | 
			
		||||
	    !(state = dm_pool_zalloc(statemem, sizeof(*state))) ||
 | 
			
		||||
	    !dmeventd_lvm2_command(statemem, state->cmd_str,
 | 
			
		||||
				   sizeof(state->cmd_str),
 | 
			
		||||
				   "lvextend --use-policies",
 | 
			
		||||
				   device)) {
 | 
			
		||||
		if (statemem)
 | 
			
		||||
			dm_pool_destroy(statemem);
 | 
			
		||||
		dmeventd_lvm2_exit();
 | 
			
		||||
		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;
 | 
			
		||||
		}
 | 
			
		||||
	state->mem = statemem;
 | 
			
		||||
	state->metadata_percent_check = CHECK_MINIMUM;
 | 
			
		||||
	state->data_percent_check = CHECK_MINIMUM;
 | 
			
		||||
	*private = state;
 | 
			
		||||
 | 
			
		||||
		/* 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;
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
	log_info("Monitoring thin pool %s.", device);
 | 
			
		||||
	syslog(LOG_INFO, "Monitoring thin %s.\n", 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);
 | 
			
		||||
	syslog(LOG_ERR, "Failed to monitor thin %s.\n", device);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -403,34 +389,13 @@ int unregister_device(const char *device,
 | 
			
		||||
		      const char *uuid __attribute__((unused)),
 | 
			
		||||
		      int major __attribute__((unused)),
 | 
			
		||||
		      int minor __attribute__((unused)),
 | 
			
		||||
		      void **user)
 | 
			
		||||
		      void **private)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct dso_state *state = *private;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	syslog(LOG_INFO, "No longer monitoring thin %s.\n", device);
 | 
			
		||||
	dm_pool_destroy(state->mem);
 | 
			
		||||
	dmeventd_lvm2_exit();
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
process_event
 | 
			
		||||
register_device
 | 
			
		||||
unregister_device
 | 
			
		||||
@@ -1,36 +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 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@
 | 
			
		||||
 | 
			
		||||
INCLUDES += -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
 | 
			
		||||
 | 
			
		||||
SOURCES = dmeventd_vdo.c
 | 
			
		||||
 | 
			
		||||
LIB_NAME = libdevmapper-event-lvm2vdo
 | 
			
		||||
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 $(INTERNAL_LIBS)
 | 
			
		||||
 | 
			
		||||
install_lvm2: install_dm_plugin
 | 
			
		||||
 | 
			
		||||
install: install_lvm2
 | 
			
		||||
@@ -1,419 +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.h"
 | 
			
		||||
#include "dmeventd_lvm.h"
 | 
			
		||||
#include "libdevmapper-event.h"
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct vdo_status {
 | 
			
		||||
	uint64_t used_blocks;
 | 
			
		||||
	uint64_t total_blocks;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _vdo_status_parse(const char *params, struct vdo_status *status)
 | 
			
		||||
{
 | 
			
		||||
	if (sscanf(params, "%*s %*s %*s %*s %*s %" PRIu64 " %" PRIu64,
 | 
			
		||||
		   &status->used_blocks,
 | 
			
		||||
		   &status->total_blocks) < 2) {
 | 
			
		||||
		log_error("Failed to parse vdo params: %s.", params);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 vdo_status status;
 | 
			
		||||
 | 
			
		||||
#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 (!_vdo_status_parse(params, &status)) {
 | 
			
		||||
		log_error("Failed to parse status.");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->percent = dm_make_percent(status.used_blocks,
 | 
			
		||||
					 status.total_blocks);
 | 
			
		||||
 | 
			
		||||
#if VDO_DEBUG
 | 
			
		||||
	log_debug("VDO %s status  %.2f%% " FMTu64 "/" FMTu64 ".",
 | 
			
		||||
		  state->name, dm_percent_to_round_float(state->percent, 2),
 | 
			
		||||
		  status.used_blocks, status.total_blocks);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* VDO pool size had changed. Clear the threshold. */
 | 
			
		||||
	if (state->known_data_size != status.total_blocks) {
 | 
			
		||||
		state->percent_check = CHECK_MINIMUM;
 | 
			
		||||
		state->known_data_size = 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);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		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 */
 | 
			
		||||
 | 
			
		||||
	/* FIXME: ATM nothing can be done, drop 0, once it becomes useful */
 | 
			
		||||
	if (0 && needs_policy)
 | 
			
		||||
		_use_policy(dmt, state);
 | 
			
		||||
out:
 | 
			
		||||
	if (new_dmt)
 | 
			
		||||
		dm_task_destroy(new_dmt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle SIGCHLD for a thread */
 | 
			
		||||
static void _sig_child(int signum __attribute__((unused)))
 | 
			
		||||
{
 | 
			
		||||
	/* empty SIG_IGN */;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Setup handler for SIGCHLD when executing external command
 | 
			
		||||
 * to get quick 'waitpid()' reaction
 | 
			
		||||
 * It will interrupt syscall just like SIGALRM and
 | 
			
		||||
 * invoke process_event().
 | 
			
		||||
 */
 | 
			
		||||
static void _init_thread_signals(struct dso_state *state)
 | 
			
		||||
{
 | 
			
		||||
	struct sigaction act = { .sa_handler = _sig_child };
 | 
			
		||||
	sigset_t my_sigset;
 | 
			
		||||
 | 
			
		||||
	sigemptyset(&my_sigset);
 | 
			
		||||
 | 
			
		||||
	if (sigaction(SIGCHLD, &act, NULL))
 | 
			
		||||
		log_warn("WARNING: Failed to set SIGCHLD action.");
 | 
			
		||||
	else if (sigaddset(&my_sigset, SIGCHLD))
 | 
			
		||||
		log_warn("WARNING: Failed to add SIGCHLD to set.");
 | 
			
		||||
	else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
 | 
			
		||||
		log_warn("WARNING: Failed to unblock SIGCHLD.");
 | 
			
		||||
	else
 | 
			
		||||
		state->restore_sigset = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _restore_thread_signals(struct dso_state *state)
 | 
			
		||||
{
 | 
			
		||||
	if (state->restore_sigset &&
 | 
			
		||||
	    pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
 | 
			
		||||
		log_warn("WARNING: Failed to block SIGCHLD.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int register_device(const char *device,
 | 
			
		||||
		    const char *uuid,
 | 
			
		||||
		    int major __attribute__((unused)),
 | 
			
		||||
		    int minor __attribute__((unused)),
 | 
			
		||||
		    void **user)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state;
 | 
			
		||||
	const char *cmd;
 | 
			
		||||
	char *str;
 | 
			
		||||
	char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
 | 
			
		||||
        const char *name = "pool";
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_init_with_pool("vdo_pool_state", state))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	state->cmd_str = "";
 | 
			
		||||
 | 
			
		||||
	/* Search for command for LVM- prefixed devices only */
 | 
			
		||||
	cmd = (strncmp(uuid, "LVM-", 4) == 0) ? "_dmeventd_vdo_command" : "";
 | 
			
		||||
 | 
			
		||||
	if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str), cmd, device))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	if (strncmp(cmd_str, "lvm ", 4) == 0) {
 | 
			
		||||
		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
 | 
			
		||||
			log_error("Failed to copy lvm VDO command.");
 | 
			
		||||
				goto bad;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (cmd_str[0] == '/') {
 | 
			
		||||
		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
 | 
			
		||||
			log_error("Failed to copy VDO command.");
 | 
			
		||||
			goto bad;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Find last space before 'vg/lv' */
 | 
			
		||||
		if (!(str = strrchr(state->cmd_str, ' ')))
 | 
			
		||||
			goto inval;
 | 
			
		||||
 | 
			
		||||
		if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str,
 | 
			
		||||
						       str - state->cmd_str))) {
 | 
			
		||||
			log_error("Failed to copy command.");
 | 
			
		||||
			goto bad;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		state->argv[1] = str + 1;  /* 1 argument - vg/lv */
 | 
			
		||||
		_init_thread_signals(state);
 | 
			
		||||
	} else if (cmd[0] == 0) {
 | 
			
		||||
		state->name = "volume"; /* What to use with 'others?' */
 | 
			
		||||
	} else/* Unuspported command format */
 | 
			
		||||
		goto inval;
 | 
			
		||||
 | 
			
		||||
	state->pid = -1;
 | 
			
		||||
	state->name = name;
 | 
			
		||||
	*user = state;
 | 
			
		||||
 | 
			
		||||
	log_info("Monitoring VDO %s %s.", name, device);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
inval:
 | 
			
		||||
	log_error("Invalid command for monitoring: %s.", cmd_str);
 | 
			
		||||
bad:
 | 
			
		||||
	log_error("Failed to monitor VDO %s %s.", name, device);
 | 
			
		||||
 | 
			
		||||
	if (state)
 | 
			
		||||
		dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int unregister_device(const char *device,
 | 
			
		||||
		      const char *uuid __attribute__((unused)),
 | 
			
		||||
		      int major __attribute__((unused)),
 | 
			
		||||
		      int minor __attribute__((unused)),
 | 
			
		||||
		      void **user)
 | 
			
		||||
{
 | 
			
		||||
	struct dso_state *state = *user;
 | 
			
		||||
	const char *name = state->name;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
 | 
			
		||||
		if (i == 0)
 | 
			
		||||
			/* Give it 2 seconds, then try to terminate & kill it */
 | 
			
		||||
			log_verbose("Child %d still not finished (%s) waiting.",
 | 
			
		||||
				    state->pid, state->cmd_str);
 | 
			
		||||
		else if (i == 3) {
 | 
			
		||||
			log_warn("WARNING: Terminating child %d.", state->pid);
 | 
			
		||||
			kill(state->pid, SIGINT);
 | 
			
		||||
			kill(state->pid, SIGTERM);
 | 
			
		||||
		} else if (i == 5) {
 | 
			
		||||
			log_warn("WARNING: Killing child %d.", state->pid);
 | 
			
		||||
			kill(state->pid, SIGKILL);
 | 
			
		||||
		}
 | 
			
		||||
		sleep(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (state->pid != -1)
 | 
			
		||||
		log_warn("WARNING: Cannot kill child %d!", state->pid);
 | 
			
		||||
 | 
			
		||||
	_restore_thread_signals(state);
 | 
			
		||||
 | 
			
		||||
	dmeventd_lvm2_exit_with_pool(state);
 | 
			
		||||
	log_info("No longer monitoring VDO %s %s.", name, device);
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								daemons/dmfilemapd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								daemons/dmfilemapd/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
dmfilemapd
 | 
			
		||||
@@ -1,66 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of the device-mapper userspace tools.
 | 
			
		||||
#
 | 
			
		||||
# This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
# modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
# of the GNU Lesser General Public License v.2.1.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
# along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 | 
			
		||||
srcdir = @srcdir@
 | 
			
		||||
top_srcdir = @top_srcdir@
 | 
			
		||||
top_builddir = @top_builddir@
 | 
			
		||||
 | 
			
		||||
SOURCES = dmfilemapd.c
 | 
			
		||||
 | 
			
		||||
TARGETS = dmfilemapd
 | 
			
		||||
 | 
			
		||||
.PHONY: install_dmfilemapd install_dmfilemapd_static
 | 
			
		||||
 | 
			
		||||
INSTALL_DMFILEMAPD_TARGETS = install_dmfilemapd_dynamic
 | 
			
		||||
 | 
			
		||||
CLEAN_TARGETS = dmfilemapd.static
 | 
			
		||||
 | 
			
		||||
CFLOW_LIST = $(SOURCES)
 | 
			
		||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
 | 
			
		||||
CFLOW_TARGET = dmfilemapd
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
all: device-mapper
 | 
			
		||||
device-mapper: $(TARGETS)
 | 
			
		||||
 | 
			
		||||
CFLAGS_dmfilemapd.o += $(EXTRA_EXEC_CFLAGS)
 | 
			
		||||
LIBS += -ldevmapper
 | 
			
		||||
 | 
			
		||||
dmfilemapd: $(LIB_SHARED) dmfilemapd.o
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
 | 
			
		||||
		-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS)
 | 
			
		||||
 | 
			
		||||
dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o $(interfacebuilddir)/libdevmapper.a
 | 
			
		||||
	$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L$(interfacebuilddir) \
 | 
			
		||||
		-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS) $(STATIC_LIBS)
 | 
			
		||||
 | 
			
		||||
ifneq ("$(CFLOW_CMD)", "")
 | 
			
		||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
 | 
			
		||||
-include $(top_builddir)/libdm/libdevmapper.cflow
 | 
			
		||||
-include $(top_builddir)/lib/liblvm-internal.cflow
 | 
			
		||||
-include $(top_builddir)/lib/liblvm2cmd.cflow
 | 
			
		||||
-include $(top_builddir)/daemons/dmfilemapd/$(LIB_NAME).cflow
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
install_dmfilemapd_dynamic: dmfilemapd
 | 
			
		||||
	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
 | 
			
		||||
 | 
			
		||||
install_dmfilemapd_static: dmfilemapd.static
 | 
			
		||||
	$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
 | 
			
		||||
 | 
			
		||||
install_dmfilemapd: $(INSTALL_DMFILEMAPD_TARGETS)
 | 
			
		||||
 | 
			
		||||
install: install_dmfilemapd
 | 
			
		||||
 | 
			
		||||
install_device-mapper: install_dmfilemapd
 | 
			
		||||
@@ -1,836 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2016 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the device-mapper userspace tools.
 | 
			
		||||
 *
 | 
			
		||||
 * It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
 | 
			
		||||
 *
 | 
			
		||||
 * This copyrighted material is made available to anyone wishing to use,
 | 
			
		||||
 * modify, copy, or redistribute it subject to the terms and conditions
 | 
			
		||||
 * of the GNU General Public License v.2.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software Foundation,
 | 
			
		||||
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "tool.h"
 | 
			
		||||
 | 
			
		||||
#include "dm-logging.h"
 | 
			
		||||
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <sys/inotify.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#  include "kdev_t.h"
 | 
			
		||||
#else
 | 
			
		||||
#  define MAJOR(x) major((x))
 | 
			
		||||
#  define MINOR(x) minor((x))
 | 
			
		||||
#  define MKDEV(x,y) makedev((dev_t)(x),(dev_t)(y))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* limit to two updates/sec */
 | 
			
		||||
#define FILEMAPD_WAIT_USECS 500000
 | 
			
		||||
 | 
			
		||||
/* how long to wait for unlinked files */
 | 
			
		||||
#define FILEMAPD_NOFILE_WAIT_USECS 100000
 | 
			
		||||
#define FILEMAPD_NOFILE_WAIT_TRIES 10
 | 
			
		||||
 | 
			
		||||
struct filemap_monitor {
 | 
			
		||||
	dm_filemapd_mode_t mode;
 | 
			
		||||
	const char *program_id;
 | 
			
		||||
	uint64_t group_id;
 | 
			
		||||
	char *path;
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	int inotify_fd;
 | 
			
		||||
	int inotify_watch_fd;
 | 
			
		||||
 | 
			
		||||
	/* monitoring heuristics */
 | 
			
		||||
	int64_t blocks; /* allocated blocks, from stat.st_blocks */
 | 
			
		||||
	uint64_t nr_regions;
 | 
			
		||||
	int deleted;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _foreground;
 | 
			
		||||
static int _verbose;
 | 
			
		||||
 | 
			
		||||
const char *const _usage = "dmfilemapd <fd> <group_id> <abs_path> <mode> "
 | 
			
		||||
			   "[<foreground>[<log_level>]]";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Daemon logging. By default, all messages are thrown away: messages
 | 
			
		||||
 * are only written to the terminal if the daemon is run in the foreground.
 | 
			
		||||
 */
 | 
			
		||||
__attribute__((format(printf, 5, 0)))
 | 
			
		||||
static void _dmfilemapd_log_line(int level,
 | 
			
		||||
				 const char *file __attribute__((unused)),
 | 
			
		||||
				 int line __attribute__((unused)),
 | 
			
		||||
				 int dm_errno_or_class,
 | 
			
		||||
				 const char *f, va_list ap)
 | 
			
		||||
{
 | 
			
		||||
	static int _abort_on_internal_errors = -1;
 | 
			
		||||
	FILE *out = log_stderr(level) ? stderr : stdout;
 | 
			
		||||
 | 
			
		||||
	level = log_level(level);
 | 
			
		||||
 | 
			
		||||
	if (level <= _LOG_WARN || _verbose) {
 | 
			
		||||
		if (level < _LOG_WARN)
 | 
			
		||||
			out = stderr;
 | 
			
		||||
		vfprintf(out, f, ap);
 | 
			
		||||
		fputc('\n', out);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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(f, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
 | 
			
		||||
		abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((format(printf, 5, 6)))
 | 
			
		||||
static void _dmfilemapd_log_with_errno(int level,
 | 
			
		||||
				       const char *file, int line,
 | 
			
		||||
				       int dm_errno_or_class,
 | 
			
		||||
				       const char *f, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, f);
 | 
			
		||||
	_dmfilemapd_log_line(level, file, line, dm_errno_or_class, f, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Only used for reporting errors before daemonise().
 | 
			
		||||
 */
 | 
			
		||||
__attribute__((format(printf, 1, 2)))
 | 
			
		||||
static void _early_log(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	vfprintf(stderr, fmt, ap);
 | 
			
		||||
	fputc('\n', stderr);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _setup_logging(void)
 | 
			
		||||
{
 | 
			
		||||
	dm_log_init_verbose(_verbose - 1);
 | 
			
		||||
	dm_log_with_errno_init(_dmfilemapd_log_with_errno);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define PROC_FD_DELETED_STR "(deleted)"
 | 
			
		||||
/*
 | 
			
		||||
 * Scan the /proc/<pid>/fd directory for pid and check for an fd
 | 
			
		||||
 * symlink whose contents match path.
 | 
			
		||||
 */
 | 
			
		||||
static int _is_open_in_pid(pid_t pid, const char *path)
 | 
			
		||||
{
 | 
			
		||||
	char deleted_path[PATH_MAX + sizeof(PROC_FD_DELETED_STR)];
 | 
			
		||||
	struct dirent *pid_dp = NULL;
 | 
			
		||||
	char path_buf[PATH_MAX];
 | 
			
		||||
	char link_buf[PATH_MAX];
 | 
			
		||||
	DIR *pid_d = NULL;
 | 
			
		||||
	ssize_t len;
 | 
			
		||||
 | 
			
		||||
	if (pid == getpid())
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (dm_snprintf(path_buf, sizeof(path_buf),
 | 
			
		||||
			DEFAULT_PROC_DIR "%d/fd", pid) < 0) {
 | 
			
		||||
		log_error("Could not format pid path.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Test for the kernel 'file (deleted)' form when scanning.
 | 
			
		||||
	 */
 | 
			
		||||
	if (dm_snprintf(deleted_path, sizeof(deleted_path), "%s %s",
 | 
			
		||||
			path, PROC_FD_DELETED_STR) < 0) {
 | 
			
		||||
		log_error("Could not format check path.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pid_d = opendir(path_buf);
 | 
			
		||||
	if (!pid_d) {
 | 
			
		||||
		log_error("Could not open proc path: %s.", path_buf);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ((pid_dp = readdir(pid_d)) != NULL) {
 | 
			
		||||
		if (pid_dp->d_name[0] == '.')
 | 
			
		||||
			continue;
 | 
			
		||||
		if ((len = readlinkat(dirfd(pid_d), pid_dp->d_name, link_buf,
 | 
			
		||||
				      sizeof(link_buf))) < 0) {
 | 
			
		||||
			log_error("readlink failed for " DEFAULT_PROC_DIR
 | 
			
		||||
				  "/%d/fd/.", pid);
 | 
			
		||||
			goto bad;
 | 
			
		||||
		}
 | 
			
		||||
		link_buf[len] = '\0';
 | 
			
		||||
		if (!strcmp(deleted_path, link_buf)) {
 | 
			
		||||
			if (closedir(pid_d))
 | 
			
		||||
				log_sys_error("closedir", path_buf);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
bad:
 | 
			
		||||
	if (closedir(pid_d))
 | 
			
		||||
		log_sys_error("closedir", path_buf);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attempt to determine whether a file is open by any process by
 | 
			
		||||
 * scanning symbolic links in /proc/<pid>/fd.
 | 
			
		||||
 *
 | 
			
		||||
 * This is a heuristic since it cannot guarantee to detect brief
 | 
			
		||||
 * access in all cases: a process that opens and then closes the
 | 
			
		||||
 * file rapidly may never be seen by the scan.
 | 
			
		||||
 *
 | 
			
		||||
 * The method will also give false-positives if a process exists
 | 
			
		||||
 * that has a deleted file open that had the same path, but a
 | 
			
		||||
 * different inode number, to the file being monitored.
 | 
			
		||||
 *
 | 
			
		||||
 * For this reason the daemon only uses _is_open() for unlinked
 | 
			
		||||
 * files when the mode is DM_FILEMAPD_FOLLOW_INODE, since these
 | 
			
		||||
 * files can no longer be newly opened by processes.
 | 
			
		||||
 *
 | 
			
		||||
 * In this situation !is_open(path) provides an indication that
 | 
			
		||||
 * the daemon should shut down: the file has been unlinked from
 | 
			
		||||
 * the file system and we appear to hold the final reference.
 | 
			
		||||
 */
 | 
			
		||||
static int _is_open(const char *path)
 | 
			
		||||
{
 | 
			
		||||
	struct dirent *proc_dp = NULL;
 | 
			
		||||
	DIR *proc_d = NULL;
 | 
			
		||||
	pid_t pid;
 | 
			
		||||
 | 
			
		||||
	proc_d = opendir(DEFAULT_PROC_DIR);
 | 
			
		||||
	if (!proc_d)
 | 
			
		||||
		return 0;
 | 
			
		||||
	while ((proc_dp = readdir(proc_d)) != NULL) {
 | 
			
		||||
		if (!isdigit(proc_dp->d_name[0]))
 | 
			
		||||
			continue;
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		pid = (pid_t) strtol(proc_dp->d_name, NULL, 10);
 | 
			
		||||
		if (errno || !pid)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (_is_open_in_pid(pid, path)) {
 | 
			
		||||
			if (closedir(proc_d))
 | 
			
		||||
				log_sys_error("closedir", DEFAULT_PROC_DIR);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (closedir(proc_d))
 | 
			
		||||
		log_sys_error("closedir", DEFAULT_PROC_DIR);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _filemap_monitor_wait(uint64_t usecs)
 | 
			
		||||
{
 | 
			
		||||
	if (_verbose) {
 | 
			
		||||
		if (usecs == FILEMAPD_WAIT_USECS)
 | 
			
		||||
			log_very_verbose("Waiting for check interval");
 | 
			
		||||
		if (usecs == FILEMAPD_NOFILE_WAIT_USECS)
 | 
			
		||||
			log_very_verbose("Waiting for unlinked path");
 | 
			
		||||
	}
 | 
			
		||||
	usleep((useconds_t) usecs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _parse_args(int argc, char **argv, struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	char *endptr;
 | 
			
		||||
 | 
			
		||||
	/* we don't care what is in argv[0]. */
 | 
			
		||||
	argc--;
 | 
			
		||||
	argv++;
 | 
			
		||||
 | 
			
		||||
	if (argc < 5) {
 | 
			
		||||
		_early_log("Wrong number of arguments.");
 | 
			
		||||
		_early_log("usage: %s", _usage);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We don't know the true nr_regions at daemon start time,
 | 
			
		||||
	 * and it is not worth a dm_stats_list()/group walk to count:
 | 
			
		||||
	 * we can assume that there is at least one region or the
 | 
			
		||||
	 * daemon would not have been started.
 | 
			
		||||
	 *
 | 
			
		||||
	 * A correct value will be obtained following the first update
 | 
			
		||||
	 * of the group's regions.
 | 
			
		||||
	 */
 | 
			
		||||
	fm->nr_regions = 1;
 | 
			
		||||
 | 
			
		||||
	/* parse <fd> */
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	fm->fd = (int) strtol(argv[0], &endptr, 10);
 | 
			
		||||
	if (errno || *endptr) {
 | 
			
		||||
		_early_log("Could not parse file descriptor: %s", argv[0]);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argc--;
 | 
			
		||||
	argv++;
 | 
			
		||||
 | 
			
		||||
	/* parse <group_id> */
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	fm->group_id = strtoull(argv[0], &endptr, 10);
 | 
			
		||||
	if (*endptr || errno) {
 | 
			
		||||
		_early_log("Could not parse group identifier: %s", argv[0]);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argc--;
 | 
			
		||||
	argv++;
 | 
			
		||||
 | 
			
		||||
	/* parse <path> */
 | 
			
		||||
	if (!argv[0] || !strlen(argv[0])) {
 | 
			
		||||
		_early_log("Path argument is required.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (*argv[0] != '/') {
 | 
			
		||||
		_early_log("Path argument must specify an absolute path.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fm->path = dm_strdup(argv[0]);
 | 
			
		||||
	if (!fm->path) {
 | 
			
		||||
		_early_log("Could not allocate memory for path argument.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argc--;
 | 
			
		||||
	argv++;
 | 
			
		||||
 | 
			
		||||
	/* parse <mode> */
 | 
			
		||||
	if (!argv[0] || !strlen(argv[0])) {
 | 
			
		||||
		_early_log("Mode argument is required.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fm->mode = dm_filemapd_mode_from_string(argv[0]);
 | 
			
		||||
	if (fm->mode == DM_FILEMAPD_FOLLOW_NONE)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	argc--;
 | 
			
		||||
	argv++;
 | 
			
		||||
 | 
			
		||||
	/* parse [<foreground>[<verbose>]] */
 | 
			
		||||
	if (argc) {
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		_foreground = (int) strtol(argv[0], &endptr, 10);
 | 
			
		||||
		if (errno || *endptr) {
 | 
			
		||||
			_early_log("Could not parse debug argument: %s.",
 | 
			
		||||
				   argv[0]);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		argc--;
 | 
			
		||||
		argv++;
 | 
			
		||||
		if (argc) {
 | 
			
		||||
			errno = 0;
 | 
			
		||||
			_verbose = (int) strtol(argv[0], &endptr, 10);
 | 
			
		||||
			if (errno || *endptr) {
 | 
			
		||||
				_early_log("Could not parse verbose "
 | 
			
		||||
					   "argument: %s", argv[0]);
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
			if (_verbose < 0 || _verbose > 3) {
 | 
			
		||||
				_early_log("Verbose argument out of range: %d.",
 | 
			
		||||
					   _verbose);
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_fd_update_blocks(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	struct stat buf;
 | 
			
		||||
 | 
			
		||||
	if (fm->fd < 0) {
 | 
			
		||||
		log_error("Filemap fd is not open.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fstat(fm->fd, &buf)) {
 | 
			
		||||
		log_error("Failed to fstat filemap file descriptor.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fm->blocks = buf.st_blocks;
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_fd_check_changed(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	int64_t old_blocks;
 | 
			
		||||
 | 
			
		||||
	old_blocks = fm->blocks;
 | 
			
		||||
 | 
			
		||||
	if (!_filemap_fd_update_blocks(fm))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	return (fm->blocks != old_blocks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _filemap_monitor_close_fd(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	if (close(fm->fd))
 | 
			
		||||
		log_error("Error closing file descriptor.");
 | 
			
		||||
	fm->fd = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _filemap_monitor_end_notify(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	inotify_rm_watch(fm->inotify_fd, fm->inotify_watch_fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_monitor_set_notify(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	int inotify_fd, watch_fd;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set IN_NONBLOCK since we do not want to block in event read()
 | 
			
		||||
	 * calls. Do not set IN_CLOEXEC as dmfilemapd is single-threaded
 | 
			
		||||
	 * and does not fork or exec.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((inotify_fd = inotify_init1(IN_NONBLOCK)) < 0) {
 | 
			
		||||
		log_sys_error("inotify_init1", "IN_NONBLOCK");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((watch_fd = inotify_add_watch(inotify_fd, fm->path,
 | 
			
		||||
					  IN_MODIFY | IN_DELETE_SELF)) < 0) {
 | 
			
		||||
		log_sys_error("inotify_add_watch", fm->path);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	fm->inotify_fd = inotify_fd;
 | 
			
		||||
	fm->inotify_watch_fd = watch_fd;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_monitor_reopen_fd(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	int tries = FILEMAPD_NOFILE_WAIT_TRIES;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * In DM_FILEMAPD_FOLLOW_PATH mode, inotify watches must be
 | 
			
		||||
	 * re-established whenever the file at the watched path is
 | 
			
		||||
	 * changed.
 | 
			
		||||
	 *
 | 
			
		||||
	 * FIXME: stat file and skip if inode is unchanged.
 | 
			
		||||
	 */
 | 
			
		||||
	if (fm->fd > 0)
 | 
			
		||||
		log_error("Filemap file descriptor already open.");
 | 
			
		||||
 | 
			
		||||
	while ((fm->fd < 0) && --tries)
 | 
			
		||||
		if (((fm->fd = open(fm->path, O_RDONLY)) < 0) && tries)
 | 
			
		||||
			_filemap_monitor_wait(FILEMAPD_NOFILE_WAIT_USECS);
 | 
			
		||||
 | 
			
		||||
	if (!tries && (fm->fd < 0)) {
 | 
			
		||||
		log_error("Could not re-open file descriptor.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return _filemap_monitor_set_notify(fm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_monitor_get_events(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	/* alignment as per man(7) inotify */
 | 
			
		||||
	char buf[sizeof(struct inotify_event) + NAME_MAX + 1]
 | 
			
		||||
		__attribute__ ((aligned(__alignof__(struct inotify_event))));
 | 
			
		||||
 | 
			
		||||
	struct inotify_event *event;
 | 
			
		||||
	int check = 0;
 | 
			
		||||
	ssize_t len;
 | 
			
		||||
	char *ptr;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Close the file descriptor for the file being monitored here
 | 
			
		||||
	 * when mode=path: this will allow the inode to be de-allocated,
 | 
			
		||||
	 * and an IN_DELETE_SELF event generated in the case that the
 | 
			
		||||
	 * daemon is holding the last open reference to the file.
 | 
			
		||||
	 */
 | 
			
		||||
	if (fm->mode == DM_FILEMAPD_FOLLOW_PATH) {
 | 
			
		||||
		_filemap_monitor_end_notify(fm);
 | 
			
		||||
		_filemap_monitor_close_fd(fm);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len = read(fm->inotify_fd, (void *) &buf, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
	/* no events to read? */
 | 
			
		||||
	if (len < 0 && (errno == EAGAIN))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* interrupted by signal? */
 | 
			
		||||
	if (len < 0 && (errno == EINTR))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (len < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (!len)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	for (ptr = buf; ptr < buf + len; ptr += sizeof(*event) + event->len) {
 | 
			
		||||
		event = (struct inotify_event *) ptr;
 | 
			
		||||
		if (event->mask & IN_DELETE_SELF)
 | 
			
		||||
			fm->deleted = 1;
 | 
			
		||||
		if (event->mask & IN_MODIFY)
 | 
			
		||||
			check = 1;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Event IN_IGNORED is generated when a file has been deleted
 | 
			
		||||
		 * and IN_DELETE_SELF generated, and indicates that the file
 | 
			
		||||
		 * watch has been automatically removed.
 | 
			
		||||
		 *
 | 
			
		||||
		 * This can only happen for the DM_FILEMAPD_FOLLOW_PATH mode,
 | 
			
		||||
		 * since inotify IN_DELETE events are generated at the time
 | 
			
		||||
		 * the inode is destroyed: DM_FILEMAPD_FOLLOW_INODE will hold
 | 
			
		||||
		 * the file descriptor open, meaning that the event will not
 | 
			
		||||
		 * be generated until after the daemon closes the file.
 | 
			
		||||
		 *
 | 
			
		||||
		 * The event is ignored here since inotify monitoring will
 | 
			
		||||
		 * be reestablished (or the daemon will terminate) following
 | 
			
		||||
		 * deletion of a DM_FILEMAPD_FOLLOW_PATH monitored file.
 | 
			
		||||
		 */
 | 
			
		||||
		if (event->mask & IN_IGNORED)
 | 
			
		||||
			log_very_verbose("Inotify watch removed: IN_IGNORED "
 | 
			
		||||
					 "in event->mask");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	/*
 | 
			
		||||
	 * Re-open file descriptor if required and log disposition.
 | 
			
		||||
	 */
 | 
			
		||||
	if (fm->mode == DM_FILEMAPD_FOLLOW_PATH)
 | 
			
		||||
		if (!_filemap_monitor_reopen_fd(fm))
 | 
			
		||||
			return -1;
 | 
			
		||||
 | 
			
		||||
	log_very_verbose("exiting _filemap_monitor_get_events() with "
 | 
			
		||||
			 "deleted=%d, check=%d", fm->deleted, check);
 | 
			
		||||
	return check;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _filemap_monitor_destroy(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	if (fm->fd > 0) {
 | 
			
		||||
		_filemap_monitor_end_notify(fm);
 | 
			
		||||
		_filemap_monitor_close_fd(fm);
 | 
			
		||||
	}
 | 
			
		||||
	dm_free((void *) fm->program_id);
 | 
			
		||||
	dm_free(fm->path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_monitor_check_same_file(int fd1, int fd2)
 | 
			
		||||
{
 | 
			
		||||
	struct stat buf1, buf2;
 | 
			
		||||
 | 
			
		||||
	if ((fd1 < 0) || (fd2 < 0))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (fstat(fd1, &buf1)) {
 | 
			
		||||
		log_error("Failed to fstat file descriptor %d", fd1);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fstat(fd2, &buf2)) {
 | 
			
		||||
		log_error("Failed to fstat file descriptor %d", fd2);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _filemap_monitor_check_file_unlinked(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	char path_buf[PATH_MAX];
 | 
			
		||||
	char link_buf[PATH_MAX];
 | 
			
		||||
	int same, fd;
 | 
			
		||||
	ssize_t len;
 | 
			
		||||
 | 
			
		||||
	fm->deleted = 0;
 | 
			
		||||
	same = 0;
 | 
			
		||||
 | 
			
		||||
	if ((fd = open(fm->path, O_RDONLY)) < 0)
 | 
			
		||||
		goto check_unlinked;
 | 
			
		||||
 | 
			
		||||
	same = _filemap_monitor_check_same_file(fm->fd, fd);
 | 
			
		||||
 | 
			
		||||
	if (close(fd))
 | 
			
		||||
		log_error("Error closing fd %d", fd);
 | 
			
		||||
 | 
			
		||||
	if (same < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (same)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
check_unlinked:
 | 
			
		||||
	/*
 | 
			
		||||
	 * The file has been unlinked from its original location: test
 | 
			
		||||
	 * whether it is still reachable in the filesystem, or if it is
 | 
			
		||||
	 * unlinked and anonymous.
 | 
			
		||||
	 */
 | 
			
		||||
	if (dm_snprintf(path_buf, sizeof(path_buf), DEFAULT_PROC_DIR
 | 
			
		||||
			"/%d/fd/%d", getpid(), fm->fd) < 0) {
 | 
			
		||||
		log_error("Could not format pid path.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if ((len = readlink(path_buf, link_buf, sizeof(link_buf) - 1)) < 0) {
 | 
			
		||||
		log_error("readlink failed for " DEFAULT_PROC_DIR "/%d/fd/%d.",
 | 
			
		||||
			  getpid(), fm->fd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	link_buf[len] = '\0';
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Try to re-open the file, from the path now reported in /proc/pid/fd.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((fd = open(link_buf, O_RDONLY)) < 0)
 | 
			
		||||
		fm->deleted = 1;
 | 
			
		||||
	else
 | 
			
		||||
		same = _filemap_monitor_check_same_file(fm->fd, fd);
 | 
			
		||||
 | 
			
		||||
	if ((fd >= 0) && close(fd))
 | 
			
		||||
		log_error("Error closing fd %d", fd);
 | 
			
		||||
 | 
			
		||||
	if (same < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Should not happen with normal /proc. */
 | 
			
		||||
	if ((fd > 0) && !same) {
 | 
			
		||||
		log_error("File descriptor mismatch: %d and %s (read from %s) "
 | 
			
		||||
			  "are not the same file!", fm->fd, link_buf, path_buf);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _daemonise(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	pid_t pid = 0;
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	if (!setsid()) {
 | 
			
		||||
		_early_log("setsid failed.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((pid = fork()) < 0) {
 | 
			
		||||
		_early_log("Failed to fork daemon process.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pid > 0) {
 | 
			
		||||
		if (_verbose)
 | 
			
		||||
			_early_log("Started dmfilemapd with pid=%d", pid);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (chdir("/")) {
 | 
			
		||||
		_early_log("Failed to change directory.");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_verbose) {
 | 
			
		||||
		if (close(STDIN_FILENO))
 | 
			
		||||
			_early_log("Error closing stdin");
 | 
			
		||||
		if (close(STDOUT_FILENO))
 | 
			
		||||
			_early_log("Error closing stdout");
 | 
			
		||||
		if (close(STDERR_FILENO))
 | 
			
		||||
			_early_log("Error closing stderr");
 | 
			
		||||
		if ((open("/dev/null", O_RDONLY) < 0) ||
 | 
			
		||||
	            (open("/dev/null", O_WRONLY) < 0) ||
 | 
			
		||||
		    (open("/dev/null", O_WRONLY) < 0)) {
 | 
			
		||||
			_early_log("Error opening stdio streams.");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* TODO: Use libdaemon/server/daemon-server.c _daemonise() */
 | 
			
		||||
	for (fd = (int) sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; fd--) {
 | 
			
		||||
		if (fd == fm->fd)
 | 
			
		||||
			continue;
 | 
			
		||||
		(void) close(fd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _update_regions(struct dm_stats *dms, struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	uint64_t *regions = NULL, *region, nr_regions = 0;
 | 
			
		||||
 | 
			
		||||
	regions = dm_stats_update_regions_from_fd(dms, fm->fd, fm->group_id);
 | 
			
		||||
	if (!regions) {
 | 
			
		||||
		log_error("Failed to update filemap regions for group_id="
 | 
			
		||||
			  FMTu64 ".", fm->group_id);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
 | 
			
		||||
		nr_regions++;
 | 
			
		||||
 | 
			
		||||
	if (!nr_regions)
 | 
			
		||||
		log_warn("File contains no extents: exiting.");
 | 
			
		||||
 | 
			
		||||
	if (nr_regions && (regions[0] != fm->group_id)) {
 | 
			
		||||
		log_warn("group_id changed from " FMTu64 " to " FMTu64,
 | 
			
		||||
			 fm->group_id, regions[0]);
 | 
			
		||||
		fm->group_id = regions[0];
 | 
			
		||||
	}
 | 
			
		||||
	dm_free(regions);
 | 
			
		||||
	fm->nr_regions = nr_regions;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _dmfilemapd(struct filemap_monitor *fm)
 | 
			
		||||
{
 | 
			
		||||
	int running = 1, check = 0, open = 0;
 | 
			
		||||
	const char *program_id;
 | 
			
		||||
	struct dm_stats *dms;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The correct program_id is retrieved from the group leader
 | 
			
		||||
	 * following the call to dm_stats_list().
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(dms = dm_stats_create(NULL)))
 | 
			
		||||
		goto_bad;
 | 
			
		||||
 | 
			
		||||
	if (!dm_stats_bind_from_fd(dms, fm->fd)) {
 | 
			
		||||
		log_error("Could not bind dm_stats handle to file descriptor "
 | 
			
		||||
			  "%d", fm->fd);
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!_filemap_monitor_set_notify(fm))
 | 
			
		||||
		goto bad;
 | 
			
		||||
 | 
			
		||||
	if (!_filemap_fd_update_blocks(fm))
 | 
			
		||||
		goto bad;
 | 
			
		||||
 | 
			
		||||
	if (!dm_stats_list(dms, DM_STATS_ALL_PROGRAMS)) {
 | 
			
		||||
		log_error("Failed to list stats handle.");
 | 
			
		||||
		goto bad;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Take the program_id for new regions (created by calls to
 | 
			
		||||
	 * dm_stats_update_regions_from_fd()) from the value used by
 | 
			
		||||
	 * the group leader.
 | 
			
		||||
	 */
 | 
			
		||||
	program_id = dm_stats_get_region_program_id(dms, fm->group_id);
 | 
			
		||||
	if (program_id)
 | 
			
		||||
		fm->program_id = dm_strdup(program_id);
 | 
			
		||||
	else
 | 
			
		||||
		fm->program_id = NULL;
 | 
			
		||||
	dm_stats_set_program_id(dms, 1, program_id);
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		if (!dm_stats_group_present(dms, fm->group_id)) {
 | 
			
		||||
			log_info("Filemap group removed: exiting.");
 | 
			
		||||
			running = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((check = _filemap_monitor_get_events(fm)) < 0)
 | 
			
		||||
			goto bad;
 | 
			
		||||
 | 
			
		||||
		if (!check)
 | 
			
		||||
			goto wait;
 | 
			
		||||
 | 
			
		||||
		if ((check = _filemap_fd_check_changed(fm)) < 0)
 | 
			
		||||
			goto bad;
 | 
			
		||||
 | 
			
		||||
		if (check && !_update_regions(dms, fm))
 | 
			
		||||
			goto bad;
 | 
			
		||||
 | 
			
		||||
		running = !!fm->nr_regions;
 | 
			
		||||
		if (!running)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
wait:
 | 
			
		||||
		_filemap_monitor_wait(FILEMAPD_WAIT_USECS);
 | 
			
		||||
 | 
			
		||||
		/* mode=inode termination condions */
 | 
			
		||||
		if (fm->mode == DM_FILEMAPD_FOLLOW_INODE) {
 | 
			
		||||
			if (!_filemap_monitor_check_file_unlinked(fm))
 | 
			
		||||
				goto bad;
 | 
			
		||||
			if (fm->deleted && !(open = _is_open(fm->path))) {
 | 
			
		||||
				log_info("File unlinked and closed: exiting.");
 | 
			
		||||
				running = 0;
 | 
			
		||||
			} else if (fm->deleted && open)
 | 
			
		||||
				log_verbose("File unlinked and open: "
 | 
			
		||||
					     "continuing.");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!dm_stats_list(dms, NULL)) {
 | 
			
		||||
			log_error("Failed to list stats handle.");
 | 
			
		||||
			goto bad;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} while (running);
 | 
			
		||||
 | 
			
		||||
	_filemap_monitor_destroy(fm);
 | 
			
		||||
	dm_stats_destroy(dms);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
bad:
 | 
			
		||||
	_filemap_monitor_destroy(fm);
 | 
			
		||||
	dm_stats_destroy(dms);
 | 
			
		||||
	log_error("Exiting");
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char * const _mode_names[] = {
 | 
			
		||||
	"inode",
 | 
			
		||||
	"path"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * dmfilemapd <fd> <group_id> <path> <mode> [<foreground>[<log_level>]]
 | 
			
		||||
 */
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	struct filemap_monitor fm;
 | 
			
		||||
 | 
			
		||||
	memset(&fm, 0, sizeof(fm));
 | 
			
		||||
 | 
			
		||||
	if (!_parse_args(argc, argv, &fm)) {
 | 
			
		||||
		dm_free(fm.path);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_setup_logging();
 | 
			
		||||
 | 
			
		||||
	log_info("Starting dmfilemapd with fd=%d, group_id=" FMTu64 " "
 | 
			
		||||
		 "mode=%s, path=%s", fm.fd, fm.group_id,
 | 
			
		||||
		 _mode_names[fm.mode], fm.path);
 | 
			
		||||
 | 
			
		||||
	if (!_foreground && !_daemonise(&fm)) {
 | 
			
		||||
		dm_free(fm.path);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return _dmfilemapd(&fm);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
path.py
 | 
			
		||||
lvmdbusd
 | 
			
		||||
lvmdb.py
 | 
			
		||||
lvm_shell_proxy.py
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of LVM2.
 | 
			
		||||
#
 | 
			
		||||
# This 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@
 | 
			
		||||
 | 
			
		||||
lvmdbusdir = $(python3dir)/lvmdbusd
 | 
			
		||||
 | 
			
		||||
LVMDBUS_SRCDIR_FILES = \
 | 
			
		||||
	automatedproperties.py \
 | 
			
		||||
	background.py \
 | 
			
		||||
	cfg.py \
 | 
			
		||||
	cmdhandler.py \
 | 
			
		||||
	fetch.py \
 | 
			
		||||
	__init__.py \
 | 
			
		||||
	job.py \
 | 
			
		||||
	loader.py \
 | 
			
		||||
	main.py \
 | 
			
		||||
	lv.py \
 | 
			
		||||
	manager.py \
 | 
			
		||||
	objectmanager.py \
 | 
			
		||||
	pv.py \
 | 
			
		||||
	request.py \
 | 
			
		||||
	state.py \
 | 
			
		||||
	udevwatch.py \
 | 
			
		||||
	utils.py \
 | 
			
		||||
	vg.py
 | 
			
		||||
 | 
			
		||||
LVMDBUS_BUILDDIR_FILES = \
 | 
			
		||||
	lvmdb.py \
 | 
			
		||||
	lvm_shell_proxy.py \
 | 
			
		||||
	path.py
 | 
			
		||||
 | 
			
		||||
LVMDBUSD = lvmdbusd
 | 
			
		||||
 | 
			
		||||
CLEAN_DIRS += __pycache__
 | 
			
		||||
 | 
			
		||||
include $(top_builddir)/make.tmpl
 | 
			
		||||
 | 
			
		||||
.PHONY: install_lvmdbusd
 | 
			
		||||
 | 
			
		||||
all:
 | 
			
		||||
	test -x $(LVMDBUSD) || chmod 755 $(LVMDBUSD)
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from .main import main
 | 
			
		||||
@@ -1,194 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
import dbus
 | 
			
		||||
import dbus.service
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .utils import get_properties, add_properties, get_object_property_diff, \
 | 
			
		||||
	log_debug
 | 
			
		||||
from .state import State
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming,PyUnresolvedReferences
 | 
			
		||||
class AutomatedProperties(dbus.service.Object):
 | 
			
		||||
	"""
 | 
			
		||||
	This class implements the needed interfaces for:
 | 
			
		||||
	org.freedesktop.DBus.Properties
 | 
			
		||||
 | 
			
		||||
	Other classes inherit from it to get the same behavior
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	def __init__(self, object_path, search_method=None):
 | 
			
		||||
		dbus.service.Object.__init__(self, cfg.bus, object_path)
 | 
			
		||||
		self._ap_interface = []
 | 
			
		||||
		self._ap_o_path = object_path
 | 
			
		||||
		self._ap_search_method = search_method
 | 
			
		||||
		self.state = None
 | 
			
		||||
 | 
			
		||||
	def dbus_object_path(self):
 | 
			
		||||
		return self._ap_o_path
 | 
			
		||||
 | 
			
		||||
	def emit_data(self):
 | 
			
		||||
		props = {}
 | 
			
		||||
 | 
			
		||||
		for i in self.interface():
 | 
			
		||||
			props[i] = AutomatedProperties._get_all_prop(self, i)
 | 
			
		||||
 | 
			
		||||
		return self._ap_o_path, props
 | 
			
		||||
 | 
			
		||||
	def set_interface(self, interface):
 | 
			
		||||
		"""
 | 
			
		||||
		With inheritance we can't easily tell what interfaces a class provides
 | 
			
		||||
		so we will have each class that implements an interface tell the
 | 
			
		||||
		base AutomatedProperties what it is they do provide.  This is kind of
 | 
			
		||||
		clunky and perhaps we can figure out a better way to do this later.
 | 
			
		||||
		:param interface:       An interface the object supports
 | 
			
		||||
		:return:
 | 
			
		||||
		"""
 | 
			
		||||
		if interface not in self._ap_interface:
 | 
			
		||||
			self._ap_interface.append(interface)
 | 
			
		||||
 | 
			
		||||
	# noinspection PyUnusedLocal
 | 
			
		||||
	def interface(self, all_interfaces=False):
 | 
			
		||||
		if all_interfaces:
 | 
			
		||||
			cpy = list(self._ap_interface)
 | 
			
		||||
			cpy.extend(
 | 
			
		||||
				["org.freedesktop.DBus.Introspectable",
 | 
			
		||||
					"org.freedesktop.DBus.Properties"])
 | 
			
		||||
			return cpy
 | 
			
		||||
 | 
			
		||||
		return self._ap_interface
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _get_prop(obj, interface_name, property_name):
 | 
			
		||||
		value = getattr(obj, 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):
 | 
			
		||||
			# Using introspection, lets build this dynamically
 | 
			
		||||
			properties = get_properties(obj)
 | 
			
		||||
			if interface_name in properties:
 | 
			
		||||
				return properties[interface_name][1]
 | 
			
		||||
			return {}
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			obj._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)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
 | 
			
		||||
							in_signature='ssv')
 | 
			
		||||
	def Set(self, interface_name, property_name, new_value):
 | 
			
		||||
		setattr(self, property_name, new_value)
 | 
			
		||||
		self.PropertiesChanged(interface_name,
 | 
			
		||||
								{property_name: new_value}, [])
 | 
			
		||||
 | 
			
		||||
	# As dbus-python does not support introspection for properties we will
 | 
			
		||||
	# get the autogenerated xml and then add our wanted properties to it.
 | 
			
		||||
	@dbus.service.method(dbus_interface=dbus.INTROSPECTABLE_IFACE,
 | 
			
		||||
							out_signature='s')
 | 
			
		||||
	def Introspect(self):
 | 
			
		||||
		r = dbus.service.Object.Introspect(self, self._ap_o_path, cfg.bus)
 | 
			
		||||
		# Look at the properties in the class
 | 
			
		||||
		props = get_properties(self)
 | 
			
		||||
 | 
			
		||||
		for int_f, v in props.items():
 | 
			
		||||
			r = add_properties(r, int_f, v[0])
 | 
			
		||||
 | 
			
		||||
		return r
 | 
			
		||||
 | 
			
		||||
	@dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
 | 
			
		||||
							signature='sa{sv}as')
 | 
			
		||||
	def PropertiesChanged(self, interface_name, changed_properties,
 | 
			
		||||
							invalidated_properties):
 | 
			
		||||
		log_debug(('SIGNAL: PropertiesChanged(%s, %s, %s, %s)' %
 | 
			
		||||
					(str(self._ap_o_path), str(interface_name),
 | 
			
		||||
					str(changed_properties), str(invalidated_properties))))
 | 
			
		||||
 | 
			
		||||
	def refresh(self, search_key=None, object_state=None):
 | 
			
		||||
		"""
 | 
			
		||||
		Take the values (properties) of an object and update them with what
 | 
			
		||||
		lvm currently has.  You can either fetch the new ones or supply the
 | 
			
		||||
		new state to be updated with
 | 
			
		||||
		:param search_key: The value to use to search for
 | 
			
		||||
		:param object_state: Use this as the new object state
 | 
			
		||||
		"""
 | 
			
		||||
		num_changed = 0
 | 
			
		||||
 | 
			
		||||
		# If we can't do a lookup, bail now, this happens if we blindly walk
 | 
			
		||||
		# through all dbus objects as some don't have a search method, like
 | 
			
		||||
		# 'Manager' object.
 | 
			
		||||
		if not self._ap_search_method:
 | 
			
		||||
			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:
 | 
			
		||||
			new_state = self._ap_search_method([search])[0]
 | 
			
		||||
			assert isinstance(new_state, State)
 | 
			
		||||
 | 
			
		||||
		assert new_state
 | 
			
		||||
 | 
			
		||||
		# When we refresh an object the object identifiers might have changed
 | 
			
		||||
		# because LVM allows the user to change them (name & uuid), thus if
 | 
			
		||||
		# they have changed we need to update the object manager so that
 | 
			
		||||
		# look-ups will happen correctly
 | 
			
		||||
		old_id = self.state.identifiers()
 | 
			
		||||
		new_id = new_state.identifiers()
 | 
			
		||||
		if old_id[0] != new_id[0] or old_id[1] != new_id[1]:
 | 
			
		||||
			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.
 | 
			
		||||
		o_prop = get_properties(self)
 | 
			
		||||
		self.state = new_state
 | 
			
		||||
		n_prop = get_properties(self)
 | 
			
		||||
 | 
			
		||||
		changed = get_object_property_diff(o_prop, n_prop)
 | 
			
		||||
 | 
			
		||||
		if changed:
 | 
			
		||||
			for int_f, v in changed.items():
 | 
			
		||||
				self.PropertiesChanged(int_f, v, [])
 | 
			
		||||
			num_changed += 1
 | 
			
		||||
		return num_changed
 | 
			
		||||
@@ -1,163 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .cmdhandler import options_to_cli_args, LvmExecutionMeta
 | 
			
		||||
import dbus
 | 
			
		||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\
 | 
			
		||||
	add_no_notify
 | 
			
		||||
import os
 | 
			
		||||
import threading
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_move_lv_cmd(move_options, lv_full_name,
 | 
			
		||||
					pv_source, pv_source_range, pv_dest_range_list):
 | 
			
		||||
	cmd = ['pvmove', '-i', '1']
 | 
			
		||||
	cmd.extend(options_to_cli_args(move_options))
 | 
			
		||||
 | 
			
		||||
	if lv_full_name:
 | 
			
		||||
		cmd.extend(['-n', lv_full_name])
 | 
			
		||||
 | 
			
		||||
	pv_range_append(cmd, pv_source, *pv_source_range)
 | 
			
		||||
	pv_dest_ranges(cmd, pv_dest_range_list)
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_merge_cmd(merge_options, lv_full_name):
 | 
			
		||||
	cmd = ['lvconvert', '--merge', '-i', '1']
 | 
			
		||||
	cmd.extend(options_to_cli_args(merge_options))
 | 
			
		||||
	cmd.append(lv_full_name)
 | 
			
		||||
	return cmd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
	command.insert(0, cfg.LVM_CMD)
 | 
			
		||||
 | 
			
		||||
	# Instruct lvm to not register an event with us
 | 
			
		||||
	command = add_no_notify(command)
 | 
			
		||||
 | 
			
		||||
	#(self, start, ended, cmd, ec, stdout_txt, stderr_txt)
 | 
			
		||||
	meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None)
 | 
			
		||||
 | 
			
		||||
	cfg.blackbox.add(meta)
 | 
			
		||||
 | 
			
		||||
	process = subprocess.Popen(command, stdout=subprocess.PIPE,
 | 
			
		||||
								env=os.environ,
 | 
			
		||||
								stderr=subprocess.PIPE, close_fds=True)
 | 
			
		||||
 | 
			
		||||
	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(':')
 | 
			
		||||
				job_state.Percent = round(
 | 
			
		||||
					float(percentage.strip()[:-1]), 1)
 | 
			
		||||
 | 
			
		||||
				# While the move is in progress we need to periodically update
 | 
			
		||||
				# the state to reflect where everything is at.
 | 
			
		||||
				cfg.load()
 | 
			
		||||
		except ValueError:
 | 
			
		||||
			log_error("Trying to parse percentage which failed for %s" %
 | 
			
		||||
				line_str)
 | 
			
		||||
 | 
			
		||||
	out = process.communicate()
 | 
			
		||||
 | 
			
		||||
	with meta.lock:
 | 
			
		||||
		meta.ended = time.time()
 | 
			
		||||
		meta.ec = process.returncode
 | 
			
		||||
		meta.stderr_txt = out[1]
 | 
			
		||||
 | 
			
		||||
	if process.returncode == 0:
 | 
			
		||||
		job_state.Percent = 100
 | 
			
		||||
	else:
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			interface_name,
 | 
			
		||||
			'Exit code %s, stderr = %s' % (str(process.returncode), out[1]))
 | 
			
		||||
 | 
			
		||||
	cfg.load()
 | 
			
		||||
	return '/'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
 | 
			
		||||
			pv_dests_and_ranges, move_options, job_state):
 | 
			
		||||
	"""
 | 
			
		||||
	Common code for the pvmove handling.
 | 
			
		||||
	:param interface_name:  What dbus interface we are providing for
 | 
			
		||||
	:param lv_name:     Optional (None or name of LV to move)
 | 
			
		||||
	:param pv_src_obj:  dbus object patch for source PV
 | 
			
		||||
	:param pv_source_range: (0,0 to ignore, else start, end segments)
 | 
			
		||||
	:param pv_dests_and_ranges: Array of PV object paths and start/end segs
 | 
			
		||||
	:param move_options: Hash with optional arguments
 | 
			
		||||
	:param job_state: Used to convey information about jobs between processes
 | 
			
		||||
	:return: '/' When complete, the empty object path
 | 
			
		||||
	"""
 | 
			
		||||
	pv_dests = []
 | 
			
		||||
	pv_src = cfg.om.get_object_by_path(pv_src_obj)
 | 
			
		||||
	if pv_src:
 | 
			
		||||
 | 
			
		||||
		# Check to see if we are handling a move to a specific
 | 
			
		||||
		# destination(s)
 | 
			
		||||
		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(
 | 
			
		||||
						interface_name,
 | 
			
		||||
						'PV Destination (%s) not found' % pr[0])
 | 
			
		||||
 | 
			
		||||
				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
 | 
			
		||||
 | 
			
		||||
		cmd = pv_move_lv_cmd(move_options,
 | 
			
		||||
								lv_name,
 | 
			
		||||
								pv_src.lvm_id,
 | 
			
		||||
								pv_source_range,
 | 
			
		||||
								pv_dests)
 | 
			
		||||
 | 
			
		||||
		return _move_merge(interface_name, cmd, job_state)
 | 
			
		||||
	else:
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			interface_name, 'pv_src_obj (%s) not found' % pv_src_obj)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
 | 
			
		||||
	# Make sure we have a dbus object representing it
 | 
			
		||||
	dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
 | 
			
		||||
	if dbo:
 | 
			
		||||
		cmd = lv_merge_cmd(merge_options, dbo.lvm_id)
 | 
			
		||||
		return _move_merge(interface_name, cmd, job_state)
 | 
			
		||||
	else:
 | 
			
		||||
		raise dbus.exceptions.DBusException(
 | 
			
		||||
			interface_name,
 | 
			
		||||
			'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 cmd_runner(request):
 | 
			
		||||
	t = threading.Thread(target=_run_cmd, args=(request,),
 | 
			
		||||
							name="cmd_runner %s" % str(request.method))
 | 
			
		||||
	t.start()
 | 
			
		||||
@@ -1,89 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import multiprocessing
 | 
			
		||||
import queue
 | 
			
		||||
import itertools
 | 
			
		||||
 | 
			
		||||
from lvmdbusd import path
 | 
			
		||||
 | 
			
		||||
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
 | 
			
		||||
 | 
			
		||||
# This is the global object manager
 | 
			
		||||
om = None
 | 
			
		||||
 | 
			
		||||
# This is the global bus connection
 | 
			
		||||
bus = None
 | 
			
		||||
 | 
			
		||||
# Command line args
 | 
			
		||||
args = None
 | 
			
		||||
 | 
			
		||||
# Set to true if we are depending on external events for updates
 | 
			
		||||
got_external_event = False
 | 
			
		||||
 | 
			
		||||
# Shared state variable across all processes
 | 
			
		||||
run = multiprocessing.Value('i', 1)
 | 
			
		||||
 | 
			
		||||
# If this is set to true, the current setup support lvm shell and we are
 | 
			
		||||
# running in that mode of operation
 | 
			
		||||
SHELL_IN_USE = None
 | 
			
		||||
 | 
			
		||||
# Lock used by pprint
 | 
			
		||||
stdout_lock = multiprocessing.Lock()
 | 
			
		||||
 | 
			
		||||
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'
 | 
			
		||||
LV_INTERFACE = BASE_INTERFACE + '.Lv'
 | 
			
		||||
LV_COMMON_INTERFACE = BASE_INTERFACE + '.LvCommon'
 | 
			
		||||
THIN_POOL_INTERFACE = BASE_INTERFACE + '.ThinPool'
 | 
			
		||||
CACHE_POOL_INTERFACE = BASE_INTERFACE + '.CachePool'
 | 
			
		||||
LV_CACHED = BASE_INTERFACE + '.CachedLv'
 | 
			
		||||
SNAPSHOT_INTERFACE = BASE_INTERFACE + '.Snapshot'
 | 
			
		||||
MANAGER_INTERFACE = BASE_INTERFACE + '.Manager'
 | 
			
		||||
JOB_INTERFACE = BASE_INTERFACE + '.Job'
 | 
			
		||||
 | 
			
		||||
BASE_OBJ_PATH = '/' + BASE_INTERFACE.replace('.', '/')
 | 
			
		||||
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"
 | 
			
		||||
CACHE_POOL_PATH = BASE_OBJ_PATH + "/CachePool"
 | 
			
		||||
HIDDEN_LV_PATH = BASE_OBJ_PATH + "/HiddenLv"
 | 
			
		||||
MANAGER_OBJ_PATH = BASE_OBJ_PATH + '/Manager'
 | 
			
		||||
JOB_OBJ_PATH = BASE_OBJ_PATH + '/Job'
 | 
			
		||||
 | 
			
		||||
# Counters for object path generation
 | 
			
		||||
pv_id = itertools.count()
 | 
			
		||||
vg_id = itertools.count()
 | 
			
		||||
lv_id = itertools.count()
 | 
			
		||||
thin_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
 | 
			
		||||
 | 
			
		||||
# Global cached state
 | 
			
		||||
db = None
 | 
			
		||||
 | 
			
		||||
# lvm flight recorder
 | 
			
		||||
blackbox = None
 | 
			
		||||
 | 
			
		||||
# RequestEntry ctor
 | 
			
		||||
create_request_entry = None
 | 
			
		||||
@@ -1,754 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from subprocess import Popen, PIPE
 | 
			
		||||
import time
 | 
			
		||||
import threading
 | 
			
		||||
from itertools import chain
 | 
			
		||||
import collections
 | 
			
		||||
import traceback
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from lvmdbusd import cfg
 | 
			
		||||
from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify
 | 
			
		||||
from lvmdbusd.lvm_shell_proxy import LVMShellProxy
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
	import simplejson as json
 | 
			
		||||
except ImportError:
 | 
			
		||||
	import json
 | 
			
		||||
 | 
			
		||||
SEP = '{|}'
 | 
			
		||||
 | 
			
		||||
total_time = 0.0
 | 
			
		||||
total_count = 0
 | 
			
		||||
 | 
			
		||||
# We need to prevent different threads from using the same lvm shell
 | 
			
		||||
# at the same time.
 | 
			
		||||
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
 | 
			
		||||
		self.ec = ec
 | 
			
		||||
		self.stdout_txt = stdout_txt
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LvmFlightRecorder(object):
 | 
			
		||||
 | 
			
		||||
	def __init__(self, size=16):
 | 
			
		||||
		self.queue = collections.deque(maxlen=size)
 | 
			
		||||
 | 
			
		||||
	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 self.queue:
 | 
			
		||||
					log_error(str(c))
 | 
			
		||||
				log_error("LVM dbus flight recorder END")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cfg.blackbox = LvmFlightRecorder()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _debug_c(cmd, exit_code, out):
 | 
			
		||||
	log_error('CMD= %s' % ' '.join(cmd))
 | 
			
		||||
	log_error(("EC= %d" % exit_code))
 | 
			
		||||
	log_error(("STDOUT=\n %s\n" % out[0]))
 | 
			
		||||
	log_error(("STDERR=\n %s\n" % out[1]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	"""
 | 
			
		||||
	# 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 = 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))
 | 
			
		||||
 | 
			
		||||
	return process.returncode, stdout_text, stderr_text
 | 
			
		||||
 | 
			
		||||
# The actual method which gets called to invoke the lvm command, can vary
 | 
			
		||||
# from forking a new process to using lvm shell
 | 
			
		||||
_t_call = call_lvm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _shell_cfg():
 | 
			
		||||
	global _t_call
 | 
			
		||||
	# noinspection PyBroadException
 | 
			
		||||
	try:
 | 
			
		||||
		lvm_shell = LVMShellProxy()
 | 
			
		||||
		_t_call = lvm_shell.call_lvm
 | 
			
		||||
		cfg.SHELL_IN_USE = lvm_shell
 | 
			
		||||
		return True
 | 
			
		||||
	except Exception:
 | 
			
		||||
		_t_call = call_lvm
 | 
			
		||||
		cfg.SHELL_IN_USE = None
 | 
			
		||||
		log_error(traceback.format_exc())
 | 
			
		||||
		log_error("Unable to utilize lvm shell, dropping back to fork & exec")
 | 
			
		||||
		return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_execution(shell):
 | 
			
		||||
	global _t_call
 | 
			
		||||
	with cmd_lock:
 | 
			
		||||
		# If the user requested lvm shell and we are currently setup that
 | 
			
		||||
		# way, just return
 | 
			
		||||
		if cfg.SHELL_IN_USE and shell:
 | 
			
		||||
			return True
 | 
			
		||||
		else:
 | 
			
		||||
			if not shell and cfg.SHELL_IN_USE:
 | 
			
		||||
				cfg.SHELL_IN_USE.exit_shell()
 | 
			
		||||
				cfg.SHELL_IN_USE = None
 | 
			
		||||
 | 
			
		||||
		_t_call = call_lvm
 | 
			
		||||
		if shell:
 | 
			
		||||
			if cfg.args.use_json:
 | 
			
		||||
				return _shell_cfg()
 | 
			
		||||
			else:
 | 
			
		||||
				return False
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_wrapper(command, debug=False):
 | 
			
		||||
	global total_time
 | 
			
		||||
	global total_count
 | 
			
		||||
 | 
			
		||||
	with cmd_lock:
 | 
			
		||||
		start = time.time()
 | 
			
		||||
		results = _t_call(command, debug)
 | 
			
		||||
		ended = time.time()
 | 
			
		||||
		total_time += (ended - start)
 | 
			
		||||
		total_count += 1
 | 
			
		||||
		cfg.blackbox.add(LvmExecutionMeta(start, ended, command, *results))
 | 
			
		||||
	return results
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
call = time_wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Default cmd
 | 
			
		||||
# Place default arguments for every command here.
 | 
			
		||||
def _dc(cmd, args):
 | 
			
		||||
	c = [cmd, '--noheading', '--separator', '%s' % SEP, '--nosuffix',
 | 
			
		||||
		'--unbuffered', '--units', 'b']
 | 
			
		||||
	c.extend(args)
 | 
			
		||||
	return c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse(out):
 | 
			
		||||
	rc = []
 | 
			
		||||
 | 
			
		||||
	for line in out.split('\n'):
 | 
			
		||||
		# This line includes separators, so process them
 | 
			
		||||
		if SEP in line:
 | 
			
		||||
			elem = line.split(SEP)
 | 
			
		||||
			cleaned_elem = []
 | 
			
		||||
			for e in elem:
 | 
			
		||||
				e = e.strip()
 | 
			
		||||
				cleaned_elem.append(e)
 | 
			
		||||
 | 
			
		||||
			if len(cleaned_elem) > 1:
 | 
			
		||||
				rc.append(cleaned_elem)
 | 
			
		||||
		else:
 | 
			
		||||
			t = line.strip()
 | 
			
		||||
			if len(t) > 0:
 | 
			
		||||
				rc.append(t)
 | 
			
		||||
	return rc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_column_names(out, column_names):
 | 
			
		||||
	lines = parse(out)
 | 
			
		||||
	rc = []
 | 
			
		||||
 | 
			
		||||
	for i in range(0, len(lines)):
 | 
			
		||||
		d = dict(list(zip(column_names, lines[i])))
 | 
			
		||||
		rc.append(d)
 | 
			
		||||
 | 
			
		||||
	return rc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def options_to_cli_args(options):
 | 
			
		||||
	rc = []
 | 
			
		||||
	for k, v in list(dict(options).items()):
 | 
			
		||||
		if k.startswith("-"):
 | 
			
		||||
			rc.append(k)
 | 
			
		||||
		else:
 | 
			
		||||
			rc.append("--%s" % k)
 | 
			
		||||
		if v != "":
 | 
			
		||||
			rc.append(str(v))
 | 
			
		||||
	return rc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_remove(device, remove_options):
 | 
			
		||||
	cmd = ['pvremove']
 | 
			
		||||
	cmd.extend(options_to_cli_args(remove_options))
 | 
			
		||||
	cmd.append(device)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _qt(tag_name):
 | 
			
		||||
	return '@%s' % tag_name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _tag(operation, what, add, rm, tag_options):
 | 
			
		||||
	cmd = [operation]
 | 
			
		||||
	cmd.extend(options_to_cli_args(tag_options))
 | 
			
		||||
 | 
			
		||||
	if isinstance(what, list):
 | 
			
		||||
		cmd.extend(what)
 | 
			
		||||
	else:
 | 
			
		||||
		cmd.append(what)
 | 
			
		||||
 | 
			
		||||
	if add:
 | 
			
		||||
		cmd.extend(list(chain.from_iterable(
 | 
			
		||||
			('--addtag', _qt(x)) for x in add)))
 | 
			
		||||
	if rm:
 | 
			
		||||
		cmd.extend(list(chain.from_iterable(
 | 
			
		||||
			('--deltag', _qt(x)) for x in rm)))
 | 
			
		||||
 | 
			
		||||
	return call(cmd, False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_tag(pv_devices, add, rm, tag_options):
 | 
			
		||||
	return _tag('pvchange', pv_devices, add, rm, tag_options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_tag(vg_name, add, rm, tag_options):
 | 
			
		||||
	return _tag('vgchange', vg_name, add, rm, tag_options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_tag(lv_name, add, rm, tag_options):
 | 
			
		||||
	return _tag('lvchange', lv_name, add, rm, tag_options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_rename(vg, new_name, rename_options):
 | 
			
		||||
	cmd = ['vgrename']
 | 
			
		||||
	cmd.extend(options_to_cli_args(rename_options))
 | 
			
		||||
	cmd.extend([vg, new_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_remove(vg_name, remove_options):
 | 
			
		||||
	cmd = ['vgremove']
 | 
			
		||||
	cmd.extend(options_to_cli_args(remove_options))
 | 
			
		||||
	cmd.extend(['-f', vg_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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', str(size_bytes) + 'B'])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name, '--yes'])
 | 
			
		||||
	pv_dest_ranges(cmd, pv_dests)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(snapshot_options))
 | 
			
		||||
	cmd.extend(["-s"])
 | 
			
		||||
 | 
			
		||||
	if size_bytes != 0:
 | 
			
		||||
		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):
 | 
			
		||||
	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(['--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(['--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(num_stripes)])
 | 
			
		||||
 | 
			
		||||
	if stripe_size_kb != 0:
 | 
			
		||||
		cmd.extend(['--stripesize', str(stripe_size_kb)])
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--name', name, vg_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
 | 
			
		||||
						num_stripes, stripe_size_kb):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--type', raid_type])
 | 
			
		||||
	cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
 | 
			
		||||
	if num_stripes != 0:
 | 
			
		||||
		cmd.extend(['--stripes', str(num_stripes)])
 | 
			
		||||
 | 
			
		||||
	if stripe_size_kb != 0:
 | 
			
		||||
		cmd.extend(['--stripesize', str(stripe_size_kb)])
 | 
			
		||||
 | 
			
		||||
	cmd.extend(['--name', name, vg_name, '--yes'])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
 | 
			
		||||
						num_stripes, stripe_size_kb):
 | 
			
		||||
	cmd = ['lvcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	return _vg_lv_create_raid(vg_name, create_options, name, raid_type,
 | 
			
		||||
								size_bytes, num_stripes, stripe_size_kb)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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(num_copies)])
 | 
			
		||||
	cmd.extend(['--size', str(size_bytes) + 'B'])
 | 
			
		||||
	cmd.extend(['--name', name, vg_name, '--yes'])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_create_cache_pool(md_full_name, data_full_name, create_options):
 | 
			
		||||
	cmd = ['lvconvert']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
	cmd.extend(['--type', 'cache-pool', '--force', '-y',
 | 
			
		||||
				'--poolmetadata', md_full_name, data_full_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_create_thin_pool(md_full_name, data_full_name, create_options):
 | 
			
		||||
	cmd = ['lvconvert']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
	cmd.extend(['--type', 'thin-pool', '--force', '-y',
 | 
			
		||||
				'--poolmetadata', md_full_name, data_full_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_remove(lv_path, remove_options):
 | 
			
		||||
	cmd = ['lvremove']
 | 
			
		||||
	cmd.extend(options_to_cli_args(remove_options))
 | 
			
		||||
	cmd.extend(['-f', lv_path])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_rename(lv_path, new_name, rename_options):
 | 
			
		||||
	cmd = ['lvrename']
 | 
			
		||||
	cmd.extend(options_to_cli_args(rename_options))
 | 
			
		||||
	cmd.extend([lv_path, new_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_resize(lv_full_name, size_change, pv_dests,
 | 
			
		||||
				resize_options):
 | 
			
		||||
	cmd = ['lvresize', '--force']
 | 
			
		||||
 | 
			
		||||
	cmd.extend(options_to_cli_args(resize_options))
 | 
			
		||||
 | 
			
		||||
	if size_change < 0:
 | 
			
		||||
		cmd.append("-L-%dB" % (-size_change))
 | 
			
		||||
	else:
 | 
			
		||||
		cmd.append("-L+%dB" % (size_change))
 | 
			
		||||
 | 
			
		||||
	cmd.append(lv_full_name)
 | 
			
		||||
	pv_dest_ranges(cmd, pv_dests)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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', str(size_bytes) + 'B', '-T'])
 | 
			
		||||
	cmd.extend(['--name', name, lv_full_name, '--yes'])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options):
 | 
			
		||||
	# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV
 | 
			
		||||
	cmd = ['lvconvert']
 | 
			
		||||
	cmd.extend(options_to_cli_args(cache_options))
 | 
			
		||||
	cmd.extend(['-y', '--type', 'cache', '--cachepool',
 | 
			
		||||
				cache_pool_full_name, lv_full_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_detach_cache(lv_full_name, detach_options, destroy_cache):
 | 
			
		||||
	cmd = ['lvconvert']
 | 
			
		||||
	if destroy_cache:
 | 
			
		||||
		option = '--uncache'
 | 
			
		||||
	else:
 | 
			
		||||
		# Currently fairly dangerous
 | 
			
		||||
		# see: https://bugzilla.redhat.com/show_bug.cgi?id=1248972
 | 
			
		||||
		option = '--splitcache'
 | 
			
		||||
	cmd.extend(options_to_cli_args(detach_options))
 | 
			
		||||
	# needed to prevent interactive questions
 | 
			
		||||
	cmd.extend(["--yes", "--force"])
 | 
			
		||||
	cmd.extend([option, lv_full_name])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def supports_json():
 | 
			
		||||
	cmd = ['help']
 | 
			
		||||
	rc, out, err = call(cmd)
 | 
			
		||||
	if rc == 0:
 | 
			
		||||
		if cfg.SHELL_IN_USE:
 | 
			
		||||
			return True
 | 
			
		||||
		else:
 | 
			
		||||
			if 'fullreport' in err:
 | 
			
		||||
				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',
 | 
			
		||||
					'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
 | 
			
		||||
					'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
 | 
			
		||||
					'vg_uuid', 'pv_missing']
 | 
			
		||||
 | 
			
		||||
	pv_seg_columns = ['pvseg_start', 'pvseg_size', 'segtype',
 | 
			
		||||
						'pv_uuid', 'lv_uuid', 'pv_name']
 | 
			
		||||
 | 
			
		||||
	vg_columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
 | 
			
		||||
					'vg_sysid', 'vg_extent_size', 'vg_extent_count',
 | 
			
		||||
					'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
 | 
			
		||||
					'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
 | 
			
		||||
					'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
 | 
			
		||||
					'vg_mda_used_count', 'vg_attr', 'vg_tags']
 | 
			
		||||
 | 
			
		||||
	lv_columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
 | 
			
		||||
				'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
 | 
			
		||||
				'origin', 'data_percent',
 | 
			
		||||
				'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
 | 
			
		||||
				'metadata_lv', 'lv_parent', 'lv_role', 'lv_layout',
 | 
			
		||||
				'snap_percent', 'metadata_percent', 'copy_percent',
 | 
			
		||||
				'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
 | 
			
		||||
 | 
			
		||||
	lv_seg_columns = ['seg_pe_ranges', 'segtype', 'lv_uuid']
 | 
			
		||||
 | 
			
		||||
	cmd = _dc('fullreport', [
 | 
			
		||||
		'-a',		# Need hidden too
 | 
			
		||||
		'--configreport', 'pv', '-o', ','.join(pv_columns),
 | 
			
		||||
		'--configreport', 'vg', '-o', ','.join(vg_columns),
 | 
			
		||||
		'--configreport', 'lv', '-o', ','.join(lv_columns),
 | 
			
		||||
		'--configreport', 'seg', '-o', ','.join(lv_seg_columns),
 | 
			
		||||
		'--configreport', 'pvseg', '-o', ','.join(pv_seg_columns),
 | 
			
		||||
		'--reportformat', 'json'
 | 
			
		||||
	])
 | 
			
		||||
 | 
			
		||||
	rc, out, err = call(cmd)
 | 
			
		||||
	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
 | 
			
		||||
		if cfg.SHELL_IN_USE:
 | 
			
		||||
			assert(type(out) == dict)
 | 
			
		||||
			return out
 | 
			
		||||
		else:
 | 
			
		||||
			return json.loads(out)
 | 
			
		||||
	return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_retrieve_with_segs(device=None):
 | 
			
		||||
	d = []
 | 
			
		||||
	err = ""
 | 
			
		||||
	out = ""
 | 
			
		||||
	rc = 0
 | 
			
		||||
 | 
			
		||||
	columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
 | 
			
		||||
				'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
 | 
			
		||||
				'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
 | 
			
		||||
				'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
 | 
			
		||||
				'vg_uuid', 'pvseg_start', 'pvseg_size', 'segtype', 'pv_missing']
 | 
			
		||||
 | 
			
		||||
	# Lvm has some issues where it returns failure when querying pvs when other
 | 
			
		||||
	# operations are in process, see:
 | 
			
		||||
	# https://bugzilla.redhat.com/show_bug.cgi?id=1274085
 | 
			
		||||
	for i in range(0, 10):
 | 
			
		||||
		cmd = _dc('pvs', ['-o', ','.join(columns)])
 | 
			
		||||
 | 
			
		||||
		if device:
 | 
			
		||||
			cmd.extend(device)
 | 
			
		||||
 | 
			
		||||
		rc, out, err = call(cmd)
 | 
			
		||||
 | 
			
		||||
		if rc == 0:
 | 
			
		||||
			d = parse_column_names(out, columns)
 | 
			
		||||
			break
 | 
			
		||||
		else:
 | 
			
		||||
			time.sleep(0.2)
 | 
			
		||||
			log_debug("LVM Bug workaround, retrying pvs command...")
 | 
			
		||||
 | 
			
		||||
	if rc != 0:
 | 
			
		||||
		msg = "We were unable to get pvs to return without error after " \
 | 
			
		||||
			"trying 10 times, RC=%d, STDERR=(%s), STDOUT=(%s)" % \
 | 
			
		||||
			(rc, err, out)
 | 
			
		||||
		log_error(msg)
 | 
			
		||||
		raise RuntimeError(msg)
 | 
			
		||||
 | 
			
		||||
	return d
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_resize(device, size_bytes, create_options):
 | 
			
		||||
	cmd = ['pvresize']
 | 
			
		||||
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
 | 
			
		||||
	if size_bytes != 0:
 | 
			
		||||
		cmd.extend(['--yes', '--setphysicalvolumesize', str(size_bytes) + 'B'])
 | 
			
		||||
 | 
			
		||||
	cmd.extend([device])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_create(create_options, devices):
 | 
			
		||||
	cmd = ['pvcreate', '-ff']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
	cmd.extend(devices)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_allocatable(device, yes, allocation_options):
 | 
			
		||||
	yn = 'n'
 | 
			
		||||
 | 
			
		||||
	if yes:
 | 
			
		||||
		yn = 'y'
 | 
			
		||||
 | 
			
		||||
	cmd = ['pvchange']
 | 
			
		||||
	cmd.extend(options_to_cli_args(allocation_options))
 | 
			
		||||
	cmd.extend(['-x', yn, device])
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pv_scan(activate, cache, device_paths, major_minors, scan_options):
 | 
			
		||||
	cmd = ['pvscan']
 | 
			
		||||
	cmd.extend(options_to_cli_args(scan_options))
 | 
			
		||||
 | 
			
		||||
	if activate:
 | 
			
		||||
		cmd.extend(['--activate', "ay"])
 | 
			
		||||
 | 
			
		||||
	if cache:
 | 
			
		||||
		cmd.append('--cache')
 | 
			
		||||
 | 
			
		||||
		if len(device_paths) > 0:
 | 
			
		||||
			for d in device_paths:
 | 
			
		||||
				cmd.append(d)
 | 
			
		||||
 | 
			
		||||
		if len(major_minors) > 0:
 | 
			
		||||
			for mm in major_minors:
 | 
			
		||||
				cmd.append("%s:%s" % (mm))
 | 
			
		||||
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_create(create_options, pv_devices, name):
 | 
			
		||||
	cmd = ['vgcreate']
 | 
			
		||||
	cmd.extend(options_to_cli_args(create_options))
 | 
			
		||||
	cmd.append(name)
 | 
			
		||||
	cmd.extend(pv_devices)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_change(change_options, name):
 | 
			
		||||
	cmd = ['vgchange']
 | 
			
		||||
	cmd.extend(options_to_cli_args(change_options))
 | 
			
		||||
	cmd.append(name)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_reduce(vg_name, missing, pv_devices, reduce_options):
 | 
			
		||||
	cmd = ['vgreduce']
 | 
			
		||||
	cmd.extend(options_to_cli_args(reduce_options))
 | 
			
		||||
 | 
			
		||||
	if missing:
 | 
			
		||||
		cmd.append('--removemissing')
 | 
			
		||||
	elif len(pv_devices) == 0:
 | 
			
		||||
		cmd.append('--all')
 | 
			
		||||
 | 
			
		||||
	cmd.append(vg_name)
 | 
			
		||||
	cmd.extend(pv_devices)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_extend(vg_name, extend_devices, extend_options):
 | 
			
		||||
	cmd = ['vgextend']
 | 
			
		||||
	cmd.extend(options_to_cli_args(extend_options))
 | 
			
		||||
	cmd.append(vg_name)
 | 
			
		||||
	cmd.extend(extend_devices)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _vg_value_set(name, arguments, options):
 | 
			
		||||
	cmd = ['vgchange']
 | 
			
		||||
	cmd.extend(options_to_cli_args(options))
 | 
			
		||||
	cmd.append(name)
 | 
			
		||||
	cmd.extend(arguments)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_allocation_policy(vg_name, policy, policy_options):
 | 
			
		||||
	return _vg_value_set(vg_name, ['--alloc', policy], policy_options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_max_pv(vg_name, number, max_options):
 | 
			
		||||
	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(number)], max_options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_uuid_gen(vg_name, ignore, options):
 | 
			
		||||
	assert ignore is None
 | 
			
		||||
	return _vg_value_set(vg_name, ['--uuid'], options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def activate_deactivate(op, name, activate, control_flags, options):
 | 
			
		||||
	cmd = [op]
 | 
			
		||||
	cmd.extend(options_to_cli_args(options))
 | 
			
		||||
 | 
			
		||||
	op = '-a'
 | 
			
		||||
 | 
			
		||||
	if control_flags:
 | 
			
		||||
		# Autoactivation
 | 
			
		||||
		if (1 << 0) & control_flags:
 | 
			
		||||
			op += 'a'
 | 
			
		||||
		# Exclusive locking (Cluster)
 | 
			
		||||
		if (1 << 1) & control_flags:
 | 
			
		||||
			op += 'e'
 | 
			
		||||
 | 
			
		||||
		# Local node activation
 | 
			
		||||
		if (1 << 2) & control_flags:
 | 
			
		||||
			op += 'l'
 | 
			
		||||
 | 
			
		||||
		# Activation modes
 | 
			
		||||
		if (1 << 3) & control_flags:
 | 
			
		||||
			cmd.extend(['--activationmode', 'complete'])
 | 
			
		||||
		elif (1 << 4) & control_flags:
 | 
			
		||||
			cmd.extend(['--activationmode', 'partial'])
 | 
			
		||||
 | 
			
		||||
		# Ignore activation skip
 | 
			
		||||
		if (1 << 5) & control_flags:
 | 
			
		||||
			cmd.append('--ignoreactivationskip')
 | 
			
		||||
 | 
			
		||||
	if activate:
 | 
			
		||||
		op += 'y'
 | 
			
		||||
	else:
 | 
			
		||||
		op += 'n'
 | 
			
		||||
 | 
			
		||||
	cmd.append(op)
 | 
			
		||||
	cmd.append(name)
 | 
			
		||||
	return call(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vg_retrieve(vg_specific):
 | 
			
		||||
	if vg_specific:
 | 
			
		||||
		assert isinstance(vg_specific, list)
 | 
			
		||||
 | 
			
		||||
	columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
 | 
			
		||||
				'vg_sysid', 'vg_extent_size', 'vg_extent_count',
 | 
			
		||||
				'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
 | 
			
		||||
				'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
 | 
			
		||||
				'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
 | 
			
		||||
				'vg_mda_used_count', 'vg_attr', 'vg_tags']
 | 
			
		||||
 | 
			
		||||
	cmd = _dc('vgs', ['-o', ','.join(columns)])
 | 
			
		||||
 | 
			
		||||
	if vg_specific:
 | 
			
		||||
		cmd.extend(vg_specific)
 | 
			
		||||
 | 
			
		||||
	d = []
 | 
			
		||||
	rc, out, err = call(cmd)
 | 
			
		||||
	if rc == 0:
 | 
			
		||||
		d = parse_column_names(out, columns)
 | 
			
		||||
 | 
			
		||||
	return d
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def lv_retrieve_with_segments():
 | 
			
		||||
	columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
 | 
			
		||||
				'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
 | 
			
		||||
				'origin', 'data_percent',
 | 
			
		||||
				'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']
 | 
			
		||||
 | 
			
		||||
	cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
 | 
			
		||||
	rc, out, err = call(cmd)
 | 
			
		||||
 | 
			
		||||
	d = []
 | 
			
		||||
 | 
			
		||||
	if rc == 0:
 | 
			
		||||
		d = parse_column_names(out, columns)
 | 
			
		||||
 | 
			
		||||
	return d
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	pv_data = pv_retrieve_with_segs()
 | 
			
		||||
 | 
			
		||||
	for p in pv_data:
 | 
			
		||||
		print(str(p))
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
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 traceback
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _main_thread_load(refresh=True, emit_signal=True):
 | 
			
		||||
	num_total_changes = 0
 | 
			
		||||
 | 
			
		||||
	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 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)
 | 
			
		||||
 | 
			
		||||
	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):
 | 
			
		||||
		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 = []
 | 
			
		||||
 | 
			
		||||
			except queue.Empty:
 | 
			
		||||
				pass
 | 
			
		||||
			except Exception:
 | 
			
		||||
				st = traceback.format_exc()
 | 
			
		||||
				log_error("update_thread exception: \n%s" % st)
 | 
			
		||||
				cfg.blackbox.dump()
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
@@ -1,228 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from .automatedproperties import AutomatedProperties
 | 
			
		||||
from .utils import job_obj_path_generate, mt_async_call
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class JobState(object):
 | 
			
		||||
	def __init__(self, request=None):
 | 
			
		||||
		self.rlock = threading.RLock()
 | 
			
		||||
 | 
			
		||||
		self._percent = 0
 | 
			
		||||
		self._complete = False
 | 
			
		||||
		self._request = request
 | 
			
		||||
		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
 | 
			
		||||
		if self._request:
 | 
			
		||||
			# Faking the percentage when we don't have one
 | 
			
		||||
			self._percent = 1
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Percent(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			return self._percent
 | 
			
		||||
 | 
			
		||||
	@Percent.setter
 | 
			
		||||
	def Percent(self, value):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			self._percent = value
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Complete(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			if self._request:
 | 
			
		||||
				self._complete = self._request.is_done()
 | 
			
		||||
 | 
			
		||||
			return self._complete
 | 
			
		||||
 | 
			
		||||
	@Complete.setter
 | 
			
		||||
	def Complete(self, value):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			self._complete = value
 | 
			
		||||
			self._percent = 100
 | 
			
		||||
			self.notify_waiting_clients()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def GetError(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			if self.Complete:
 | 
			
		||||
				if self._request:
 | 
			
		||||
					(rc, error) = self._request.get_errors()
 | 
			
		||||
					return (rc, str(error))
 | 
			
		||||
				else:
 | 
			
		||||
					return (self._ec, self._stderr)
 | 
			
		||||
			else:
 | 
			
		||||
				return (-1, 'Job is not complete!')
 | 
			
		||||
 | 
			
		||||
	def dtor(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			self._request = None
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Result(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			if self._request:
 | 
			
		||||
				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):
 | 
			
		||||
	_Percent_meta = ('d', JOB_INTERFACE)
 | 
			
		||||
	_Complete_meta = ('b', JOB_INTERFACE)
 | 
			
		||||
	_Result_meta = ('o', JOB_INTERFACE)
 | 
			
		||||
	_GetError_meta = ('(is)', JOB_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	def __init__(self, request, job_state=None):
 | 
			
		||||
		super(Job, self).__init__(job_obj_path_generate())
 | 
			
		||||
		self.set_interface(JOB_INTERFACE)
 | 
			
		||||
 | 
			
		||||
		if job_state:
 | 
			
		||||
			self.state = job_state
 | 
			
		||||
		else:
 | 
			
		||||
			self.state = JobState(request)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Percent(self):
 | 
			
		||||
		return dbus.Double(float(self.state.Percent))
 | 
			
		||||
 | 
			
		||||
	@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)")
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(dbus_interface=JOB_INTERFACE)
 | 
			
		||||
	def Remove(self):
 | 
			
		||||
		if self.state.Complete:
 | 
			
		||||
			cfg.om.remove_object(self, True)
 | 
			
		||||
			self.state.dtor()
 | 
			
		||||
		else:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				JOB_INTERFACE, 'Job is not complete!')
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(dbus_interface=JOB_INTERFACE,
 | 
			
		||||
							in_signature='i',
 | 
			
		||||
							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))
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Result(self):
 | 
			
		||||
		return dbus.ObjectPath(self.state.Result)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def lvm_id(self):
 | 
			
		||||
		return str(id(self))
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Uuid(self):
 | 
			
		||||
		import uuid
 | 
			
		||||
		return uuid.uuid1()
 | 
			
		||||
@@ -1,85 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from . import cfg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _compare_construction(o_state, new_state):
 | 
			
		||||
	# We need to check to see if the objects would get constructed
 | 
			
		||||
	# the same
 | 
			
		||||
	existing_ctor, existing_path = o_state.creation_signature()
 | 
			
		||||
	new_ctor, new_path = new_state.creation_signature()
 | 
			
		||||
 | 
			
		||||
	# print("%s == %s and %s == %s" % (str(existing_ctor), str(new_ctor),
 | 
			
		||||
	#      str(existing_path), str(new_path)))
 | 
			
		||||
 | 
			
		||||
	return ((existing_ctor == new_ctor) and (existing_path == new_path))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def common(retrieve, o_type, search_keys,
 | 
			
		||||
			object_path, refresh, emit_signal, cache_refresh):
 | 
			
		||||
	num_changes = 0
 | 
			
		||||
	existing_paths = []
 | 
			
		||||
	rc = []
 | 
			
		||||
 | 
			
		||||
	if search_keys:
 | 
			
		||||
		assert isinstance(search_keys, list)
 | 
			
		||||
 | 
			
		||||
	if cache_refresh:
 | 
			
		||||
		cfg.db.refresh()
 | 
			
		||||
 | 
			
		||||
	objects = retrieve(search_keys, cache_refresh=False)
 | 
			
		||||
 | 
			
		||||
	# If we are doing a refresh we need to know what we have in memory, what's
 | 
			
		||||
	# in lvm and add those that are new and remove those that are gone!
 | 
			
		||||
	if refresh:
 | 
			
		||||
		existing_paths = cfg.om.object_paths_by_type(o_type)
 | 
			
		||||
 | 
			
		||||
	for o in objects:
 | 
			
		||||
		# Assume we need to add this one to dbus, unless we are refreshing
 | 
			
		||||
		# and it's already present
 | 
			
		||||
		return_object = True
 | 
			
		||||
 | 
			
		||||
		if refresh:
 | 
			
		||||
			# We are refreshing all the PVs from LVM, if this one exists
 | 
			
		||||
			# we need to refresh our state.
 | 
			
		||||
			dbus_object = cfg.om.get_object_by_uuid_lvm_id(*o.identifiers())
 | 
			
		||||
 | 
			
		||||
			if dbus_object:
 | 
			
		||||
				del existing_paths[dbus_object.dbus_object_path()]
 | 
			
		||||
 | 
			
		||||
				# If the old object state and new object state wouldn't be
 | 
			
		||||
				# created with the same path and same object constructor we
 | 
			
		||||
				# need to remove the old object and construct the new one
 | 
			
		||||
				# instead!
 | 
			
		||||
				if not _compare_construction(dbus_object.state, o):
 | 
			
		||||
					# Remove existing and construct new one
 | 
			
		||||
					cfg.om.remove_object(dbus_object, emit_signal)
 | 
			
		||||
					dbus_object = o.create_dbus_object(None)
 | 
			
		||||
					cfg.om.register_object(dbus_object, emit_signal)
 | 
			
		||||
					num_changes += 1
 | 
			
		||||
				else:
 | 
			
		||||
					num_changes += dbus_object.refresh(object_state=o)
 | 
			
		||||
				return_object = False
 | 
			
		||||
 | 
			
		||||
		if return_object:
 | 
			
		||||
			dbus_object = o.create_dbus_object(object_path)
 | 
			
		||||
			cfg.om.register_object(dbus_object, emit_signal)
 | 
			
		||||
			rc.append(dbus_object)
 | 
			
		||||
 | 
			
		||||
		object_path = None
 | 
			
		||||
 | 
			
		||||
	if refresh:
 | 
			
		||||
		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
 | 
			
		||||
@@ -1,831 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from .automatedproperties import AutomatedProperties
 | 
			
		||||
 | 
			
		||||
from . import utils
 | 
			
		||||
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
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from .utils import n, n32
 | 
			
		||||
from .loader import common
 | 
			
		||||
from .state import State
 | 
			
		||||
from . import background
 | 
			
		||||
from .utils import round_size, mt_remove_dbus_objects
 | 
			
		||||
from .job import JobState
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
# provides and what you can stack.
 | 
			
		||||
def get_key(i):
 | 
			
		||||
 | 
			
		||||
	name = i['lv_name']
 | 
			
		||||
	parent = i['lv_parent']
 | 
			
		||||
	pool = i['pool_lv']
 | 
			
		||||
	a1 = ""
 | 
			
		||||
	a2 = ""
 | 
			
		||||
 | 
			
		||||
	if name[0] == '[':
 | 
			
		||||
		a1 = '#'
 | 
			
		||||
 | 
			
		||||
	# We have a parent
 | 
			
		||||
	if parent:
 | 
			
		||||
		# Check if parent is hidden
 | 
			
		||||
		if parent[0] == '[':
 | 
			
		||||
			a2 = '##'
 | 
			
		||||
		else:
 | 
			
		||||
			a2 = '#'
 | 
			
		||||
 | 
			
		||||
	# If a LV has a pool, then it should be sorted/loaded after the pool
 | 
			
		||||
	# lv, unless it's a hidden too, then after other hidden, but before visible
 | 
			
		||||
	if pool:
 | 
			
		||||
		if pool[0] != '[':
 | 
			
		||||
			a2 += '~'
 | 
			
		||||
		else:
 | 
			
		||||
			a1 = '$' + a1
 | 
			
		||||
 | 
			
		||||
	return "%s%s%s" % (a1, a2, name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyUnusedLocal
 | 
			
		||||
def lvs_state_retrieve(selection, cache_refresh=True):
 | 
			
		||||
	rc = []
 | 
			
		||||
 | 
			
		||||
	if cache_refresh:
 | 
			
		||||
		cfg.db.refresh()
 | 
			
		||||
 | 
			
		||||
	# When building up the model, it's best to process LVs with the least
 | 
			
		||||
	# dependencies to those that are dependant upon other LVs.  Otherwise, when
 | 
			
		||||
	# we are trying to gather information we could be in a position where we
 | 
			
		||||
	# don't have information available yet.
 | 
			
		||||
	lvs = sorted(cfg.db.fetch_lvs(selection), key=get_key)
 | 
			
		||||
 | 
			
		||||
	for l in lvs:
 | 
			
		||||
		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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_lvs(lv_name=None, object_path=None, refresh=False, emit_signal=False,
 | 
			
		||||
				cache_refresh=True):
 | 
			
		||||
	# noinspection PyUnresolvedReferences
 | 
			
		||||
	return common(
 | 
			
		||||
		lvs_state_retrieve,
 | 
			
		||||
		(LvCommon, Lv, LvThinPool, LvSnapShot),
 | 
			
		||||
		lv_name, object_path, refresh, emit_signal, cache_refresh)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal
 | 
			
		||||
class LvState(State):
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pv_devices(uuid):
 | 
			
		||||
		rc = []
 | 
			
		||||
		for pv in sorted(cfg.db.lv_contained_pv(uuid)):
 | 
			
		||||
			(pv_uuid, pv_name, pv_segs) = pv
 | 
			
		||||
			pv_obj = cfg.om.get_object_path_by_uuid_lvm_id(pv_uuid, pv_name)
 | 
			
		||||
 | 
			
		||||
			segs_decorate = []
 | 
			
		||||
			for i in pv_segs:
 | 
			
		||||
				segs_decorate.append((dbus.UInt64(i[0]),
 | 
			
		||||
									dbus.UInt64(i[1]),
 | 
			
		||||
									dbus.String(i[2])))
 | 
			
		||||
 | 
			
		||||
			rc.append((dbus.ObjectPath(pv_obj), segs_decorate))
 | 
			
		||||
 | 
			
		||||
		return dbus.Array(rc, signature="(oa(tts))")
 | 
			
		||||
 | 
			
		||||
	def vg_name_lookup(self):
 | 
			
		||||
		return cfg.om.get_object_by_path(self.Vg).Name
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def lvm_id(self):
 | 
			
		||||
		return "%s/%s" % (self.vg_name_lookup(), self.Name)
 | 
			
		||||
 | 
			
		||||
	def identifiers(self):
 | 
			
		||||
		return (self.Uuid, self.lvm_id)
 | 
			
		||||
 | 
			
		||||
	def _get_hidden_lv(self):
 | 
			
		||||
		rc = dbus.Array([], "o")
 | 
			
		||||
 | 
			
		||||
		vg_name = self.vg_name_lookup()
 | 
			
		||||
 | 
			
		||||
		for l in cfg.db.hidden_lvs(self.Uuid):
 | 
			
		||||
			full_name = "%s/%s" % (vg_name, l[1])
 | 
			
		||||
			op = cfg.om.get_object_path_by_uuid_lvm_id(l[0], full_name)
 | 
			
		||||
			assert op
 | 
			
		||||
			rc.append(dbus.ObjectPath(op))
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	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):
 | 
			
		||||
		utils.init_class_from_arguments(self)
 | 
			
		||||
 | 
			
		||||
		# The segtypes is possibly an array with potentially dupes or a single
 | 
			
		||||
		# value
 | 
			
		||||
		self._segs = dbus.Array([], signature='s')
 | 
			
		||||
		if not isinstance(segtypes, list):
 | 
			
		||||
			self._segs.append(dbus.String(segtypes))
 | 
			
		||||
		else:
 | 
			
		||||
			self._segs.extend([dbus.String(x) for x in set(segtypes)])
 | 
			
		||||
 | 
			
		||||
		self.Vg = cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
			vg_uuid, vg_name, vg_obj_path_generate)
 | 
			
		||||
 | 
			
		||||
		self.Devices = LvState._pv_devices(self.Uuid)
 | 
			
		||||
 | 
			
		||||
		if PoolLv:
 | 
			
		||||
			gen = utils.lv_object_path_method(Name, (Attr, layout, role))
 | 
			
		||||
 | 
			
		||||
			self.PoolLv = cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
				pool_lv_uuid, '%s/%s' % (vg_name, PoolLv), gen)
 | 
			
		||||
		else:
 | 
			
		||||
			self.PoolLv = '/'
 | 
			
		||||
 | 
			
		||||
		if OriginLv:
 | 
			
		||||
			self.OriginLv = \
 | 
			
		||||
				cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
					origin_uuid, '%s/%s' % (vg_name, OriginLv),
 | 
			
		||||
					vg_obj_path_generate)
 | 
			
		||||
		else:
 | 
			
		||||
			self.OriginLv = '/'
 | 
			
		||||
 | 
			
		||||
		self.HiddenLvs = self._get_hidden_lv()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def SegType(self):
 | 
			
		||||
		return self._segs
 | 
			
		||||
 | 
			
		||||
	def _object_path_create(self):
 | 
			
		||||
		return utils.lv_object_path_method(
 | 
			
		||||
			self.Name, (self.Attr, self.layout, self.role))
 | 
			
		||||
 | 
			
		||||
	def _object_type_create(self):
 | 
			
		||||
		if self.Attr[0] == 't':
 | 
			
		||||
			return LvThinPool
 | 
			
		||||
		elif self.Attr[0] == 'C':
 | 
			
		||||
			if 'pool' in self.layout:
 | 
			
		||||
				return LvCachePool
 | 
			
		||||
			else:
 | 
			
		||||
				return LvCacheLv
 | 
			
		||||
		elif self.Name[0] == '[':
 | 
			
		||||
			return LvCommon
 | 
			
		||||
		elif self.OriginLv != '/':
 | 
			
		||||
			return LvSnapShot
 | 
			
		||||
		else:
 | 
			
		||||
			return Lv
 | 
			
		||||
 | 
			
		||||
	def create_dbus_object(self, path):
 | 
			
		||||
		if not path:
 | 
			
		||||
			path = cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
				self.Uuid, self.lvm_id, self._object_path_create())
 | 
			
		||||
 | 
			
		||||
		obj_ctor = self._object_type_create()
 | 
			
		||||
		return obj_ctor(path, self)
 | 
			
		||||
 | 
			
		||||
	def creation_signature(self):
 | 
			
		||||
		klass = self._object_type_create()
 | 
			
		||||
		path_method = self._object_path_create()
 | 
			
		||||
		return (klass, path_method)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Uuid', 's')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Path', 's')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SizeBytes', 't')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SegType', 'as')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Vg', 'o')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'OriginLv', 'o')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'PoolLv', 'o')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Devices', "a(oa(tts))")
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'HiddenLvs', "ao")
 | 
			
		||||
@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, 'MetaDataPercent', 'u')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'CopyPercent', 'u')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SyncPercent', 'u')
 | 
			
		||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataSizeBytes', 't')
 | 
			
		||||
class LvCommon(AutomatedProperties):
 | 
			
		||||
	_Tags_meta = ("as", LV_COMMON_INTERFACE)
 | 
			
		||||
	_Roles_meta = ("as", LV_COMMON_INTERFACE)
 | 
			
		||||
	_IsThinVolume_meta = ("b", LV_COMMON_INTERFACE)
 | 
			
		||||
	_IsThinPool_meta = ("b", LV_COMMON_INTERFACE)
 | 
			
		||||
	_Active_meta = ("b", LV_COMMON_INTERFACE)
 | 
			
		||||
	_VolumeType_meta = ("(ss)", LV_COMMON_INTERFACE)
 | 
			
		||||
	_Permissions_meta = ("(ss)", LV_COMMON_INTERFACE)
 | 
			
		||||
	_AllocationPolicy_meta = ("(ss)", LV_COMMON_INTERFACE)
 | 
			
		||||
	_State_meta = ("(ss)", LV_COMMON_INTERFACE)
 | 
			
		||||
	_TargetType_meta = ("(ss)", LV_COMMON_INTERFACE)
 | 
			
		||||
	_Health_meta = ("(ss)", LV_COMMON_INTERFACE)
 | 
			
		||||
	_FixedMinor_meta = ('b', LV_COMMON_INTERFACE)
 | 
			
		||||
	_ZeroBlocks_meta = ('b', LV_COMMON_INTERFACE)
 | 
			
		||||
	_SkipActivation_meta = ('b', LV_COMMON_INTERFACE)
 | 
			
		||||
	_MovePv_meta = ('o', LV_COMMON_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	def _get_move_pv(self):
 | 
			
		||||
		path = None
 | 
			
		||||
 | 
			
		||||
		# It's likely that the move_pv is empty
 | 
			
		||||
		if self.state.move_pv_uuid and self.state.move_pv:
 | 
			
		||||
			path = cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
				self.state.move_pv_uuid, self.state.move_pv)
 | 
			
		||||
		if not path:
 | 
			
		||||
			path = '/'
 | 
			
		||||
		return path
 | 
			
		||||
 | 
			
		||||
	# noinspection PyUnusedLocal,PyPep8Naming
 | 
			
		||||
	def __init__(self, object_path, object_state):
 | 
			
		||||
		super(LvCommon, self).__init__(object_path, lvs_state_retrieve)
 | 
			
		||||
		self.set_interface(LV_COMMON_INTERFACE)
 | 
			
		||||
		self.state = object_state
 | 
			
		||||
		self._move_pv = self._get_move_pv()
 | 
			
		||||
 | 
			
		||||
	@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(
 | 
			
		||||
				LV_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
	@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
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def VolumeType(self):
 | 
			
		||||
		type_map = {'C': 'Cache', 'm': 'mirrored',
 | 
			
		||||
					'M': 'Mirrored without initial sync', 'o': 'origin',
 | 
			
		||||
					'O': 'Origin with merging snapshot', 'r': 'raid',
 | 
			
		||||
					'R': 'Raid without initial sync', 's': 'snapshot',
 | 
			
		||||
					'S': 'merging Snapshot', 'p': 'pvmove',
 | 
			
		||||
					'v': 'virtual', 'i': 'mirror  or  raid  image',
 | 
			
		||||
					'I': 'mirror or raid Image out-of-sync',
 | 
			
		||||
					'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',
 | 
			
		||||
					'-': 'Unspecified'}
 | 
			
		||||
		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 dbus.Struct((self.state.Attr[1], type_map[self.state.Attr[1]]),
 | 
			
		||||
						signature="(ss)")
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def AllocationPolicy(self):
 | 
			
		||||
		type_map = {'a': 'anywhere', 'A': 'anywhere locked',
 | 
			
		||||
					'c': 'contiguous', 'C': 'contiguous locked',
 | 
			
		||||
					'i': 'inherited', 'I': 'inherited locked',
 | 
			
		||||
					'l': 'cling', 'L': 'cling locked',
 | 
			
		||||
					'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'}
 | 
			
		||||
		return dbus.Struct((self.state.Attr[2], type_map[self.state.Attr[2]]),
 | 
			
		||||
						signature="(ss)")
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def FixedMinor(self):
 | 
			
		||||
		return dbus.Boolean(self.state.Attr[3] == 'm')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def State(self):
 | 
			
		||||
		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',
 | 
			
		||||
					'X': 'unknown', '-': 'Unspecified'}
 | 
			
		||||
		return dbus.Struct((self.state.Attr[4], type_map[self.state.Attr[4]]),
 | 
			
		||||
						signature="(ss)")
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def TargetType(self):
 | 
			
		||||
		type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid',
 | 
			
		||||
					's': 'snapshot', 't': 'thin', 'u': 'unknown',
 | 
			
		||||
					'v': 'virtual', '-': 'Unspecified'}
 | 
			
		||||
		return dbus.Struct((self.state.Attr[6], type_map[self.state.Attr[6]]),
 | 
			
		||||
						signature="(ss)")
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def ZeroBlocks(self):
 | 
			
		||||
		return dbus.Boolean(self.state.Attr[7] == 'z')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Health(self):
 | 
			
		||||
		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):
 | 
			
		||||
		return dbus.Boolean(self.state.Attr[9] == 'k')
 | 
			
		||||
 | 
			
		||||
	def vg_name_lookup(self):
 | 
			
		||||
		return self.state.vg_name_lookup()
 | 
			
		||||
 | 
			
		||||
	def lv_full_name(self):
 | 
			
		||||
		return "%s/%s" % (self.state.vg_name_lookup(), self.state.Name)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def identifiers(self):
 | 
			
		||||
		return self.state.identifiers
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Tags(self):
 | 
			
		||||
		return utils.parse_tags(self.state.Tags)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Roles(self):
 | 
			
		||||
		return utils.parse_tags(self.state.role)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def lvm_id(self):
 | 
			
		||||
		return self.state.lvm_id
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def IsThinVolume(self):
 | 
			
		||||
		return dbus.Boolean(self.state.Attr[0] == 'V')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def IsThinPool(self):
 | 
			
		||||
		return dbus.Boolean(self.state.Attr[0] == 't')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Active(self):
 | 
			
		||||
		return dbus.Boolean(self.state.active == "active")
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def MovePv(self):
 | 
			
		||||
		return dbus.ObjectPath(self._move_pv)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class Lv(LvCommon):
 | 
			
		||||
	def _fetch_hidden(self, name):
 | 
			
		||||
 | 
			
		||||
		# The name is vg/name
 | 
			
		||||
		full_name = "%s/%s" % (self.vg_name_lookup(), name)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(full_name)
 | 
			
		||||
 | 
			
		||||
	def _get_data_meta(self):
 | 
			
		||||
 | 
			
		||||
		# Get the data
 | 
			
		||||
		return (self._fetch_hidden(self.state.data_lv),
 | 
			
		||||
				self._fetch_hidden(self.state.metadata_lv))
 | 
			
		||||
 | 
			
		||||
	# noinspection PyUnusedLocal,PyPep8Naming
 | 
			
		||||
	def __init__(self, object_path, object_state):
 | 
			
		||||
		super(Lv, self).__init__(object_path, object_state)
 | 
			
		||||
		self.set_interface(LV_INTERFACE)
 | 
			
		||||
		self.state = object_state
 | 
			
		||||
 | 
			
		||||
	@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
 | 
			
		||||
		rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='ia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Remove(self, tmo, remove_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._remove,
 | 
			
		||||
			(self.Uuid, self.lvm_id, remove_options),
 | 
			
		||||
			cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@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
 | 
			
		||||
		rc, out, err = cmdhandler.lv_rename(lv_name, new_name,
 | 
			
		||||
											rename_options)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='sia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Rename(self, name, tmo, rename_options, cb, cbe):
 | 
			
		||||
		utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name)
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._rename,
 | 
			
		||||
			(self.Uuid, self.lvm_id, name, rename_options),
 | 
			
		||||
			cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='o(tt)a(ott)ia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Move(self, pv_src_obj, pv_source_range,
 | 
			
		||||
				pv_dests_and_ranges,
 | 
			
		||||
				tmo, move_options, cb, cbe):
 | 
			
		||||
 | 
			
		||||
		job_state = JobState()
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
				tmo, background.move,
 | 
			
		||||
				(LV_INTERFACE, self.lvm_id, pv_src_obj, pv_source_range,
 | 
			
		||||
				pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
 | 
			
		||||
				job_state)
 | 
			
		||||
 | 
			
		||||
		background.cmd_runner(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
 | 
			
		||||
 | 
			
		||||
		rc, out, err = cmdhandler.vg_lv_snapshot(
 | 
			
		||||
			lv_name, snapshot_options, name, optional_size)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(full_name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='stia{sv}',
 | 
			
		||||
		out_signature='(oo)',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Snapshot(self, name, optional_size, tmo,
 | 
			
		||||
			snapshot_options, cb, cbe):
 | 
			
		||||
 | 
			
		||||
		utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name)
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._snap_shot,
 | 
			
		||||
			(self.Uuid, self.lvm_id, name,
 | 
			
		||||
			optional_size, snapshot_options), cb, cbe)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _resize(lv_uuid, lv_name, new_size_bytes, pv_dests_and_ranges,
 | 
			
		||||
				resize_options):
 | 
			
		||||
		# Make sure we have a dbus object representing it
 | 
			
		||||
		pv_dests = []
 | 
			
		||||
		dbo = LvCommon.validate_dbus_object(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])
 | 
			
		||||
 | 
			
		||||
				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
 | 
			
		||||
 | 
			
		||||
		size_change = new_size_bytes - dbo.SizeBytes
 | 
			
		||||
		rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change,
 | 
			
		||||
											pv_dests, resize_options)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		return "/"
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='ta(ott)ia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Resize(self, new_size_bytes, pv_dests_and_ranges, tmo,
 | 
			
		||||
			resize_options, cb, cbe):
 | 
			
		||||
		"""
 | 
			
		||||
		Resize a LV
 | 
			
		||||
		:param new_size_bytes: The requested final size in bytes
 | 
			
		||||
		:param pv_dests_and_ranges: An array of pv object paths and src &
 | 
			
		||||
									dst. segment ranges
 | 
			
		||||
		:param tmo: -1 to wait forever, 0 to return job immediately, else
 | 
			
		||||
					number of seconds to wait for operation to complete
 | 
			
		||||
					before getting a job
 | 
			
		||||
		:param resize_options: key/value hash of options
 | 
			
		||||
		:param cb:  Used by framework not client facing API
 | 
			
		||||
		:param cbe: Used by framework not client facing API
 | 
			
		||||
		:return: '/' if complete, else job object path
 | 
			
		||||
		"""
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._resize,
 | 
			
		||||
			(self.Uuid, self.lvm_id, round_size(new_size_bytes),
 | 
			
		||||
			pv_dests_and_ranges,
 | 
			
		||||
			resize_options), cb, cbe, return_tuple=False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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)
 | 
			
		||||
		rc, out, err = cmdhandler.activate_deactivate(
 | 
			
		||||
			'lvchange', lv_name, activate, control_flags, options)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='tia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Activate(self, control_flags, tmo, activate_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._lv_activate_deactivate,
 | 
			
		||||
			(self.state.Uuid, self.state.lvm_id, True,
 | 
			
		||||
			control_flags, activate_options),
 | 
			
		||||
			cb, cbe, return_tuple=False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	# noinspection PyProtectedMember
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='tia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Deactivate(self, control_flags, tmo, activate_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._lv_activate_deactivate,
 | 
			
		||||
			(self.state.Uuid, self.state.lvm_id, False,
 | 
			
		||||
			control_flags, activate_options),
 | 
			
		||||
			cb, cbe, return_tuple=False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@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)
 | 
			
		||||
		rc, out, err = cmdhandler.lv_tag(
 | 
			
		||||
			lv_name, tags_add, tags_del, tag_options)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='asia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def TagsAdd(self, tags, tmo, tag_options, cb, cbe):
 | 
			
		||||
 | 
			
		||||
		for t in tags:
 | 
			
		||||
			utils.validate_tag(LV_INTERFACE, t)
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._add_rm_tags,
 | 
			
		||||
			(self.state.Uuid, self.state.lvm_id,
 | 
			
		||||
			tags, None, tag_options),
 | 
			
		||||
			cb, cbe, return_tuple=False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_INTERFACE,
 | 
			
		||||
		in_signature='asia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def TagsDel(self, tags, tmo, tag_options, cb, cbe):
 | 
			
		||||
 | 
			
		||||
		for t in tags:
 | 
			
		||||
			utils.validate_tag(LV_INTERFACE, t)
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Lv._add_rm_tags,
 | 
			
		||||
			(self.state.Uuid, self.state.lvm_id,
 | 
			
		||||
			None, tags, tag_options),
 | 
			
		||||
			cb, cbe, return_tuple=False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class LvThinPool(Lv):
 | 
			
		||||
	_DataLv_meta = ("o", THIN_POOL_INTERFACE)
 | 
			
		||||
	_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	def __init__(self, object_path, object_state):
 | 
			
		||||
		super(LvThinPool, self).__init__(object_path, object_state)
 | 
			
		||||
		self.set_interface(THIN_POOL_INTERFACE)
 | 
			
		||||
		self._data_lv, self._metadata_lv = self._get_data_meta()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def DataLv(self):
 | 
			
		||||
		return dbus.ObjectPath(self._data_lv)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def MetaDataLv(self):
 | 
			
		||||
		return dbus.ObjectPath(self._metadata_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)
 | 
			
		||||
 | 
			
		||||
		rc, out, err = cmdhandler.lv_lv_create(
 | 
			
		||||
			lv_name, create_options, name, size_bytes)
 | 
			
		||||
		LvCommon.handle_execute(rc, out, err)
 | 
			
		||||
		full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
 | 
			
		||||
		return cfg.om.get_object_path_by_lvm_id(full_name)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=THIN_POOL_INTERFACE,
 | 
			
		||||
		in_signature='stia{sv}',
 | 
			
		||||
		out_signature='(oo)',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def LvCreate(self, name, size_bytes, tmo, create_options, cb, cbe):
 | 
			
		||||
		utils.validate_lv_name(THIN_POOL_INTERFACE, self.vg_name_lookup(), name)
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, LvThinPool._lv_create,
 | 
			
		||||
			(self.Uuid, self.lvm_id, name,
 | 
			
		||||
			round_size(size_bytes), create_options), cb, cbe)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class LvCachePool(Lv):
 | 
			
		||||
	_DataLv_meta = ("o", CACHE_POOL_INTERFACE)
 | 
			
		||||
	_MetaDataLv_meta = ("o", CACHE_POOL_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	def __init__(self, object_path, object_state):
 | 
			
		||||
		super(LvCachePool, self).__init__(object_path, object_state)
 | 
			
		||||
		self.set_interface(CACHE_POOL_INTERFACE)
 | 
			
		||||
		self._data_lv, self._metadata_lv = self._get_data_meta()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def DataLv(self):
 | 
			
		||||
		return dbus.ObjectPath(self._data_lv)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def MetaDataLv(self):
 | 
			
		||||
		return dbus.ObjectPath(self._metadata_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)
 | 
			
		||||
 | 
			
		||||
		# 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_cache_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=CACHE_POOL_INTERFACE,
 | 
			
		||||
		in_signature='oia{sv}',
 | 
			
		||||
		out_signature='(oo)',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def CacheLv(self, lv_object, tmo, cache_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, LvCachePool._cache_lv,
 | 
			
		||||
			(self.Uuid, self.lvm_id, lv_object,
 | 
			
		||||
			cache_options), cb, cbe)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class LvCacheLv(Lv):
 | 
			
		||||
	_CachePool_meta = ("o", LV_CACHED)
 | 
			
		||||
 | 
			
		||||
	def __init__(self, object_path, object_state):
 | 
			
		||||
		super(LvCacheLv, self).__init__(object_path, object_state)
 | 
			
		||||
		self.set_interface(LV_CACHED)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def CachePool(self):
 | 
			
		||||
		return dbus.ObjectPath(self.state.PoolLv)
 | 
			
		||||
 | 
			
		||||
	@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)
 | 
			
		||||
 | 
			
		||||
		# Get current cache name
 | 
			
		||||
		cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
 | 
			
		||||
 | 
			
		||||
		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()
 | 
			
		||||
 | 
			
		||||
			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))
 | 
			
		||||
 | 
			
		||||
		return uncached_lv_path
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=LV_CACHED,
 | 
			
		||||
		in_signature='bia{sv}',
 | 
			
		||||
		out_signature='(oo)',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def DetachCachePool(self, destroy_cache, tmo, detach_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, LvCacheLv._detach_lv,
 | 
			
		||||
			(self.Uuid, self.lvm_id, detach_options,
 | 
			
		||||
			destroy_cache), cb, cbe)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class LvSnapShot(Lv):
 | 
			
		||||
	def __init__(self, object_path, object_state):
 | 
			
		||||
		super(LvSnapShot, self).__init__(object_path, object_state)
 | 
			
		||||
		self.set_interface(SNAPSHOT_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=SNAPSHOT_INTERFACE,
 | 
			
		||||
		in_signature='ia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Merge(self, tmo, merge_options, cb, cbe):
 | 
			
		||||
		job_state = JobState()
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(tmo, background.merge,
 | 
			
		||||
							(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
 | 
			
		||||
							merge_options, job_state), cb, cbe, False,
 | 
			
		||||
							job_state)
 | 
			
		||||
		background.cmd_runner(r)
 | 
			
		||||
@@ -1,269 +0,0 @@
 | 
			
		||||
#!@PYTHON3@
 | 
			
		||||
 | 
			
		||||
# 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/>.
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2015-2016, Vratislav Podzimek <vpodzime@redhat.com>
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
import shlex
 | 
			
		||||
from fcntl import fcntl, F_GETFL, F_SETFL
 | 
			
		||||
import os
 | 
			
		||||
import traceback
 | 
			
		||||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
import time
 | 
			
		||||
import select
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
	import simplejson as json
 | 
			
		||||
except ImportError:
 | 
			
		||||
	import json
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from lvmdbusd.cfg import LVM_CMD
 | 
			
		||||
from lvmdbusd.utils import log_debug, log_error, add_no_notify
 | 
			
		||||
 | 
			
		||||
SHELL_PROMPT = "lvm> "
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _quote_arg(arg):
 | 
			
		||||
	if len(shlex.split(arg)) > 1:
 | 
			
		||||
		return '"%s"' % arg
 | 
			
		||||
	else:
 | 
			
		||||
		return arg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LVMShellProxy(object):
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _read(stream):
 | 
			
		||||
		tmp = stream.read()
 | 
			
		||||
		if tmp:
 | 
			
		||||
			return tmp.decode("utf-8")
 | 
			
		||||
		return ''
 | 
			
		||||
 | 
			
		||||
	# 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):
 | 
			
		||||
		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:
 | 
			
		||||
			try:
 | 
			
		||||
				rd_fd = [
 | 
			
		||||
					self.lvm_shell.stdout.fileno(),
 | 
			
		||||
					self.report_stream.fileno(),
 | 
			
		||||
					self.lvm_shell.stderr.fileno()]
 | 
			
		||||
				ready = select.select(rd_fd, [], [], 2)
 | 
			
		||||
 | 
			
		||||
				for r in ready[0]:
 | 
			
		||||
					if r == self.lvm_shell.stdout.fileno():
 | 
			
		||||
						stdout += LVMShellProxy._read(self.lvm_shell.stdout)
 | 
			
		||||
					elif r == self.report_stream.fileno():
 | 
			
		||||
						report += LVMShellProxy._read(self.report_stream)
 | 
			
		||||
					elif r == self.lvm_shell.stderr.fileno():
 | 
			
		||||
						stderr += LVMShellProxy._read(self.lvm_shell.stderr)
 | 
			
		||||
 | 
			
		||||
				# 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
 | 
			
		||||
 | 
			
		||||
	def _write_cmd(self, cmd):
 | 
			
		||||
		cmd_bytes = bytes(cmd, "utf-8")
 | 
			
		||||
		num_written = self.lvm_shell.stdin.write(cmd_bytes)
 | 
			
		||||
		assert (num_written == len(cmd_bytes))
 | 
			
		||||
		self.lvm_shell.stdin.flush()
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _make_non_block(stream):
 | 
			
		||||
		flags = fcntl(stream, F_GETFL)
 | 
			
		||||
		fcntl(stream, F_SETFL, flags | os.O_NONBLOCK)
 | 
			
		||||
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
 | 
			
		||||
		# Create a temp directory
 | 
			
		||||
		tmp_dir = tempfile.mkdtemp(prefix="lvmdbus_")
 | 
			
		||||
		tmp_file = "%s/lvmdbus_report" % (tmp_dir)
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			# Lets create fifo for the report output
 | 
			
		||||
			os.mkfifo(tmp_file, 0o600)
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
		# Setup the environment for using our own socket for reporting
 | 
			
		||||
		local_env = copy.deepcopy(os.environ)
 | 
			
		||||
		local_env["LVM_REPORT_FD"] = "32"
 | 
			
		||||
		local_env["LVM_COMMAND_PROFILE"] = "lvmdbusd"
 | 
			
		||||
 | 
			
		||||
		# Disable the abort logic if lvm logs too much, which easily happens
 | 
			
		||||
		# when utilizing the lvm shell.
 | 
			
		||||
		local_env["LVM_LOG_FILE_MAX_LINES"] = "0"
 | 
			
		||||
 | 
			
		||||
		# run the lvm shell
 | 
			
		||||
		self.lvm_shell = subprocess.Popen(
 | 
			
		||||
			[LVM_CMD + " 32>%s" % tmp_file],
 | 
			
		||||
			stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=local_env,
 | 
			
		||||
			stderr=subprocess.PIPE, close_fds=True, shell=True)
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			LVMShellProxy._make_non_block(self.lvm_shell.stdout)
 | 
			
		||||
			LVMShellProxy._make_non_block(self.lvm_shell.stderr)
 | 
			
		||||
 | 
			
		||||
			# wait for the first prompt
 | 
			
		||||
			errors = self._read_until_prompt(no_output=True)[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
 | 
			
		||||
			os.unlink(tmp_file)
 | 
			
		||||
			os.rmdir(tmp_dir)
 | 
			
		||||
 | 
			
		||||
	def get_error_msg(self):
 | 
			
		||||
		# We got an error, lets go fetch the error message
 | 
			
		||||
		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']
 | 
			
		||||
 | 
			
		||||
			return error_msg
 | 
			
		||||
 | 
			
		||||
		return 'No error reason provided! (missing "log" section)'
 | 
			
		||||
 | 
			
		||||
	def call_lvm(self, argv, debug=False):
 | 
			
		||||
		rc = 1
 | 
			
		||||
		error_msg = ""
 | 
			
		||||
 | 
			
		||||
		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"
 | 
			
		||||
 | 
			
		||||
		# run the command by writing it to the shell's STDIN
 | 
			
		||||
		self._write_cmd(cmd)
 | 
			
		||||
 | 
			
		||||
		# read everything from the STDOUT to the next prompt
 | 
			
		||||
		stdout, report_json, stderr = self._read_until_prompt()
 | 
			
		||||
 | 
			
		||||
		# Parse the report to see what happened
 | 
			
		||||
		if 'log' in report_json:
 | 
			
		||||
			if report_json['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
 | 
			
		||||
 | 
			
		||||
	def exit_shell(self):
 | 
			
		||||
		try:
 | 
			
		||||
			self._write_cmd('exit\n')
 | 
			
		||||
		except Exception as e:
 | 
			
		||||
			log_error(str(e))
 | 
			
		||||
 | 
			
		||||
	def __del__(self):
 | 
			
		||||
		try:
 | 
			
		||||
			self.lvm_shell.terminate()
 | 
			
		||||
		except:
 | 
			
		||||
			pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
	shell = LVMShellProxy()
 | 
			
		||||
	in_line = "start"
 | 
			
		||||
	try:
 | 
			
		||||
		while in_line:
 | 
			
		||||
			in_line = input("lvm> ")
 | 
			
		||||
			if in_line:
 | 
			
		||||
				start = time.time()
 | 
			
		||||
				ret, out, err = shell.call_lvm(in_line.split())
 | 
			
		||||
				end = time.time()
 | 
			
		||||
 | 
			
		||||
				print(("RC: %d" % ret))
 | 
			
		||||
				print(("OUT:\n%s" % out))
 | 
			
		||||
				print(("ERR:\n%s" % err))
 | 
			
		||||
 | 
			
		||||
				print("Command     = %f seconds" % (end - start))
 | 
			
		||||
	except KeyboardInterrupt:
 | 
			
		||||
		pass
 | 
			
		||||
	except EOFError:
 | 
			
		||||
		pass
 | 
			
		||||
	except Exception:
 | 
			
		||||
		traceback.print_exc(file=sys.stdout)
 | 
			
		||||
@@ -1,540 +0,0 @@
 | 
			
		||||
#!@PYTHON3@
 | 
			
		||||
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
import pprint as prettyprint
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from lvmdbusd import cmdhandler
 | 
			
		||||
from lvmdbusd.utils import log_debug, log_error
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DataStore(object):
 | 
			
		||||
	def __init__(self, usejson=True):
 | 
			
		||||
		self.pvs = {}
 | 
			
		||||
		self.vgs = {}
 | 
			
		||||
		self.lvs = {}
 | 
			
		||||
		self.pv_lvs = {}
 | 
			
		||||
		self.lv_pvs = {}
 | 
			
		||||
		self.lvs_hidden = {}
 | 
			
		||||
 | 
			
		||||
		self.pv_path_to_uuid = {}
 | 
			
		||||
		self.vg_name_to_uuid = {}
 | 
			
		||||
		self.lv_full_name_to_uuid = {}
 | 
			
		||||
 | 
			
		||||
		self.lvs_in_vgs = {}
 | 
			
		||||
		self.pvs_in_vgs = {}
 | 
			
		||||
 | 
			
		||||
		# self.refresh()
 | 
			
		||||
		self.num_refreshes = 0
 | 
			
		||||
 | 
			
		||||
		if usejson:
 | 
			
		||||
			self.json = cmdhandler.supports_json()
 | 
			
		||||
		else:
 | 
			
		||||
			self.json = usejson
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _insert_record(table, key, record, allowed_multiple):
 | 
			
		||||
		if key in table:
 | 
			
		||||
			existing = table[key]
 | 
			
		||||
 | 
			
		||||
			for rec_k, rec_v in record.items():
 | 
			
		||||
				if rec_k in allowed_multiple:
 | 
			
		||||
					# This column name allows us to store multiple value for
 | 
			
		||||
					# each type
 | 
			
		||||
					if not isinstance(existing[rec_k], list):
 | 
			
		||||
						existing_value = existing[rec_k]
 | 
			
		||||
						existing[rec_k] = [existing_value, rec_v]
 | 
			
		||||
					else:
 | 
			
		||||
						existing[rec_k].append(rec_v)
 | 
			
		||||
				else:
 | 
			
		||||
					# If something is not expected to have changing values
 | 
			
		||||
					# lets ensure that
 | 
			
		||||
					if existing[rec_k] != rec_v:
 | 
			
		||||
						raise RuntimeError(
 | 
			
		||||
							"existing[%s]=%s != %s" %
 | 
			
		||||
							(rec_k, str(existing[rec_k]),
 | 
			
		||||
							str(rec_v)))
 | 
			
		||||
		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'])
 | 
			
		||||
 | 
			
		||||
		c_pvs = OrderedDict()
 | 
			
		||||
		c_lookup = {}
 | 
			
		||||
		c_pvs_in_vgs = {}
 | 
			
		||||
 | 
			
		||||
		for p in pvs:
 | 
			
		||||
			DataStore._insert_record(
 | 
			
		||||
				c_pvs, p['pv_uuid'], p,
 | 
			
		||||
				['pvseg_start', 'pvseg_size', 'segtype'])
 | 
			
		||||
 | 
			
		||||
		DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup)
 | 
			
		||||
		return c_pvs, c_lookup, c_pvs_in_vgs
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_pvs_json(_all):
 | 
			
		||||
 | 
			
		||||
		c_pvs = OrderedDict()
 | 
			
		||||
		c_lookup = {}
 | 
			
		||||
		c_pvs_in_vgs = {}
 | 
			
		||||
 | 
			
		||||
		# Each item item in the report is a collection of information pertaining
 | 
			
		||||
		# to the vg
 | 
			
		||||
		for r in _all['report']:
 | 
			
		||||
			tmp_pv = []
 | 
			
		||||
 | 
			
		||||
			# Get the pv data for this VG.
 | 
			
		||||
			if 'pv' in r:
 | 
			
		||||
				tmp_pv.extend(r['pv'])
 | 
			
		||||
 | 
			
		||||
				# Sort them
 | 
			
		||||
				sorted_tmp_pv = sorted(tmp_pv, key=lambda pk: pk['pv_name'])
 | 
			
		||||
 | 
			
		||||
				# Add them to result set
 | 
			
		||||
				for p in sorted_tmp_pv:
 | 
			
		||||
					c_pvs[p['pv_uuid']] = p
 | 
			
		||||
 | 
			
		||||
				if 'pvseg' in r:
 | 
			
		||||
					for s in r['pvseg']:
 | 
			
		||||
						r = c_pvs[s['pv_uuid']]
 | 
			
		||||
						r.setdefault('pvseg_start', []).append(s['pvseg_start'])
 | 
			
		||||
						r.setdefault('pvseg_size', []).append(s['pvseg_size'])
 | 
			
		||||
						r.setdefault('segtype', []).append(s['segtype'])
 | 
			
		||||
 | 
			
		||||
				# TODO: Remove this bug work around when we have orphan segs.
 | 
			
		||||
				for i in c_pvs.values():
 | 
			
		||||
					if 'pvseg_start' not in i:
 | 
			
		||||
						i['pvseg_start'] = '0'
 | 
			
		||||
						i['pvseg_size'] = i['pv_pe_count']
 | 
			
		||||
						i['segtype'] = 'free'
 | 
			
		||||
 | 
			
		||||
		DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup)
 | 
			
		||||
 | 
			
		||||
		return c_pvs, c_lookup, c_pvs_in_vgs
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_vgs(_vgs):
 | 
			
		||||
		vgs = sorted(_vgs, key=lambda vk: vk['vg_name'])
 | 
			
		||||
 | 
			
		||||
		c_vgs = OrderedDict()
 | 
			
		||||
		c_lookup = {}
 | 
			
		||||
 | 
			
		||||
		for i in vgs:
 | 
			
		||||
			c_lookup[i['vg_name']] = i['vg_uuid']
 | 
			
		||||
			DataStore._insert_record(c_vgs, i['vg_uuid'], i, [])
 | 
			
		||||
 | 
			
		||||
		return c_vgs, c_lookup
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_vgs_json(_all):
 | 
			
		||||
 | 
			
		||||
		tmp_vg = []
 | 
			
		||||
		for r in _all['report']:
 | 
			
		||||
			# Get the pv data for this VG.
 | 
			
		||||
			if 'vg' in r:
 | 
			
		||||
				tmp_vg.extend(r['vg'])
 | 
			
		||||
 | 
			
		||||
		# Sort for consistent output, however this is optional
 | 
			
		||||
		vgs = sorted(tmp_vg, key=lambda vk: vk['vg_name'])
 | 
			
		||||
 | 
			
		||||
		c_vgs = OrderedDict()
 | 
			
		||||
		c_lookup = {}
 | 
			
		||||
 | 
			
		||||
		for i in vgs:
 | 
			
		||||
			c_lookup[i['vg_name']] = i['vg_uuid']
 | 
			
		||||
			c_vgs[i['vg_uuid']] = i
 | 
			
		||||
 | 
			
		||||
		return c_vgs, c_lookup
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_lvs_common(c_lvs, c_lv_full_lookup):
 | 
			
		||||
 | 
			
		||||
		c_lvs_in_vgs = OrderedDict()
 | 
			
		||||
		c_lvs_hidden = OrderedDict()
 | 
			
		||||
 | 
			
		||||
		for i in c_lvs.values():
 | 
			
		||||
			if i['vg_uuid'] not in c_lvs_in_vgs:
 | 
			
		||||
				c_lvs_in_vgs[i['vg_uuid']] = []
 | 
			
		||||
 | 
			
		||||
			c_lvs_in_vgs[
 | 
			
		||||
				i['vg_uuid']].append(
 | 
			
		||||
					(i['lv_name'],
 | 
			
		||||
					(i['lv_attr'], i['lv_layout'], i['lv_role']),
 | 
			
		||||
					i['lv_uuid']))
 | 
			
		||||
 | 
			
		||||
			if i['lv_parent']:
 | 
			
		||||
				# Lookup what the parent refers too
 | 
			
		||||
				parent_name = i['lv_parent']
 | 
			
		||||
				full_parent_name = "%s/%s" % (i['vg_name'], parent_name)
 | 
			
		||||
				if full_parent_name not in c_lv_full_lookup:
 | 
			
		||||
					parent_name = '[%s]' % (parent_name)
 | 
			
		||||
					full_parent_name = "%s/%s" % (i['vg_name'], parent_name)
 | 
			
		||||
 | 
			
		||||
				parent_uuid = c_lv_full_lookup[full_parent_name]
 | 
			
		||||
 | 
			
		||||
				if parent_uuid not in c_lvs_hidden:
 | 
			
		||||
					c_lvs_hidden[parent_uuid] = []
 | 
			
		||||
 | 
			
		||||
				c_lvs_hidden[parent_uuid].append(
 | 
			
		||||
					(i['lv_uuid'], i['lv_name']))
 | 
			
		||||
 | 
			
		||||
		return c_lvs, c_lvs_in_vgs, c_lvs_hidden, c_lv_full_lookup
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_lvs(_lvs):
 | 
			
		||||
		lvs = sorted(_lvs, key=lambda vk: vk['lv_name'])
 | 
			
		||||
 | 
			
		||||
		c_lvs = OrderedDict()
 | 
			
		||||
		c_lv_full_lookup = OrderedDict()
 | 
			
		||||
 | 
			
		||||
		for i in lvs:
 | 
			
		||||
			full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
 | 
			
		||||
			c_lv_full_lookup[full_name] = i['lv_uuid']
 | 
			
		||||
			DataStore._insert_record(
 | 
			
		||||
				c_lvs, i['lv_uuid'], i,
 | 
			
		||||
				['seg_pe_ranges', 'segtype'])
 | 
			
		||||
 | 
			
		||||
		return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_lvs_json(_all):
 | 
			
		||||
 | 
			
		||||
		c_lvs = OrderedDict()
 | 
			
		||||
		c_lv_full_lookup = {}
 | 
			
		||||
 | 
			
		||||
		# Each item item in the report is a collection of information pertaining
 | 
			
		||||
		# to the vg
 | 
			
		||||
		for r in _all['report']:
 | 
			
		||||
			# Get the lv data for this VG.
 | 
			
		||||
			if 'lv' in r:
 | 
			
		||||
				# Add them to result set
 | 
			
		||||
				for i in r['lv']:
 | 
			
		||||
					full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
 | 
			
		||||
					c_lv_full_lookup[full_name] = i['lv_uuid']
 | 
			
		||||
					c_lvs[i['lv_uuid']] = i
 | 
			
		||||
 | 
			
		||||
				# Add in the segment data
 | 
			
		||||
				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('segtype', []).append(s['segtype'])
 | 
			
		||||
 | 
			
		||||
		return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _make_list(l):
 | 
			
		||||
		if not isinstance(l, list):
 | 
			
		||||
			l = [l]
 | 
			
		||||
		return l
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _parse_seg_entry(se, segtype):
 | 
			
		||||
		if se:
 | 
			
		||||
			# print("_parse_seg_entry %s %s" % (str(se), str(segtype)))
 | 
			
		||||
			device, segs = se.split(":")
 | 
			
		||||
			start, end = segs.split('-')
 | 
			
		||||
			return (device, (start, end), segtype)
 | 
			
		||||
		else:
 | 
			
		||||
			return ("", (), segtype)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _build_segments(l, seg_types):
 | 
			
		||||
		rc = []
 | 
			
		||||
		l = DataStore._make_list(l)
 | 
			
		||||
		s = DataStore._make_list(seg_types)
 | 
			
		||||
 | 
			
		||||
		assert len(l) == len(s)
 | 
			
		||||
		ls = list(zip(l, s))
 | 
			
		||||
 | 
			
		||||
		for i in ls:
 | 
			
		||||
			if ' ' in i[0]:
 | 
			
		||||
				tmp = i[0].split(' ')
 | 
			
		||||
				for t in tmp:
 | 
			
		||||
					rc.append(DataStore._parse_seg_entry(t, i[1]))
 | 
			
		||||
			else:
 | 
			
		||||
				rc.append(DataStore._parse_seg_entry(*i))
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pv_device_lv_entry(table, pv_device, lv_uuid, meta, lv_attr,
 | 
			
		||||
							segment_info):
 | 
			
		||||
 | 
			
		||||
		if pv_device not in table:
 | 
			
		||||
			table[pv_device] = {}
 | 
			
		||||
 | 
			
		||||
		if lv_uuid not in table[pv_device]:
 | 
			
		||||
			table[pv_device][lv_uuid] = {}
 | 
			
		||||
			table[pv_device][lv_uuid]['segs'] = [segment_info]
 | 
			
		||||
			table[pv_device][lv_uuid]['name'] = meta
 | 
			
		||||
			table[pv_device][lv_uuid]['meta'] = lv_attr
 | 
			
		||||
		else:
 | 
			
		||||
			table[pv_device][lv_uuid]['segs'].append(segment_info)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pv_device_lv_format(pv_device_lvs):
 | 
			
		||||
		rc = {}
 | 
			
		||||
 | 
			
		||||
		for pv_device, pd in pv_device_lvs.items():
 | 
			
		||||
			lvs = []
 | 
			
		||||
			for lv_uuid, ld in sorted(pd.items()):
 | 
			
		||||
				lvs.append((lv_uuid, ld['name'], ld['meta'], ld['segs']))
 | 
			
		||||
 | 
			
		||||
			rc[pv_device] = lvs
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _lvs_device_pv_entry(table, lv_uuid, pv_device, pv_uuid, segment_info):
 | 
			
		||||
		if lv_uuid not in table:
 | 
			
		||||
			table[lv_uuid] = {}
 | 
			
		||||
 | 
			
		||||
		if pv_device not in table[lv_uuid]:
 | 
			
		||||
			table[lv_uuid][pv_device] = {}
 | 
			
		||||
			table[lv_uuid][pv_device]['segs'] = [segment_info]
 | 
			
		||||
			table[lv_uuid][pv_device]['pv_uuid'] = pv_uuid
 | 
			
		||||
		else:
 | 
			
		||||
			table[lv_uuid][pv_device]['segs'].append(segment_info)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _lvs_device_pv_format(lvs_device_pvs):
 | 
			
		||||
		rc = {}
 | 
			
		||||
 | 
			
		||||
		for lv_uuid, ld in lvs_device_pvs.items():
 | 
			
		||||
			pvs = []
 | 
			
		||||
			for pv_device, pd in sorted(ld.items()):
 | 
			
		||||
				pvs.append((pd['pv_uuid'], pv_device, pd['segs']))
 | 
			
		||||
 | 
			
		||||
			rc[lv_uuid] = pvs
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	def _parse_pv_in_lvs(self):
 | 
			
		||||
		pv_device_lvs = {}  # What LVs are stored on a PV
 | 
			
		||||
		lvs_device_pv = {}  # Where LV data is stored
 | 
			
		||||
 | 
			
		||||
		for i in self.lvs.values():
 | 
			
		||||
			segs = self._build_segments(i['seg_pe_ranges'], i['segtype'])
 | 
			
		||||
			for s in segs:
 | 
			
		||||
				# We are referring to physical device
 | 
			
		||||
				if '/dev/' in s[0]:
 | 
			
		||||
					device, r, seg_type = s
 | 
			
		||||
 | 
			
		||||
					DataStore._pv_device_lv_entry(
 | 
			
		||||
						pv_device_lvs, device, i['lv_uuid'], i['lv_name'],
 | 
			
		||||
						(i['lv_attr'], i['lv_layout'], i['lv_role']),
 | 
			
		||||
						(r[0], r[1], seg_type))
 | 
			
		||||
 | 
			
		||||
					# (pv_name, pv_segs, pv_uuid)
 | 
			
		||||
					DataStore._lvs_device_pv_entry(
 | 
			
		||||
						lvs_device_pv, i['lv_uuid'], device,
 | 
			
		||||
						self.pv_path_to_uuid[device], (r[0], r[1], seg_type))
 | 
			
		||||
				else:
 | 
			
		||||
					# TODO Handle the case where the segments refer to a LV
 | 
			
		||||
					# and not a PV
 | 
			
		||||
					pass
 | 
			
		||||
					# print("Handle this %s %s %s" % (s[0], s[1], s[2]))
 | 
			
		||||
 | 
			
		||||
		# Convert form to needed result for consumption
 | 
			
		||||
		pv_device_lvs_result = DataStore._pv_device_lv_format(pv_device_lvs)
 | 
			
		||||
		lvs_device_pv_result = DataStore._lvs_device_pv_format(lvs_device_pv)
 | 
			
		||||
 | 
			
		||||
		return pv_device_lvs_result, lvs_device_pv_result
 | 
			
		||||
 | 
			
		||||
	def refresh(self, log=True):
 | 
			
		||||
		"""
 | 
			
		||||
		Go out and query lvm for the latest data in as few trips as possible
 | 
			
		||||
		:param log  Add debug log entry/exit messages
 | 
			
		||||
		:return: None
 | 
			
		||||
		"""
 | 
			
		||||
		self.num_refreshes += 1
 | 
			
		||||
		if log:
 | 
			
		||||
			log_debug("lvmdb - refresh entry")
 | 
			
		||||
 | 
			
		||||
		# Grab everything first then parse it
 | 
			
		||||
		if self.json:
 | 
			
		||||
			# Do a single lvm retrieve for everything in json
 | 
			
		||||
			a = cmdhandler.lvm_full_report_json()
 | 
			
		||||
 | 
			
		||||
			_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs_json(a)
 | 
			
		||||
			_vgs, _vgs_lookup = self._parse_vgs_json(a)
 | 
			
		||||
			_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs_json(a)
 | 
			
		||||
 | 
			
		||||
		else:
 | 
			
		||||
			_raw_pvs = cmdhandler.pv_retrieve_with_segs()
 | 
			
		||||
			_raw_vgs = cmdhandler.vg_retrieve(None)
 | 
			
		||||
			_raw_lvs = cmdhandler.lv_retrieve_with_segments()
 | 
			
		||||
 | 
			
		||||
			_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs(_raw_pvs)
 | 
			
		||||
			_vgs, _vgs_lookup = self._parse_vgs(_raw_vgs)
 | 
			
		||||
			_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs(_raw_lvs)
 | 
			
		||||
 | 
			
		||||
		# Set all
 | 
			
		||||
		self.pvs = _pvs
 | 
			
		||||
		self.pv_path_to_uuid = _pvs_lookup
 | 
			
		||||
		self.vg_name_to_uuid = _vgs_lookup
 | 
			
		||||
		self.lv_full_name_to_uuid = _lvs_lookup
 | 
			
		||||
 | 
			
		||||
		self.vgs = _vgs
 | 
			
		||||
		self.lvs = _lvs
 | 
			
		||||
		self.lvs_in_vgs = _lvs_in_vgs
 | 
			
		||||
		self.pvs_in_vgs = _pvs_in_vgs
 | 
			
		||||
		self.lvs_hidden = _lvs_hidden
 | 
			
		||||
 | 
			
		||||
		# Create lookup table for which LV and segments are on each PV
 | 
			
		||||
		self.pv_lvs, self.lv_pvs = self._parse_pv_in_lvs()
 | 
			
		||||
 | 
			
		||||
		if log:
 | 
			
		||||
			log_debug("lvmdb - refresh exit")
 | 
			
		||||
 | 
			
		||||
	def fetch_pvs(self, pv_name):
 | 
			
		||||
		if not pv_name:
 | 
			
		||||
			return self.pvs.values()
 | 
			
		||||
		else:
 | 
			
		||||
			rc = []
 | 
			
		||||
			for s in pv_name:
 | 
			
		||||
				# Ths user could be using a symlink instead of the actual
 | 
			
		||||
				# block device, make sure we are using actual block device file
 | 
			
		||||
				# if the pv name isn't in the lookup
 | 
			
		||||
				if s not in self.pv_path_to_uuid:
 | 
			
		||||
					s = os.path.realpath(s)
 | 
			
		||||
				rc.append(self.pvs[self.pv_path_to_uuid[s]])
 | 
			
		||||
			return rc
 | 
			
		||||
 | 
			
		||||
	def pv_missing(self, pv_uuid):
 | 
			
		||||
		if pv_uuid in self.pvs:
 | 
			
		||||
			if self.pvs[pv_uuid]['pv_missing'] == '':
 | 
			
		||||
				return False
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
	def fetch_vgs(self, vg_name):
 | 
			
		||||
		if not vg_name:
 | 
			
		||||
			return self.vgs.values()
 | 
			
		||||
		else:
 | 
			
		||||
			rc = []
 | 
			
		||||
			for s in vg_name:
 | 
			
		||||
				rc.append(self.vgs[self.vg_name_to_uuid[s]])
 | 
			
		||||
			return rc
 | 
			
		||||
 | 
			
		||||
	def fetch_lvs(self, lv_names):
 | 
			
		||||
		try:
 | 
			
		||||
			if not lv_names:
 | 
			
		||||
				return self.lvs.values()
 | 
			
		||||
			else:
 | 
			
		||||
				rc = []
 | 
			
		||||
				for s in lv_names:
 | 
			
		||||
					rc.append(self.lvs[self.lv_full_name_to_uuid[s]])
 | 
			
		||||
				return rc
 | 
			
		||||
		except KeyError as ke:
 | 
			
		||||
			log_error("Key %s not found!" % (str(lv_names)))
 | 
			
		||||
			log_error("lv name to uuid lookup")
 | 
			
		||||
			for keys in sorted(self.lv_full_name_to_uuid.keys()):
 | 
			
		||||
				log_error("%s" % (keys))
 | 
			
		||||
			log_error("lvs entries by uuid")
 | 
			
		||||
			for keys in sorted(self.lvs.keys()):
 | 
			
		||||
				log_error("%s" % (keys))
 | 
			
		||||
			raise ke
 | 
			
		||||
 | 
			
		||||
	def pv_pe_segments(self, pv_uuid):
 | 
			
		||||
		pv = self.pvs[pv_uuid]
 | 
			
		||||
		return list(zip(pv['pvseg_start'], pv['pvseg_size']))
 | 
			
		||||
 | 
			
		||||
	def pv_contained_lv(self, pv_device):
 | 
			
		||||
		rc = []
 | 
			
		||||
		if pv_device in self.pv_lvs:
 | 
			
		||||
			rc = self.pv_lvs[pv_device]
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	def lv_contained_pv(self, lv_uuid):
 | 
			
		||||
		rc = []
 | 
			
		||||
		if lv_uuid in self.lv_pvs:
 | 
			
		||||
			rc = self.lv_pvs[lv_uuid]
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	def lvs_in_vg(self, vg_uuid):
 | 
			
		||||
		# Return an array of
 | 
			
		||||
		# (lv_name, (lv_attr, lv_layout, lv_role), lv_uuid)
 | 
			
		||||
		rc = []
 | 
			
		||||
		if vg_uuid in self.lvs_in_vgs:
 | 
			
		||||
			rc = self.lvs_in_vgs[vg_uuid]
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	def pvs_in_vg(self, vg_uuid):
 | 
			
		||||
		# Returns an array of (pv_name, pv_uuid)
 | 
			
		||||
		rc = []
 | 
			
		||||
		if vg_uuid in self.pvs_in_vgs:
 | 
			
		||||
			rc = self.pvs_in_vgs[vg_uuid]
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	def hidden_lvs(self, lv_uuid):
 | 
			
		||||
		# For a specified LV, return a list of hidden lv_uuid, lv_name
 | 
			
		||||
		# for it
 | 
			
		||||
		rc = []
 | 
			
		||||
		if lv_uuid in self.lvs_hidden:
 | 
			
		||||
			rc = self.lvs_hidden[lv_uuid]
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
	pp = prettyprint.PrettyPrinter(indent=4)
 | 
			
		||||
 | 
			
		||||
	use_json = False
 | 
			
		||||
 | 
			
		||||
	if len(sys.argv) != 1:
 | 
			
		||||
		print(len(sys.argv))
 | 
			
		||||
		use_json = True
 | 
			
		||||
 | 
			
		||||
	ds = DataStore(use_json)
 | 
			
		||||
	ds.refresh()
 | 
			
		||||
 | 
			
		||||
	print("PVS")
 | 
			
		||||
	for v in ds.pvs.values():
 | 
			
		||||
		pp.pprint(v)
 | 
			
		||||
		print('PV missing is %s' % ds.pv_missing(v['pv_uuid']))
 | 
			
		||||
 | 
			
		||||
	print("VGS")
 | 
			
		||||
	for v in ds.vgs.values():
 | 
			
		||||
		pp.pprint(v)
 | 
			
		||||
 | 
			
		||||
	print("LVS")
 | 
			
		||||
	for v in ds.lvs.values():
 | 
			
		||||
		pp.pprint(v)
 | 
			
		||||
 | 
			
		||||
	print("LVS in VG")
 | 
			
		||||
	for k, v in ds.lvs_in_vgs.items():
 | 
			
		||||
		print("VG uuid = %s" % (k))
 | 
			
		||||
		pp.pprint(v)
 | 
			
		||||
 | 
			
		||||
	print("pv_in_lvs")
 | 
			
		||||
	for k, v in ds.pv_lvs.items():
 | 
			
		||||
		print("PV %s contains LVS:" % (k))
 | 
			
		||||
		pp.pprint(v)
 | 
			
		||||
 | 
			
		||||
	for k, v in ds.lv_pvs.items():
 | 
			
		||||
		print("LV device = %s" % (k))
 | 
			
		||||
		pp.pprint(v)
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#!@PYTHON3@
 | 
			
		||||
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
from lvmdbusd import main
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	sys.exit(main())
 | 
			
		||||
@@ -1,196 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from . import cfg
 | 
			
		||||
from . import objectmanager
 | 
			
		||||
from . import utils
 | 
			
		||||
from .cfg import BUS_NAME, BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH
 | 
			
		||||
import threading
 | 
			
		||||
from . import cmdhandler
 | 
			
		||||
import time
 | 
			
		||||
import signal
 | 
			
		||||
import dbus
 | 
			
		||||
import dbus.mainloop.glib
 | 
			
		||||
from . import lvmdb
 | 
			
		||||
# noinspection PyUnresolvedReferences
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
from .fetch import StateUpdate
 | 
			
		||||
from .manager import Manager
 | 
			
		||||
import traceback
 | 
			
		||||
import queue
 | 
			
		||||
from . import udevwatch
 | 
			
		||||
from .utils import log_debug, log_error
 | 
			
		||||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
from .cmdhandler import LvmFlightRecorder
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Lvm(objectmanager.ObjectManager):
 | 
			
		||||
	def __init__(self, object_path):
 | 
			
		||||
		super(Lvm, self).__init__(object_path, BASE_INTERFACE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def process_request():
 | 
			
		||||
	while cfg.run.value != 0:
 | 
			
		||||
		# noinspection PyBroadException
 | 
			
		||||
		try:
 | 
			
		||||
			req = cfg.worker_q.get(True, 5)
 | 
			
		||||
			log_debug(
 | 
			
		||||
				"Running method: %s with args %s" %
 | 
			
		||||
				(str(req.method), str(req.arguments)))
 | 
			
		||||
			req.run_cmd()
 | 
			
		||||
			log_debug("Method complete ")
 | 
			
		||||
		except queue.Empty:
 | 
			
		||||
			pass
 | 
			
		||||
		except Exception:
 | 
			
		||||
			st = traceback.format_exc()
 | 
			
		||||
			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
 | 
			
		||||
	parser = argparse.ArgumentParser()
 | 
			
		||||
	parser.add_argument(
 | 
			
		||||
		"--udev", action='store_true',
 | 
			
		||||
		help="Use udev for updating state",
 | 
			
		||||
		default=False,
 | 
			
		||||
		dest='use_udev')
 | 
			
		||||
	parser.add_argument(
 | 
			
		||||
		"--debug", action='store_true',
 | 
			
		||||
		help="Dump debug messages", default=False,
 | 
			
		||||
		dest='debug')
 | 
			
		||||
	parser.add_argument(
 | 
			
		||||
		"--nojson", action='store_false',
 | 
			
		||||
		help="Do not use LVM JSON output (disables lvmshell)", default=True,
 | 
			
		||||
		dest='use_json')
 | 
			
		||||
	parser.add_argument(
 | 
			
		||||
		"--lvmshell", action='store_true',
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
	# Ensure that we get consistent output for parsing stdout/stderr
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	# List of threads that we start up
 | 
			
		||||
	thread_list = []
 | 
			
		||||
 | 
			
		||||
	install_signal_handlers()
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	cfg.om = Lvm(BASE_OBJ_PATH)
 | 
			
		||||
	cfg.om.register_object(Manager(MANAGER_OBJ_PATH))
 | 
			
		||||
 | 
			
		||||
	cfg.db = lvmdb.DataStore(cfg.args.use_json)
 | 
			
		||||
 | 
			
		||||
	# Using a thread to process requests, we cannot hang the dbus library
 | 
			
		||||
	# thread that is handling the dbus interface
 | 
			
		||||
	thread_list.append(threading.Thread(target=process_request,
 | 
			
		||||
										name='process_request'))
 | 
			
		||||
 | 
			
		||||
	# 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
 | 
			
		||||
 | 
			
		||||
	cfg.loop = GLib.MainLoop()
 | 
			
		||||
 | 
			
		||||
	for thread in thread_list:
 | 
			
		||||
		thread.damon = True
 | 
			
		||||
		thread.start()
 | 
			
		||||
 | 
			
		||||
	# Add udev watching
 | 
			
		||||
	if cfg.args.use_udev:
 | 
			
		||||
		log_debug('Utilizing udev to trigger updates')
 | 
			
		||||
 | 
			
		||||
	# In all cases we are going to monitor for udev until we get an
 | 
			
		||||
	# ExternalEvent.  In the case where we get an external event and the user
 | 
			
		||||
	# didn't specify --udev we will stop monitoring udev
 | 
			
		||||
	udevwatch.add()
 | 
			
		||||
 | 
			
		||||
	end = time.time()
 | 
			
		||||
	log_debug(
 | 
			
		||||
		'Service ready! total time= %.4f, lvm time= %.4f count= %d' %
 | 
			
		||||
		(end - start, cmdhandler.total_time, cmdhandler.total_count),
 | 
			
		||||
		'bg_black', 'fg_light_green')
 | 
			
		||||
 | 
			
		||||
	try:
 | 
			
		||||
		if cfg.run.value != 0:
 | 
			
		||||
			cfg.loop.run()
 | 
			
		||||
			udevwatch.remove()
 | 
			
		||||
 | 
			
		||||
			for thread in thread_list:
 | 
			
		||||
				thread.join()
 | 
			
		||||
	except KeyboardInterrupt:
 | 
			
		||||
		# If we are unable to register signal handler, we will end up here when
 | 
			
		||||
		# the service gets a ^C or a kill -2 <parent pid>
 | 
			
		||||
		utils.handler(signal.SIGINT)
 | 
			
		||||
	return 0
 | 
			
		||||
@@ -1,267 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
from .automatedproperties import AutomatedProperties
 | 
			
		||||
 | 
			
		||||
from . import utils
 | 
			
		||||
from .cfg import MANAGER_INTERFACE
 | 
			
		||||
import dbus
 | 
			
		||||
from . import cfg
 | 
			
		||||
from . import cmdhandler
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from . import udevwatch
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class Manager(AutomatedProperties):
 | 
			
		||||
	_Version_meta = ("s", MANAGER_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	def __init__(self, object_path):
 | 
			
		||||
		super(Manager, self).__init__(object_path)
 | 
			
		||||
		self.set_interface(MANAGER_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Version(self):
 | 
			
		||||
		return dbus.String('1.0.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))
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pv_create(device, create_options):
 | 
			
		||||
 | 
			
		||||
		# Check to see if we are already trying to create a PV for an existing
 | 
			
		||||
		# PV
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='sia{sv}',
 | 
			
		||||
		out_signature='(oo)',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def PvCreate(self, device, tmo, create_options, cb, cbe):
 | 
			
		||||
		utils.validate_device_path(MANAGER_INTERFACE, device)
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Manager._pv_create,
 | 
			
		||||
			(device, create_options), cb, cbe)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _create_vg(name, pv_object_paths, create_options):
 | 
			
		||||
		pv_devices = []
 | 
			
		||||
 | 
			
		||||
		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(
 | 
			
		||||
					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)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='saoia{sv}',
 | 
			
		||||
		out_signature='(oo)',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def VgCreate(self, name, pv_object_paths, tmo, create_options, cb, cbe):
 | 
			
		||||
		utils.validate_vg_name(MANAGER_INTERFACE, name)
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Manager._create_vg,
 | 
			
		||||
			(name, pv_object_paths, create_options,),
 | 
			
		||||
			cb, cbe)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _refresh():
 | 
			
		||||
		utils.log_debug('Manager.Refresh - entry')
 | 
			
		||||
 | 
			
		||||
		# This is a diagnostic and should not be run in normal operation, so
 | 
			
		||||
		# lets remove the log entries for refresh as it's implied.
 | 
			
		||||
 | 
			
		||||
		# Run an internal diagnostic on the object manager look up tables
 | 
			
		||||
		lc = cfg.om.validate_lookups()
 | 
			
		||||
 | 
			
		||||
		rc = cfg.load(log=False)
 | 
			
		||||
 | 
			
		||||
		if rc != 0:
 | 
			
		||||
			utils.log_debug('Manager.Refresh - exit %d' % (rc),
 | 
			
		||||
							'bg_black', 'fg_light_red')
 | 
			
		||||
		else:
 | 
			
		||||
			utils.log_debug('Manager.Refresh - exit %d' % (rc))
 | 
			
		||||
		return rc + lc
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		out_signature='t',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Refresh(self, cb, cbe):
 | 
			
		||||
		"""
 | 
			
		||||
		Take all the objects we know about and go out and grab the latest
 | 
			
		||||
		more of a test method at the moment to make sure we are handling object
 | 
			
		||||
		paths correctly.
 | 
			
		||||
 | 
			
		||||
		:param cb   Callback for result
 | 
			
		||||
		:param cbe  Callback for errors
 | 
			
		||||
 | 
			
		||||
		Returns the number of changes, object add/remove/properties changed
 | 
			
		||||
		"""
 | 
			
		||||
		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):
 | 
			
		||||
		"""
 | 
			
		||||
		Given a lvm id in one of the forms:
 | 
			
		||||
 | 
			
		||||
		/dev/sda
 | 
			
		||||
		some_vg
 | 
			
		||||
		some_vg/some_lv
 | 
			
		||||
		Oe1rPX-Pf0W-15E5-n41N-ZmtF-jXS0-Osg8fn
 | 
			
		||||
 | 
			
		||||
		return the object path in O(1) time.
 | 
			
		||||
 | 
			
		||||
		:param key: The lookup value
 | 
			
		||||
		: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))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='b', out_signature='b',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def UseLvmShell(self, yes_no, cb, cbe):
 | 
			
		||||
		"""
 | 
			
		||||
		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
 | 
			
		||||
		"""
 | 
			
		||||
		r = RequestEntry(-1, Manager._use_lvm_shell, (yes_no,), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _external_event(command):
 | 
			
		||||
		utils.log_debug("Processing _external_event= %s" % command,
 | 
			
		||||
							'bg_black', 'fg_orange')
 | 
			
		||||
		cfg.load()
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='s', out_signature='i')
 | 
			
		||||
	def ExternalEvent(self, command):
 | 
			
		||||
		utils.log_debug("ExternalEvent %s" % command)
 | 
			
		||||
		# If a user didn't explicitly specify udev, we will turn it off now.
 | 
			
		||||
		if not cfg.args.use_udev:
 | 
			
		||||
			if udevwatch.remove():
 | 
			
		||||
				utils.log_debug("ExternalEvent received, disabling "
 | 
			
		||||
								"udev monitoring")
 | 
			
		||||
				# We are dependent on external events now to stay current!
 | 
			
		||||
				cfg.got_external_event = True
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			-1, Manager._external_event, (command,), None, None, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
		return dbus.Int32(0)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _pv_scan(activate, cache, device_path, major_minor, scan_options):
 | 
			
		||||
 | 
			
		||||
		rc, out, err = cmdhandler.pv_scan(
 | 
			
		||||
			activate, cache, device_path,
 | 
			
		||||
			major_minor, scan_options)
 | 
			
		||||
 | 
			
		||||
		Manager.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=MANAGER_INTERFACE,
 | 
			
		||||
		in_signature='bbasa(ii)ia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def PvScan(self, activate, cache, device_paths, major_minors,
 | 
			
		||||
			tmo, scan_options, cb, cbe):
 | 
			
		||||
		"""
 | 
			
		||||
		Scan all supported LVM block devices in the system for physical volumes
 | 
			
		||||
		NOTE: major_minors & device_paths only usable when cache == True
 | 
			
		||||
		:param activate: If True, activate any newly found LVs
 | 
			
		||||
		:param cache:    If True, update lvmetad
 | 
			
		||||
		:param device_paths: Array of device paths or empty
 | 
			
		||||
		:param major_minors: Array of structures (major,minor)
 | 
			
		||||
		:param tmo: Timeout for operation
 | 
			
		||||
		:param scan_options:  Additional options to pvscan
 | 
			
		||||
		:param cb: Not visible in API (used for async. callback)
 | 
			
		||||
		:param cbe: Not visible in API (used for async. error callback)
 | 
			
		||||
		:return: '/' if operation done, else job path
 | 
			
		||||
		"""
 | 
			
		||||
		for d in device_paths:
 | 
			
		||||
			utils.validate_device_path(MANAGER_INTERFACE, d)
 | 
			
		||||
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Manager._pv_scan,
 | 
			
		||||
			(activate, cache, device_paths, major_minors,
 | 
			
		||||
			scan_options), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def lvm_id(self):
 | 
			
		||||
		"""
 | 
			
		||||
		Intended to be overridden by classes that inherit
 | 
			
		||||
		"""
 | 
			
		||||
		return str(id(self))
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Uuid(self):
 | 
			
		||||
		"""
 | 
			
		||||
		Intended to be overridden by classes that inherit
 | 
			
		||||
		"""
 | 
			
		||||
		import uuid
 | 
			
		||||
		return uuid.uuid1()
 | 
			
		||||
@@ -1,385 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import threading
 | 
			
		||||
import traceback
 | 
			
		||||
import dbus
 | 
			
		||||
import os
 | 
			
		||||
import copy
 | 
			
		||||
from . import cfg
 | 
			
		||||
from .utils import log_debug, pv_obj_path_generate, log_error
 | 
			
		||||
from .automatedproperties import AutomatedProperties
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
class ObjectManager(AutomatedProperties):
 | 
			
		||||
	"""
 | 
			
		||||
	Implements the org.freedesktop.DBus.ObjectManager interface
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	def __init__(self, object_path, interface):
 | 
			
		||||
		super(ObjectManager, self).__init__(object_path, interface)
 | 
			
		||||
		self.set_interface(interface)
 | 
			
		||||
		self._ap_o_path = object_path
 | 
			
		||||
		self._objects = {}
 | 
			
		||||
		self._id_to_object_path = {}
 | 
			
		||||
		self.rlock = threading.RLock()
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def _get_managed_objects(obj):
 | 
			
		||||
		with obj.rlock:
 | 
			
		||||
			rc = {}
 | 
			
		||||
			try:
 | 
			
		||||
				for k, v in list(obj._objects.items()):
 | 
			
		||||
					path, props = v[0].emit_data()
 | 
			
		||||
					rc[path] = props
 | 
			
		||||
			except Exception:
 | 
			
		||||
				traceback.print_exc(file=sys.stdout)
 | 
			
		||||
				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
 | 
			
		||||
		calls into ObjectManager while blocking others they can use this method
 | 
			
		||||
		to lock others out.
 | 
			
		||||
		:return:
 | 
			
		||||
		"""
 | 
			
		||||
		return ObjectManagerLock(self.rlock)
 | 
			
		||||
 | 
			
		||||
	@dbus.service.signal(
 | 
			
		||||
		dbus_interface="org.freedesktop.DBus.ObjectManager",
 | 
			
		||||
		signature='oa{sa{sv}}')
 | 
			
		||||
	def InterfacesAdded(self, object_path, int_name_prop_dict):
 | 
			
		||||
		log_debug(
 | 
			
		||||
			('SIGNAL: InterfacesAdded(%s, %s)' %
 | 
			
		||||
			(str(object_path), str(int_name_prop_dict))))
 | 
			
		||||
 | 
			
		||||
	@dbus.service.signal(
 | 
			
		||||
		dbus_interface="org.freedesktop.DBus.ObjectManager",
 | 
			
		||||
		signature='oas')
 | 
			
		||||
	def InterfacesRemoved(self, object_path, interface_list):
 | 
			
		||||
		log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' %
 | 
			
		||||
			(str(object_path), str(interface_list))))
 | 
			
		||||
 | 
			
		||||
	def validate_lookups(self):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			tmp_lookups = copy.deepcopy(self._id_to_object_path)
 | 
			
		||||
 | 
			
		||||
			# iterate over all we know, removing from the copy.  If all is well
 | 
			
		||||
			# we will have zero items left over
 | 
			
		||||
			for path, md in self._objects.items():
 | 
			
		||||
				obj, lvm_id, uuid = md
 | 
			
		||||
 | 
			
		||||
				if lvm_id:
 | 
			
		||||
					assert path == tmp_lookups[lvm_id]
 | 
			
		||||
					del tmp_lookups[lvm_id]
 | 
			
		||||
 | 
			
		||||
				if uuid:
 | 
			
		||||
					assert path == tmp_lookups[uuid]
 | 
			
		||||
					del tmp_lookups[uuid]
 | 
			
		||||
 | 
			
		||||
			rc = len(tmp_lookups)
 | 
			
		||||
			if rc:
 | 
			
		||||
				# Error condition
 | 
			
		||||
				log_error("_id_to_object_path has extraneous lookups!")
 | 
			
		||||
				for key, path in tmp_lookups.items():
 | 
			
		||||
					log_error("Key= %s, path= %s" % (key, path))
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	def _lookup_add(self, obj, path, lvm_id, uuid):
 | 
			
		||||
		"""
 | 
			
		||||
		Store information about what we added to the caches so that we
 | 
			
		||||
		can remove it cleanly
 | 
			
		||||
		:param obj:     The dbus object we are storing
 | 
			
		||||
		:param lvm_id:  The lvm id for the asset
 | 
			
		||||
		:param uuid:    The uuid for the asset
 | 
			
		||||
		:return:
 | 
			
		||||
		"""
 | 
			
		||||
		# Note: Only called internally, lock implied
 | 
			
		||||
 | 
			
		||||
		# We could have a temp entry from the forward creation of a path
 | 
			
		||||
		self._lookup_remove(path)
 | 
			
		||||
 | 
			
		||||
		self._objects[path] = (obj, lvm_id, uuid)
 | 
			
		||||
 | 
			
		||||
		# Make sure we have one or the other
 | 
			
		||||
		assert lvm_id or uuid
 | 
			
		||||
 | 
			
		||||
		if lvm_id:
 | 
			
		||||
			self._id_to_object_path[lvm_id] = path
 | 
			
		||||
 | 
			
		||||
		if uuid:
 | 
			
		||||
			self._id_to_object_path[uuid] = path
 | 
			
		||||
 | 
			
		||||
	def _lookup_remove(self, obj_path):
 | 
			
		||||
		# Note: Only called internally, lock implied
 | 
			
		||||
		if obj_path in self._objects:
 | 
			
		||||
			(obj, lvm_id, uuid) = self._objects[obj_path]
 | 
			
		||||
 | 
			
		||||
			if lvm_id in self._id_to_object_path:
 | 
			
		||||
				del self._id_to_object_path[lvm_id]
 | 
			
		||||
 | 
			
		||||
			if uuid in self._id_to_object_path:
 | 
			
		||||
				del self._id_to_object_path[uuid]
 | 
			
		||||
 | 
			
		||||
			del self._objects[obj_path]
 | 
			
		||||
 | 
			
		||||
	def lookup_update(self, dbus_obj, new_uuid, new_lvm_id):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			obj_path = dbus_obj.dbus_object_path()
 | 
			
		||||
			self._lookup_remove(obj_path)
 | 
			
		||||
			self._lookup_add(
 | 
			
		||||
				dbus_obj, obj_path,
 | 
			
		||||
				new_lvm_id, new_uuid)
 | 
			
		||||
 | 
			
		||||
	def object_paths_by_type(self, o_type):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			rc = {}
 | 
			
		||||
 | 
			
		||||
			for k, v in list(self._objects.items()):
 | 
			
		||||
				if isinstance(v[0], o_type):
 | 
			
		||||
					rc[k] = True
 | 
			
		||||
			return rc
 | 
			
		||||
 | 
			
		||||
	def register_object(self, dbus_object, emit_signal=False):
 | 
			
		||||
		"""
 | 
			
		||||
		Given a dbus object add it to the collection
 | 
			
		||||
		:param dbus_object: Dbus object to register
 | 
			
		||||
		:param emit_signal: If true emit a signal for interfaces added
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			path, props = dbus_object.emit_data()
 | 
			
		||||
 | 
			
		||||
			# print('Registering object path %s for %s' %
 | 
			
		||||
			# (path, dbus_object.lvm_id))
 | 
			
		||||
 | 
			
		||||
			# We want fast access to the object by a number of different ways
 | 
			
		||||
			# so we use multiple hashs with different keys
 | 
			
		||||
			self._lookup_add(dbus_object, path, dbus_object.lvm_id,
 | 
			
		||||
				dbus_object.Uuid)
 | 
			
		||||
 | 
			
		||||
			if emit_signal:
 | 
			
		||||
				self.InterfacesAdded(path, props)
 | 
			
		||||
 | 
			
		||||
	def remove_object(self, dbus_object, emit_signal=False):
 | 
			
		||||
		"""
 | 
			
		||||
		Given a dbus object, remove it from the collection and remove it
 | 
			
		||||
		from the dbus framework as well
 | 
			
		||||
		:param dbus_object:  Dbus object to remove
 | 
			
		||||
		:param emit_signal:  If true emit the interfaces removed signal
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			# Store off the object path and the interface first
 | 
			
		||||
			path = dbus_object.dbus_object_path()
 | 
			
		||||
			interfaces = dbus_object.interface()
 | 
			
		||||
 | 
			
		||||
			# print 'UN-Registering object path %s for %s' % \
 | 
			
		||||
			#      (path, dbus_object.lvm_id)
 | 
			
		||||
 | 
			
		||||
			self._lookup_remove(path)
 | 
			
		||||
 | 
			
		||||
			# Remove from dbus library
 | 
			
		||||
			dbus_object.remove_from_connection(cfg.bus, path)
 | 
			
		||||
 | 
			
		||||
			# Optionally emit a signal
 | 
			
		||||
			if emit_signal:
 | 
			
		||||
				self.InterfacesRemoved(path, interfaces)
 | 
			
		||||
 | 
			
		||||
	def get_object_by_path(self, path):
 | 
			
		||||
		"""
 | 
			
		||||
		Given a dbus path return the object registered for it
 | 
			
		||||
		:param path: The dbus path
 | 
			
		||||
		:return: The object
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			if path in self._objects:
 | 
			
		||||
				return self._objects[path][0]
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
	def get_object_by_uuid_lvm_id(self, uuid, lvm_id):
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			return self.get_object_by_path(
 | 
			
		||||
				self.get_object_path_by_uuid_lvm_id(uuid, lvm_id))
 | 
			
		||||
 | 
			
		||||
	def get_object_by_lvm_id(self, lvm_id):
 | 
			
		||||
		"""
 | 
			
		||||
		Given an lvm identifier, return the object registered for it
 | 
			
		||||
		: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)
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
	def get_object_path_by_lvm_id(self, lvm_id):
 | 
			
		||||
		"""
 | 
			
		||||
		Given an lvm identifier, return the object path for it
 | 
			
		||||
		:param lvm_id: The lvm identifier
 | 
			
		||||
		:return: Object path or '/' if not found
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			lookup_rc = self._id_lookup(lvm_id)
 | 
			
		||||
			if lookup_rc:
 | 
			
		||||
				return lookup_rc
 | 
			
		||||
			return '/'
 | 
			
		||||
 | 
			
		||||
	def _uuid_verify(self, path, uuid, lvm_id):
 | 
			
		||||
		"""
 | 
			
		||||
		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: 		lvm uuid to verify
 | 
			
		||||
		:param lvm_id:		lvm_id used to find object
 | 
			
		||||
		:return: None
 | 
			
		||||
		"""
 | 
			
		||||
		# 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:
 | 
			
		||||
			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
 | 
			
		||||
 | 
			
		||||
		if the_id:
 | 
			
		||||
			# The _id_to_object_path contains hash keys for everything, so
 | 
			
		||||
			# uuid and lvm_id
 | 
			
		||||
			if the_id in self._id_to_object_path:
 | 
			
		||||
				path = self._id_to_object_path[the_id]
 | 
			
		||||
			else:
 | 
			
		||||
				if "/" in the_id:
 | 
			
		||||
					if the_id.startswith('/'):
 | 
			
		||||
						# We could have a pv device path lookup that failed,
 | 
			
		||||
						# lets try canonical form and try again.
 | 
			
		||||
						canonical = os.path.realpath(the_id)
 | 
			
		||||
						if canonical in self._id_to_object_path:
 | 
			
		||||
							path = self._id_to_object_path[canonical]
 | 
			
		||||
					else:
 | 
			
		||||
						vg, lv = the_id.split("/", 1)
 | 
			
		||||
						int_lvm_id = vg + "/" + ("[%s]" % lv)
 | 
			
		||||
						if int_lvm_id in self._id_to_object_path:
 | 
			
		||||
							path = self._id_to_object_path[int_lvm_id]
 | 
			
		||||
		return path
 | 
			
		||||
 | 
			
		||||
	def get_object_path_by_uuid_lvm_id(self, uuid, lvm_id, path_create=None):
 | 
			
		||||
		"""
 | 
			
		||||
		For a given lvm asset return the dbus object path registered for it.
 | 
			
		||||
		This method first looks up by uuid and then by lvm_id.  You
 | 
			
		||||
		can search by just one by setting uuid == lvm_id (uuid or lvm_id).
 | 
			
		||||
		If the object is not found and path_create is a not None, the
 | 
			
		||||
		path_create function will be called to create a new object path and
 | 
			
		||||
		register it with the object manager for the specified uuid & lvm_id.
 | 
			
		||||
		Note: If path create is not None, uuid and lvm_id cannot be equal
 | 
			
		||||
		:param uuid: The uuid for the lvm object we are searching for
 | 
			
		||||
		:param lvm_id: The lvm name (eg. pv device path, vg name, lv full name)
 | 
			
		||||
		:param path_create: If not None, create the path using this function if
 | 
			
		||||
				we fail to find the object by uuid or lvm_id.
 | 
			
		||||
		:returns None if lvm asset not found and path_create == None otherwise
 | 
			
		||||
				a valid dbus object path
 | 
			
		||||
		"""
 | 
			
		||||
		with self.rlock:
 | 
			
		||||
			assert lvm_id
 | 
			
		||||
			assert uuid
 | 
			
		||||
 | 
			
		||||
			if path_create:
 | 
			
		||||
				assert uuid != lvm_id
 | 
			
		||||
 | 
			
		||||
			# Check for Manager.LookUpByLvmId query, we cannot
 | 
			
		||||
			# check/verify/update the uuid and lvm_id lookups so don't!
 | 
			
		||||
			if uuid == lvm_id:
 | 
			
		||||
				path = self._id_lookup(lvm_id)
 | 
			
		||||
			else:
 | 
			
		||||
				# We have a uuid and a lvm_id we can do sanity checks to ensure
 | 
			
		||||
				# that they are consistent
 | 
			
		||||
 | 
			
		||||
				# If a PV is missing it's device path is '[unknown]' or some
 | 
			
		||||
				# other text derivation of unknown.  When we find that a PV is
 | 
			
		||||
				# missing we will clear out the lvm_id as it's likely not unique
 | 
			
		||||
				# and thus not useful and potentially harmful for lookups.
 | 
			
		||||
				if path_create == pv_obj_path_generate and \
 | 
			
		||||
						cfg.db.pv_missing(uuid):
 | 
			
		||||
					lvm_id = None
 | 
			
		||||
 | 
			
		||||
				# Lets check for the uuid first
 | 
			
		||||
				path = self._id_lookup(uuid)
 | 
			
		||||
				if path:
 | 
			
		||||
					# 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:
 | 
			
		||||
						# 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, %s: return %s' %
 | 
			
		||||
			# 	   (uuid, lvm_id, str(path_create), str(gen_new), path))
 | 
			
		||||
 | 
			
		||||
			return path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectManagerLock(object):
 | 
			
		||||
	"""
 | 
			
		||||
	The sole purpose of this class is to allow other code the ability to
 | 
			
		||||
	lock the object manager using a `with` statement, eg.
 | 
			
		||||
 | 
			
		||||
	with cfg.om.locked():
 | 
			
		||||
		# Do stuff with object manager
 | 
			
		||||
 | 
			
		||||
	This will ensure that the lock is always released (assuming this is done
 | 
			
		||||
	correctly)
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	def __init__(self, recursive_lock):
 | 
			
		||||
		self._lock = recursive_lock
 | 
			
		||||
 | 
			
		||||
	def __enter__(self):
 | 
			
		||||
		# Acquire lock
 | 
			
		||||
		self._lock.acquire()
 | 
			
		||||
 | 
			
		||||
	# noinspection PyUnusedLocal
 | 
			
		||||
	def __exit__(self, e_type, e_value, e_traceback):
 | 
			
		||||
		# Release lock
 | 
			
		||||
		self._lock.release()
 | 
			
		||||
		self._lock = None
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
LVM_BINARY = "@LVM_PATH@"
 | 
			
		||||
@@ -1,260 +0,0 @@
 | 
			
		||||
# 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/>.
 | 
			
		||||
 | 
			
		||||
from .automatedproperties import AutomatedProperties
 | 
			
		||||
from . import utils
 | 
			
		||||
from . import cfg
 | 
			
		||||
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
 | 
			
		||||
from .loader import common
 | 
			
		||||
from .request import RequestEntry
 | 
			
		||||
from .state import State
 | 
			
		||||
from .utils import round_size
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyUnusedLocal
 | 
			
		||||
def pvs_state_retrieve(selection, cache_refresh=True):
 | 
			
		||||
	rc = []
 | 
			
		||||
 | 
			
		||||
	if cache_refresh:
 | 
			
		||||
		cfg.db.refresh()
 | 
			
		||||
 | 
			
		||||
	for p in cfg.db.fetch_pvs(selection):
 | 
			
		||||
		rc.append(
 | 
			
		||||
			PvState(
 | 
			
		||||
				p["pv_name"], p["pv_uuid"], p["pv_name"],
 | 
			
		||||
				p["pv_fmt"], n(p["pv_size"]), n(p["pv_free"]),
 | 
			
		||||
				n(p["pv_used"]), n(p["dev_size"]), n(p["pv_mda_size"]),
 | 
			
		||||
				n(p["pv_mda_free"]), int(p["pv_ba_start"]),
 | 
			
		||||
				n(p["pv_ba_size"]), n(p["pe_start"]),
 | 
			
		||||
				int(p["pv_pe_count"]), int(p["pv_pe_alloc_count"]),
 | 
			
		||||
				p["pv_attr"], p["pv_tags"], p["vg_name"], p["vg_uuid"]))
 | 
			
		||||
	return rc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_pvs(device=None, object_path=None, refresh=False, emit_signal=False,
 | 
			
		||||
		cache_refresh=True):
 | 
			
		||||
	return common(
 | 
			
		||||
		pvs_state_retrieve, (Pv,), device, object_path, refresh,
 | 
			
		||||
		emit_signal, cache_refresh)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyUnresolvedReferences
 | 
			
		||||
class PvState(State):
 | 
			
		||||
	@property
 | 
			
		||||
	def lvm_id(self):
 | 
			
		||||
		return self.lvm_path
 | 
			
		||||
 | 
			
		||||
	def _lv_object_list(self, vg_name):
 | 
			
		||||
		rc = []
 | 
			
		||||
		if vg_name:
 | 
			
		||||
			for lv in sorted(cfg.db.pv_contained_lv(self.lvm_id)):
 | 
			
		||||
				lv_uuid, lv_name, meta, segs = lv
 | 
			
		||||
				full_name = "%s/%s" % (vg_name, lv_name)
 | 
			
		||||
 | 
			
		||||
				path_create = lv_object_path_method(lv_name, meta)
 | 
			
		||||
				lv_path = cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
					lv_uuid, full_name, path_create)
 | 
			
		||||
 | 
			
		||||
				rc.append((lv_path, segs))
 | 
			
		||||
		return rc
 | 
			
		||||
 | 
			
		||||
	# noinspection PyUnusedLocal,PyPep8Naming
 | 
			
		||||
	def __init__(self, lvm_path, Uuid, Name,
 | 
			
		||||
			Fmt, SizeBytes, FreeBytes, UsedBytes, DevSizeBytes,
 | 
			
		||||
			MdaSizeBytes, MdaFreeBytes, BaStart, BaSizeBytes,
 | 
			
		||||
			PeStart, PeCount, PeAllocCount, attr, Tags, vg_name,
 | 
			
		||||
			vg_uuid):
 | 
			
		||||
		utils.init_class_from_arguments(self)
 | 
			
		||||
		self.pe_segments = cfg.db.pv_pe_segments(Uuid)
 | 
			
		||||
 | 
			
		||||
		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:
 | 
			
		||||
			self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id(
 | 
			
		||||
				vg_uuid, vg_name, vg_obj_path_generate)
 | 
			
		||||
		else:
 | 
			
		||||
			self.vg_path = '/'
 | 
			
		||||
 | 
			
		||||
	def identifiers(self):
 | 
			
		||||
		return (self.Uuid, self.lvm_path)
 | 
			
		||||
 | 
			
		||||
	def create_dbus_object(self, path):
 | 
			
		||||
		if not path:
 | 
			
		||||
			path = cfg.om.get_object_path_by_uuid_lvm_id(self.Uuid, self.Name,
 | 
			
		||||
														pv_obj_path_generate)
 | 
			
		||||
		return Pv(path, self)
 | 
			
		||||
 | 
			
		||||
	# noinspection PyMethodMayBeStatic
 | 
			
		||||
	def creation_signature(self):
 | 
			
		||||
		return (Pv, pv_obj_path_generate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyPep8Naming
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'Uuid', 's')  # PV UUID/pv_uuid
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'Name', 's')  # PV/pv_name
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'Fmt', 's')  # Fmt/pv_fmt
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'SizeBytes', 't')  # PSize/pv_size
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'FreeBytes', 't')  # PFree/pv_free
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'UsedBytes', 't')  # Used/pv_used
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'DevSizeBytes', 't')  # DevSize/dev_size
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'MdaSizeBytes', 't')  # PMdaSize/pv_mda_size
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'MdaFreeBytes', 't')  # PMdaFree/pv_mda_free
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'BaStart', 't')  # BA start/pv_ba_start
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'BaSizeBytes', 't')  # BA size/pv_ba_size
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'PeStart', 't')  # 1st PE/pe_start
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'PeCount', 't')  # PE/pv_pe_count
 | 
			
		||||
@utils.dbus_property(PV_INTERFACE, 'PeAllocCount', 't')  # PE Allocation count
 | 
			
		||||
class Pv(AutomatedProperties):
 | 
			
		||||
	# For properties that we need custom handlers we need these, otherwise
 | 
			
		||||
	# we won't get our introspection data
 | 
			
		||||
	_Tags_meta = ("as", PV_INTERFACE)
 | 
			
		||||
	_PeSegments_meta = ("a(tt)", PV_INTERFACE)
 | 
			
		||||
	_Exportable_meta = ("b", PV_INTERFACE)
 | 
			
		||||
	_Allocatable_meta = ("b", PV_INTERFACE)
 | 
			
		||||
	_Missing_meta = ("b", PV_INTERFACE)
 | 
			
		||||
	_Lv_meta = ("a(oa(tts))", PV_INTERFACE)
 | 
			
		||||
	_Vg_meta = ("o", PV_INTERFACE)
 | 
			
		||||
 | 
			
		||||
	# noinspection PyUnusedLocal,PyPep8Naming
 | 
			
		||||
	def __init__(self, object_path, state_obj):
 | 
			
		||||
		super(Pv, self).__init__(object_path, pvs_state_retrieve)
 | 
			
		||||
		self.set_interface(PV_INTERFACE)
 | 
			
		||||
		self.state = state_obj
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	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)
 | 
			
		||||
		rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
 | 
			
		||||
		Pv.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@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(
 | 
			
		||||
				PV_INTERFACE,
 | 
			
		||||
				'Exit code %s, stderr = %s' % (str(rc), err))
 | 
			
		||||
 | 
			
		||||
	@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:
 | 
			
		||||
			raise dbus.exceptions.DBusException(
 | 
			
		||||
				PV_INTERFACE,
 | 
			
		||||
				'PV with uuid %s and name %s not present!' %
 | 
			
		||||
				(pv_uuid, pv_name))
 | 
			
		||||
		return dbo
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=PV_INTERFACE,
 | 
			
		||||
		in_signature='ia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def Remove(self, tmo, remove_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Pv._remove,
 | 
			
		||||
			(self.Uuid, self.lvm_id, remove_options),
 | 
			
		||||
			cb, cbe, return_tuple=False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@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)
 | 
			
		||||
 | 
			
		||||
		rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
 | 
			
		||||
												resize_options)
 | 
			
		||||
		Pv.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=PV_INTERFACE,
 | 
			
		||||
		in_signature='tia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def ReSize(self, new_size_bytes, tmo, resize_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Pv._resize,
 | 
			
		||||
			(self.Uuid, self.lvm_id, round_size(new_size_bytes),
 | 
			
		||||
			resize_options), cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@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)
 | 
			
		||||
		rc, out, err = cmdhandler.pv_allocatable(
 | 
			
		||||
			pv_name, yes_no, allocation_options)
 | 
			
		||||
		Pv.handle_execute(rc, out, err)
 | 
			
		||||
		return '/'
 | 
			
		||||
 | 
			
		||||
	@dbus.service.method(
 | 
			
		||||
		dbus_interface=PV_INTERFACE,
 | 
			
		||||
		in_signature='bia{sv}',
 | 
			
		||||
		out_signature='o',
 | 
			
		||||
		async_callbacks=('cb', 'cbe'))
 | 
			
		||||
	def AllocationEnabled(self, yes, tmo, allocation_options, cb, cbe):
 | 
			
		||||
		r = RequestEntry(
 | 
			
		||||
			tmo, Pv._allocation_enabled,
 | 
			
		||||
			(self.Uuid, self.lvm_id,
 | 
			
		||||
			yes, allocation_options),
 | 
			
		||||
			cb, cbe, False)
 | 
			
		||||
		cfg.worker_q.put(r)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Tags(self):
 | 
			
		||||
		return utils.parse_tags(self.state.Tags)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def PeSegments(self):
 | 
			
		||||
		if len(self.state.pe_segments):
 | 
			
		||||
			return dbus.Array(self.state.pe_segments, signature='(tt)')
 | 
			
		||||
		return dbus.Array([], '(tt)')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Exportable(self):
 | 
			
		||||
		return dbus.Boolean(self.state.attr[1] == 'x')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Allocatable(self):
 | 
			
		||||
		return dbus.Boolean(self.state.attr[0] == 'a')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Missing(self):
 | 
			
		||||
		return dbus.Boolean(self.state.attr[2] == 'm')
 | 
			
		||||
 | 
			
		||||
	def object_path(self):
 | 
			
		||||
		return self._object_path
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def lvm_id(self):
 | 
			
		||||
		return self.state.lvm_id
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def identifiers(self):
 | 
			
		||||
		return self.state.identifiers()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Lv(self):
 | 
			
		||||
		return dbus.Array(self.state.lv, signature="(oa(tts))")
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def Vg(self):
 | 
			
		||||
		return dbus.ObjectPath(self.state.vg_path)
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user